感じていた疑問

いくらかネットや書籍を見てみたが、いまいちわからなかったこととして、

  • たとえばinputText.delegate = selfとしてDelegate先をしているとあるが、selfって結局どれ?inputText.delegateなのかinputTextなのかViewControllerクラスかでもクラス名を右辺にもってくるってどういう了見?
  • 巷のシンプルなDelegate例だと、Protocolにfuncは定義されているが中身がなさすぎて「中身がないメソッドを定義する意味がわからん、メソッド名を固定しているくらいやん」となった

実際のところ、自分が腹落ちするように曲解している可能性もあるが、少なくとも上記疑問点は解消した。

サンプルを使った自分なりの解釈

シンプルでわかりやすいので、「TextFieldに文字列を入力してReturn(Enter)を押したらキーボードを閉じるプログラム」をUIKitで実装した場合を考える。一応念の為助言しておくと、以下はSwiftUIで実装していないので、もしSwiftUIしか勉強していなければ回れ右したほうが良い。

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        inputText.delegate = self
    }

    @IBOutlet weak var inputText: UITextField!

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
}

ひとまず最初はDelegateの意味を「アプリで操作された内容とタイミングを通知してくれて、私たちが行いたい処理を実装できる仕組み」(「たった2日でマスターできるiPhoneアプリ開発集中講座 Xcode 11 Swift 5対応」、ソシム、2019)だとする。その上で、なんだか設計に有利な考え方が使われているらしいと思っておく。

Delegateを考える際は、以下の登場人物を念頭に置く。

  1. Delegate元(処理を依頼する側 ;依頼人)
  2. Delegate先(処理を代理する側;代理人)
  3. Delegate Protocol(上の2人が処理を依頼・代理する際の約束事;これを守っている(conform: 批准する、準拠する、従う)者のみ代理人になれる)

上記実装例の場合、

  1. Delegate元→UITextFieldクラス
  2. Delegate先→ViewControllerクラス
  3. Delegate Protocol→UITextFieldDelegate

代理人をどうやって定義しているかというと、inputText.delegate = selfである。1つずつ見ていくと、

  • inputTextはUITextField(つまり依頼人)型(注1)の変数であり、UITextFieldの定義を参照するとvar delegate: UITextFieldDelegate!であることから.delegate = でDelegate先が指定できる
  • selfの定義を参照するとlet `self`: ViewControllerである(注2)ことから、ここでいうself=自身とは「ViewController(型(注1)の変数)」
  • したがって、ViewControllerクラス内のメソッドとして処理の中身を実装する(これをDelegate Methodという)

textFieldShouldReturn()がDelegate Methodである。ここで「中身を実装する」と強調したのは、今回の場合textFieldShouldReturn()が「Returnキーを押した後に入力情報を引数として与えられて〇〇が実行される」ことは事前にProtocolで約束されており、実装することは〇〇に限定されるからである。つまり〇〇はDelegate先の実装内容に依存する。

逆に言えば、〇〇以外の約束事はDelegate先を一切気にせず設計できる。この考え方がつまりデザインパターンであり、簡単に言えば使い回せる(repeatable)設計になっているということである。なお、今回はすでに実装された設計を利用していることに注意したい。また、利用する側であるこちらも約束事がどのように実装されているかは気にせずともよく、実際どのようになっているかは知らないまま上記プログラムを実装できている。

注釈

(注1)var inputText: UITextFieldと実装されている場合、UITextFieldクラス「型」の変数っていうのか?: StringだったらString型とか文字列型と言えるだろうが、一般的な言い回しがわからない

(注2)ここでselfをbacktick(`)で挟んでいるのは、その単語が予約語だからである(classという名前の変数を定義したい場合は同様にvar `class`: Intのように記述する)

参考資料