初めてiOSアプリを開発するプログラム未経験文系新人さんのための「delegate」の使い方

More than 1 year has passed since last update.

WithOne AdventCalendar 1日目の記事です。
みなさん参加してくださってありがとうございました。

この度新人さん二人が私の元でiOS開発をすることになったので、
自分がiOSを開発始めた時に躓いたdelegateについて説明しようと思います。

説明が解りにくい、等あったらどんどんご指摘いただきたいです。。

delegateとは

delegateは英語で「代理人」という意味です。
Swiftでは、delegateは「代わりに処理を行ってくれるもの」になります。

よく見かけるdelegate

iOS開発でよく見かけるのは以下のようなUITableViewのdelegateではないかと思います。

TodoViewController.swift

class TodoViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    var tableView: UITableView?

    override func viewDidLoad() {
         super.viewDidLoad()
         self.tableView = UITableView()
         self.tableView.delegate = self // ←ココ!
         self.tableView.dataSource = self
         ・・・
    }
}

これは「TodoViewControllerが、tableViewの代わりに処理を行う」という書き方です。
それはどういうことなのでしょうか・・・?

delegateはプロトコル

プロトコルはJavaでいうインターフェースのようなもので
プロパティやメソッドを定義することができます。

デリゲートは以下のようにprotocolを使用して定義されています。
今回は「家事をする」 プロトコルを作成します。

HouseworkDelegate.swift
@objc
protocol HouseworkDelegate {
    func cooking() -> Void
    func cleaning() -> Void
    func washing() -> Void
}

料理、掃除、洗濯をする家事プロトコルを作成しました。
このプロトコルに準拠したクラスは家事をすることができるのです。

delegateに準拠したクラスを作る

家事を代わりに行ってくれるお手伝いさんを作ってみましょう。
家事のプロトコルに準拠したマリコさんを作成しました。

Mariko.swift
class Mariko: HouseworkDelegate {
    func cooking() {
        print("中華を作ります")
    }
    func cleaning() {
        print("床拭きをします")
    }
    func washing() {
        print("柔軟剤を使って洗濯物をします")
    }
}

お手伝いさんになるためには先ほど作ったHouseworkDelegateに準拠している必要がありますので、まずクラスの後ろにコロンでHouseworkDelegateをつけます。
そして、HouseworkDelegateに定義されていたメソッドをマリコさんに実装します。

これがサチコさんになると、少しできることも変わってきます。
与えられたお仕事に対してどのように対応するは人によって違います。

Sachiko.swift
class Sachiko: HouseworkDelegate {
    func cooking() {
        print("和食を作ります")
    }
    func cleaning() {
        print("窓拭きをします")
    }
    func washing() {
        print("手で洗濯物を洗います")
    }
}

delegateを使う

delegateを使ってみましょう。
今回は田中さん一家の家事を代わりに行ってくれる人を作成することにします。

TanakaFamily.swift
class TanakaFamily {

    // 家事をする人
    var houseworkDelegate: HouseworkDelegate?
    func lifeCycle() {

        if houseworkDelegate != nil {
                          // 家事をする人がいればその人が料理をする
             houseworkDelegate?.cooking()
        } else {
             print("田中家の誰かが料理をした。")
        }
・・・
    }
}
    let family = TanakaFamily()
    family.houseworkDelegate = Mariko() // 家事をする人を、お手伝いさんのマリコにした
    family.lifeCycle()

上記のような処理を行った時、
lifeCycle()を実行するとなんと出力されるでしょうか。
答えは"中華を作ります"です。
代わりに家事をしてくれるマリコさんに料理は任せています。
田中家の人が家事をする必要はありません。

では、以下のように書き換えるとlifeCycle()実行時にはなんと出力されるでしょうか

    let family = TanakaFamily()
    family.houseworkDelegate = Sachiko() // 家事をする人を、お手伝いさんのサチコにした
    family.lifeCycle()

答えは"和食を作ります"です。

このように、「家事をする人」の決まりを作っておくことで、
その決まり(プロトコル)に準拠した人なら、誰でも家事を代わりに行ってもらえるわけです。

delegate = self

最初に例で出した、よく見かけるデリゲート、tableView.delegate = self を思い出してください。
自分自身にdlegateを設定した場合はどうなるのでしょうか。

Mother.swift
class Mother: HouseworkDelegate {

    init() {
        let family = TanakaFamily()
        family.houseworkDelegate = self // 自分が田中家の家事をする
        family.lifeCycle()
    }

    func cooking() {
        print("カレーを作ります")
    }
    func cleaning() {
        print("トイレ掃除をします")
    }
    func washing() {
        print("柔軟剤を使って洗濯物をします")
    }
    func babySitting() {
        print("子供とキャッチボールをします。")
    }
}

上記のクラスのinit()で田中一家のhouseworkDelegateにselfを指定しています。
この場合TanakaFamilyの家事をするのはこのMotherクラスとなり、
MotherをHouseworkDelegateに準拠させるよう実装します。

このように、何らかのクラスの挙動を、自分自身で行いたい場合は、delegateにselfを指定して自分で処理を書きます。

tableView.delegate = self

最初の方に出てきたtableView.delegate = self
が何をしているか少しだけわかったでしょうか。。?

tableView.delegate(UITableViewDelegate)は
たくさんのメソッドの定義を持っており、
UITableViewの各メソッドにはデフォルトの実装が行われていますが、
それを自由にカスタマイズできるようになっています。

自分自身でtableViewの動きを実装したい時、
delegate = selfを使い,
「tableViewの代わりに処理を私(self)が行う」
のです。

ポエムっぽくなってしまったのでポエムタグをつけておきます。。