teratailアプリを作ってみた

#はじめに
teratailのアプリを作成したので、制作過程について書いていきたいと思います。
[teratail](https://teratail.com/)はエンジニアのQ&Aサイトです。[API](https://teratailv1.docs.apiary.io/#)が提供されているのでこのAPIを利用しています。

##完成像
![teratail.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/8517a7b2-0f4d-691f-eead-f27174b22b9f.gif)

#機能
##質問一覧
新着・Swift・Flutter・お気に入りした質問を表示させています。
タブの切り替えはXLPagerTabStripというライブラリを使用しています（ライブラリ詳細は後述）。
また、下までスクロールするとAPI通信を行い、追加で次の質問を読み込んで表示するようにしています。
![home.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/175ee10e-c30f-7774-a6d6-cbf566d1d5be.gif)


##質問詳細
質問のセルをタップしたら、WebViewで質問の詳細を表示します。
ローディング表示にはSVProgressHUDを使用しました。
![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/f9fb5287-2462-bd72-5d9c-9d374ade4d09.gif)


##お気に入り
⭐️マークをタップするとお気に入りの追加/削除ができます。
お気に入りした質問IDはUserDefaultsに保存しています。
![favo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/95036484-2096-2003-6eb2-2c9be1e9391e.gif)


##検索
teratailのAPIにタグ情報が含まれているので、そのタグをキーに検索をかけています。
![search.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/5fe357ed-a97d-1e1b-d0aa-28bf00b69be9.gif)


##検索履歴
検索履歴もUserDefaultsに保存しています。
同じワードで検索された場合は、検索履歴に二重で表示されないよう制御しています。
右の✖︎ボタンをクリックすると削除できるようにし、検索履歴のワードをタップすると、UISearchBarのTextFieldに自動で入力されるようにしています。
![result.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/88247ff2-2e56-976d-e56a-d338abad07d0.gif)


##エラーハンドリング
検索結果ゼロ件、ネットワーク通信エラー、また、teratailのAPIにはAPI使用回数制限があり、回数超過した場合は通信できなくなるので、その旨もユーザーに分かるようにエラーメッセージを表示させています。API通信のレスポンスで返ってくるStatusCodeによって場合分けしています。

|通信エラー|API使用回数超過|お気に入りゼロ|検索結果ゼロ|
|---|---|---|---|
|<img width="500" alt="unnamed.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/4deb3180-ad09-ae73-ad46-b5bb70c52f93.jpeg">|<img width="500" alt="unnamed-2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/a45f486e-1359-5e68-6602-4d1604f5f793.jpeg">|<img width="500" alt="unnamed.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/712dad10-ed10-02b9-a208-27677798fb09.jpeg">|<img width="500" alt="unnamed-1.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/21037f15-9a2f-123f-0f23-513ec17fc977.jpeg">


#技術要素
##アーキテクチャ
MVPを採用しました。
各レイヤーの役割は以下のようになっています。

###View
- 画面の描画
- ボタンタップ等のイベントをPresenterに渡す
- Presenterから渡された処理を表示（UIの更新）

###Presenter
- Viewから受け取ったイベントをもとにModelに処理を移譲
- Modelからの処理完了の通知を受けて、Viewを更新
- Viewの状態を保持

###Model
- データの処理
 - API通信して質問データの取得
 - UserDefaultsを使用してデータの取得・更新
- データの取得・更新が完了したらPresenterに通知
 - 今回はNotificationCenterを使用 

###MVPのメリット
- ViewControllerの責務を分けることができる
 - FatViewControllerの防止
- 学習コストが高くない
 - MVVMと異なりRx等、新しい技術を学ぶ必要もないので、MVCの次は何を勉強しようと悩んでいる人には良いと思います


##ライブラリ
###[Alamofire](https://github.com/Alamofire/Alamofire)
ネットワーク通信では定番のライブラリですね。

###[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)
Alamofireとセットでよく使われていますね。JSONオブジェクトをパースするのに使用しました。

###[XLPagerTabStrip](https://github.com/xmartlabs/XLPagerTabStrip)
横スワイプでタブの切り替えを実装するのに使用しました。今回初めて使ってみましたが、とても簡単におしゃれなデザインを実装できるのでおすすめです。デザインも⬇️のように色んなパターンがあります。

![instagram.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/f572ea8b-d91d-2d46-6edd-796e78b1ed71.gif)![youtube.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/c7ef2d7c-62e2-8d5e-90ea-681b18d22778.gif)
![pagerTabStripTypes.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/f96cf4b2-bdaa-60d7-1b08-ae03c9f7ab89.gif)![spotify.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590610/ef9d5d3d-2378-c68a-88aa-3f8c147df199.gif)


###[SVProgressHUD](https://github.com/SVProgressHUD/SVProgressHUD)
WebViewを表示する時のレスポンス待ちのローディング表示に使用しました。
シンプルなデザインなので、どのアプリでも使いやすいと思います。

###[SDWebImage](https://github.com/SDWebImage/SDWebImage)
プロフィール画像をキャッシュするのに使用しました。
使用前後で表示スピードがかなり違ったので、使ってよかったです。


##ライブラリ管理
CocoaPodsを使用しました。
ただ、Firebase系のSDKを使用する場合はビルドが相当重くなるので、その場合はCarthageがおすすめです。

##DB
先述した通りUserDefaultsを使用しました。
もともとFirebaseを使ったことがあったので、途中までCloud Firestoreを使用して実装を進めていました。ただ、保存したかったデータは下記のみだったので、わざわざCloud Firestoreを使用するほどでもないなと思い、UserDefaultsに変更しました。結果、ローカルDBで十分でした。作りたいアプリに対して適切な技術選定するのは大切だと思うので、これは意識していきたいです。

- お気に入りした質問ID
- 検索履歴