文系iOSエンジニアの備忘録

エンジニア iOS Swift

Swiftのオブジェクト間の通知※他言語の意見も求ム

はじめに

現場で通信処理をModelに切り分ける際に、通信結果取得後にViewへ通知させUI更新させるために、DelegateとColosure Calbackなどパッと思いつく選択肢があって じゃあどれを使おうか悩んだので調べてメリットとデメリットまとめました。

まずはそれぞれのパターンの説明から。

Delegateパターン

swiftではView->ViewControllerやModel->View(ViewController)でよく見かけるパターンです。

抽象的なインターフェースを介して通知、というか処理を行うものです。

swiftではprotocolという実装規則を追加する機能があり(もう一つ役割がありますが)これを利用します。

Model

protocol hogeDelegate: class {
    func updateUI()
}

class Usecase {

  weak var delegate: hogeDelegate?

  func requestUrl(token: String) {
        
        provider.request(.confirmAst(token: token)) { result in
            
            switch result {
            case let .success(response):
                if response.statusCode == 401 {
                    print("Unauthorized")
                }
                
                if response.statusCode == 403 {
                    print("User is not authorized to access this resource with an explicit deny")
                }
                let jsonDecoder = JSONDecoder()
                do {
                    let hogeEntity = try jsonDecoder.decode(HogeEntity.self, from: response.data)
                    self.setAstTel(ast: hogehoge.phone)

                    **delegate.updateUI() <-ココ**

                } catch {
                    **delegate.updateUI() <-ココ**
                }
                
            case let .failure(error):
                **delegate.updateUI() <-ココ**
            }
        }
    }
}

呼び出し側

class ViewController: UIViewController, HogeDelegate {
    let usecase  = Usecase()

    func updateUI() {
        // UI更新処理
       // 引数にデータを持たせて更新なども
    }

    override func viewDidload() {
        usecase.delegate = self
    }

    ~略~
}

Calbackパターン

Callbackパターンでは、完了後の処理をクロージャーで受け取り、そのコールバック用のクロージャーを実行することで通知します。

Model

class Usecase {
~略~

func requestUrl(token: String) {
provider.request(.confirmAst(token: token, completion: @escaping (_ response: AstConfirmation?, _ error: Error?) -> Void)) { result in
            
            switch result {
            case let .success(response):
                if response.statusCode == 401 {
                    print("Unauthorized")
                }
                
                if response.statusCode == 403 {
                    print("User is not authorized to access this resource with an explicit deny")
                }
                let jsonDecoder = JSONDecoder()
                do {
                    let hogeEntity = try jsonDecoder.decode(HogeEntity.self, from: response.data)
                    self.setAstTel(ast: hogehoge.phone)
                    **completion(astConfirmation, nil) <- ココ**
                } catch {
                    **completion(nil, error) <- ココ**
                }
                
            case let .failure(error):
                **completion(nil, error) <- ココ**
            }
        }
    }
}

呼び出し側

 class ViewController: UIViewController {
    let usecase  = Usecase()
    let token = "xxxx"
    override func viewDidload() {
       requestUrl(token: token)  {  response, error in
              // UI更新処理
              // 引数にデータを持たせて更新なども
       }
    }

    ~略~
}

メリデメ表

通知のパターン 通知元と通知先の数 メリット デメリット
Delegate 1:1 プロトコルにより実装すべき通知インタフェースが明確。
通知先で複数の処理をさせたい場合にもその分通知を送ることができる。
通知するメソッドが1つの場合は、記述量に見合わない。
Calback 1:1 処理の依頼部分と完了後の処理を近くに書くことができ、可読性が高い。
コード量少なくシンプルに書くことができる。
呼び出す箇所を増やすたびに処理が増えていく。
クロージャーのネストが増えすぎると逆に可読性が落ちる。

最後に

ケースバイケースだと思いますが、指摘はもちろん 何か今までの経験だったりメリットデメリットがあれば教えてクレメンス(白目)

※共同編集可能です。

参考:Qita Apple 公式ドキュメント:Delegateについて