delegateパターンの実装で迷わないための考え方

今まで雰囲気で書いていたことの本質的な理解をやっていく!

開発環境

> xcodebuild -version
Xcode 14.0
Build version 14A5228q

Xcodeはbetaだが本記事の内容はiOS16未満のAPIを使用。

モチベーション

delegateパターンを実装する際に迷いが生じたのでもう迷わないようにするために実装の考えをメモしておく。

意識すること

意識すること、と書いたが一つ意識できればもう一方が埋まるため一つだけ意識できれば良さそう。

  • 処理を実行する主体
  • 処理を主体に任せたい客体

UITableViewDelegateで考える

Appleが提供しているDelegateプロトコルとして、UITableViewDelegateがある。利用の例として、view controller側でこのprotocolを準拠させるケースがある。

import UIKit

final class ViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView! {
        didSet {
            tableView.delegate = self
        }
    }
}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // action when tapped cell
    }
}
  • 処理を実行する主体: view controller
  • 処理を主体に任せたい客体: table view

table viewの処理をdelegateを介してview controllerに委譲している。
次に主体も客体も無から実装してみる。

お題に沿って考える

お題に沿ってdelegateパターンを実装してみる。

お題

あるview controller(A)から別のview controller(B)に遷移し、Bを閉じた時にAの処理を実行する。

考えること

処理を実行する主体と処理を主体に任せたい客体を考える。
一連の処理の流れの主体はAのため、以下のように設定してコードに落とし込む。

  • 処理を実行する主体: A
  • 処理を主体に任せたい客体: B

となると、お題を言い換えたほうが実装しやすいため言い換える。

「あるview controller(A)から別のview controller(B)に遷移し、Bを閉じた後にAの処理を実行する」

「AからBに遷移し、Bを閉じた後に行う処理をAに委譲する」

コードを書く。

コード

import UIKit

// MARK: - ViewController (A)
final class ViewController: UIViewController {
    @IBAction func showSecondViewController(_ sender: Any) {
        guard let vc = storyboard?.instantiateViewController(withIdentifier: "secondViewController") as? SecondViewController else {
            return
        }
        vc.delegate = self
        vc.modalPresentationStyle = .fullScreen
        present(vc, animated: true)
    }
}

extension ViewController: SecondViewControllerDelegate {
    func didDismissSecondViewController() {
        print("SecondViewController dismissed")
    }
}

// MARK: - SecondViewController (B)
final class SecondViewController: UIViewController {
    weak var delegate: SecondViewControllerDelegate?
    
    @IBAction func dismiss(_ sender: Any) {
        dismiss(animated: true) { [weak self] in
            self?.delegate?.didDismissSecondViewController()
        }
    }
}

protocol SecondViewControllerDelegate: AnyObject {
    func didDismissSecondViewController()
}

処理を実行する主体は誰か?処理を主体に任せたい客体は誰か?を意識すれば迷うことはもう無さそう。

参考