protocolを使うと、非同期通信後のUI更新処理がとてもわかりやすかった話

# はじめに

こんにちは、iOSエンジニアの dayossi です。

家族が幸せになれるサービスを提供したいと思って、
[HaloHalo](https://hallo-hallo.sakura.ne.jp/home/)という家族日記アプリをリリースしています。


今回は、非同期通信後の処理について
protocolがとても便利だったという内容を書いています。

（今回の記事の成果物は、最下部に載せています）

# そもそも非同期でUI更新したいと思ったきっかけは？

「非同期通信でデータを取得したあと、CollectionViewに値を反映しようと思ったけど
なんか表示の仕方がうまくいかないなー」

と思ったのがきっかけです。


## 原因は？

今回は非同期通信の処理に絞って、2つの問題に分けて考えてみました。

・１：参照しているデータがおかしい
・２：UI更新が遅い、滑らかにできない


###１：参照しているデータがおかしい

・クラス型で構築したデータモデルに、APIで取得した値を格納すると
 格納した値は参照型となります。
 変数に置き換えたあとでも、変数内のデータを書き換えると
 もとのデータも書き換えられます。

・なので、例えば複数のユーザーデータを同時に取得して
 ユーザー固有の値を表示したい場合だと
 UI更新が行われる前にデータが書き換えられてしまう可能性があり、
 期待どおりの表示にならない場合があります。

【 参照元 】
[Value Semantics を持たない型の問題と対処法](https://heart-of-swift.github.io/value-semantics/problems-without-value-semantics)



###２：UI更新が遅い、滑らかにできない

MVCモデルのように、UI表示を行うViewとUI描画に必要なデータを持つModelとが密接になっていたり
ViewControllerに全ての処理をベタ書きして、FatViewControllerの状態になると
API通信でサーバーからデータを取得する処理と
UIを構築する処理が同一スレッドで行われるため

メインスレッドでの処理が止められ、わずかな時間ではあるものの
アプリの挙動が止まります。

そのため、サクサクとキレイにUIを表示してほしいのに
画面がｶｸついてしまう問題があります。

【 参照元 】
[【Swift】Grand Central Dispatch (GCD)とOperationQueue まとめ](https://qiita.com/shiz/items/693241f41344a9df6d6f)



## いままで行っていた対策

これらの問題の対策として以下を、個人的によく取っていました。

###１つ目：参照しているデータがおかしい　
###→ データの保持方法を工夫する
● データを保持するときは、structによる値型で管理する

→ APIから返ってくるJSONデータを、structによる値型で保持すると
複数の通信結果を受けたときに、変な上書き処理が行われるリスクをぐんと減らせるから


● 格納したデータを、別の配列や辞書型の変数に入れて管理する

→ SwiftのArreyは、参照型のデータを値型で保持してくれる機能があるから(。。。便利!!!)

【 参照元 】
[SwiftのArrayが実はすばらしかった](https://qiita.com/koher/items/47f444200d4e02b6414e)）


###2つ目：UI更新が遅い、滑らかにできない 
###→ 非同期処理に切り替える
● DispatchQueueを活用した非同期処理を入れる

→ UI描画・更新を行うメインスレッドと、データ処理や通信を行うバックグラウンドスレッドを分割して
メインスレッドの処理を軽くしたかったから

【 参照元 】
 [Swift の非同期処理の例（複数パターン）](https://qiita.com/yamatatsu10969/items/4265d1878251145ad3d5)
[【Swift】Grand Central Dispatch (GCD)とOperationQueue まとめ](https://qiita.com/shiz/items/693241f41344a9df6d6f)

ただ、非同期に得られたデータをUIへ反映するための伝達手段として
コールバックで値を返すやり方をゴリゴリ書いていたので

ネストがどんどん深くなり、カオス状態になっていました。。。
もう読み返したくなくなるくらい。。。泣

```
class hoge{
  /*
    例えば、ユーザーデータをサーバーから取得したあと 
    HugaClassにある getImage() というメソッドで
　　画像データを取りに行きたいのに...
  */

  // ムダにクラス全体をインスタンス化...  
  let huga = HugaClass()
 
  // ネストが深いうえに、クロージャ内で強参照...
  func getUserData(){ (userData) in
      let userId = userData.userId
      huga.willFetchImage(userId){ (imageData) in
         self.imageData.image = imageData.image
      }
  }
}
```

一番ネックになったのはこの
「取得したデータをもとに、どうやってUIを更新するか？」
という点でした。


# protocolの可能性

前置きが長くなりましたが、ここからがようやく本題です。

クラス内に実装した処理を呼び出すために、
毎回クラスをインスタンス化して、そのクラス内の処理を呼び出すと
どこに何の処理を書いたのかがわかりにくくなります。

少ないコード数で書き出せる反面、
活用できる場面が非常に限定的になるもろさもあります。


そんな中、iOSアプリ設計パターンを読んでいて
>Model
>> Model自身とRxSwiftらしいModel実装について、次の観点で解説します
>>・ protocol化して疎結合に、テスタブルにする
>>・ Observableを返却して、VIewModelとの建て付けを良くする

>(引用元：iOSアプリ設計パターン入門 p124 より)

の中の 「protocol化して疎結合に、テスタブルにする」 という内容が印象的でした。

要するに、protocolを介して
必要な情報だけを抽出して伝達しましょう、ということと認識しています。

```
// MARK:- どんな処理を引き渡すのかが明確!
protocol HugaProtocol{
     func willFetchImage(_ userId:String)
}

class hoge: hugaProtocol{
  
  // クラスではなく、protocolで引き渡せる
  private weak var repository:HugaProtocol?
  init(repository:HugaProtocol){
    self.repository = repository
  }

  func getUserData(){ (userData) in
     let userId = userData.userId
     input.willFetchImage(userId)
  }
}
```

この書き方にすると、特定のクラスに依存しなくて済むようになるので

ある特定のクラス内の処理をわざわざ呼び出さなくても
protocolに準拠したクラスなら誰でも、
protocol内の処理を利用可能な状態にできます。

つまり、柔軟に設計を替えられるようになります。

さらに、これまでのclass同士をコールバック処理でくっつけるようなやり方よりも
各クラスの責務も明確に確認できるので、
どこで何の処理を行っているかがわかりやすくなります。

プロトコル指向とオブジェクト指向の違いと、プロトコル指向の恩恵に
ここでようやく気がつきました。。。

【 参照元 】
 [プロトコル指向プログラミングについて調べてみた](https://qiita.com/kotaro_p/items/4a4ebd34b15ccbcd1895)
 [WWDC 2015「Swiftでプロトコル指向プログラミング」](https://qiita.com/eytyet/items/205b09e9fa69810e83db)


# 非同期処理への応用

protocolの特徴は
特定のクラスに依存しなくても、特定の処理を呼び出せる点です。

なので、CollectionViewやTableViewのライフサイクル内で
データ取得→UI描出→UI更新を無理やり同一スレッドで完結させなくても、

データ取得後、データを保存してからreload処理を呼んで
改めてUI更新→UI再描出を行う方法をとることができます。

アプリ設計とprotocolによって、非同期処理に柔軟に対応でき
かつ滑らかなUIアニメーションも損わずに済みそうです。


もう、protocolサイコーだなって…感動しました!!


# もっとprotocolを活用しよう!!
protocolを活用できると、わかりやすい設計になるだけでなく
変化に強いiOSアプリにできそうな予感しかしないので
本当にワクワクしました!!

特に非同期通信をこまめに行うアプリでは
protocolは大いに活用できそうです。

これからもっとprotocolと仲良くなろうと思います!!


# 今回の成果物
（あくまでprotocolの活用事例を示しています。
 このままでは到底、実践には活用できない点はご容赦ください）
https://github.com/Kohei312/CollectioView_loose-coupling

# 参考書籍・記事

###書籍
もっと早く読んでおきたかった...わかりやすくてホントにオススメです!
[ 関 義隆, 史 翔新, 田中 賢治 他. iOSアプリ設計パターン入門(2018) PEAKS出版](https://peaks.cc/books/iOS_architecture)

###値型について
[SwiftのArrayが実はすばらしかった](https://qiita.com/koher/items/47f444200d4e02b6414e)
[純粋値型Swift](https://qiita.com/koher/items/0745415a8b9842563ea7)
[Value Semantics を持たない型の問題と対処法](https://heart-of-swift.github.io/value-semantics/problems-without-value-semantics)

###非同期処理について
 [Swift の非同期処理の例（複数パターン）](https://qiita.com/yamatatsu10969/items/4265d1878251145ad3d5)
[【Swift】Grand Central Dispatch (GCD)とOperationQueue まとめ](https://qiita.com/shiz/items/693241f41344a9df6d6f)

###プロトコルについて
[Protocol-oriented Programming とは](https://heart-of-swift.github.io/protocol-oriented-programming/)
[Swiftのプロトコル](https://qiita.com/abouch/items/67f9def6dc2feec3e183)


###delegateなどの情報通知について
[【Swift】DDDを取り入れたiOS開発 その1 ~UseCaseとdelegate~](https://tech.recruit-mp.co.jp/mobile/post-8521/)
[Swiftとオブジェクト間の通知のパターン](https://qiita.com/shtnkgm/items/f07f1c61985214ceb7b3)
[[iOS] iOSのDelegateをしっかりと理解する](https://dev.classmethod.jp/articles/ios-delegate/)
[SwiftにおけるDelegateとは何か、なぜ使うのか](https://qiita.com/st43/items/9f9990d76cefa1909ef4)
[【Swift】delegate実装の流れ](https://qiita.com/s_emoto/items/04505ed549178555b10b)

###Dependency Injection(DI)
[猿でも分かる! Dependency Injection: 依存性の注入](https://qiita.com/hshimo/items/1136087e1c6e5c5b0d9f)
[swiftで 依存関係逆転の原則 を使ってテストしやすい設計にする](https://qiita.com/peka2/items/4562456b11163b82feee)

###(+α)
[【Swift】ハマりがちな循環参照について](https://qiita.com/1234224576/items/2241266f7d307cd25ba9)