Swift時代に悩ましいUIViewControllerをどう扱うか

これは [Swift Tweets](https://swift-tweets.github.io/) の発表をまとめたものです。イベントのスポンサーとして Qiita に許可をいただいた上で、このような形（ツイートの引用）で投稿しています。

--------

[Twitterのハッシュタグはこちら](https://twitter.com/hashtag/swtws?f=tweets&vertical=default&src=hash)

--------

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Swift Tweetsオーディエンスの皆様こんばんは！Tweetupという新しい試みに参加させていただきとてもワクワクしています。本日は「Swift時代に悩ましいUIViewControllerをどう扱うか」についてご紹介させていただきます。よろしくお願いします。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/JWfOjH0E1W">pic.twitter.com/JWfOjH0E1W</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820256300269334533">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">まずは自己紹介から。杉上洋平と申します。iOSの開発は日本でiPhoneが販売されたときに、嫁が早速手に入れてアプリがないので作ってほしいと言われたのをきっかけに、2008年からアプリを作り続けています。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/rDgsq1OtU8">pic.twitter.com/rDgsq1OtU8</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820256438303924225">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">AppStoreでのアプリの自主販売での生計を皮切りにフリーランスを経て現在はWantedlyでアプリの開発を行っています。WantedlyではSwiftが1.0Betaのころから３本の新規アプリを開発しSwift言語の成長とエコシステムの発展と共に歩んできました。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/pYJwaP0Vqm">pic.twitter.com/pYJwaP0Vqm</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820256568457326593">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">アプリの開発にあたりOSSやコミュニティの皆さんに助けられて進めることができました。そのため開発で得た知見は Pay it Forwardの精神のもとQiita( <a href="https://t.co/mv6q3S2Fst">https://t.co/mv6q3S2Fst</a> )に記事を記載させていただいております。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/zGJcd758B4">pic.twitter.com/zGJcd758B4</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820256693607014400">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">本発表では去年11月にリリースしたWantedlyPeopleというアプリの開発において、Swiftの特性を活用しつつUIViewController(以下VC)をどのように向き合うか試行錯誤した内容をご紹介します。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820256819683540992">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">この扱い方が一般的により優れているということではなく、アプリの要件、開発規模、チームメンバー構成、設計方針により適応できるかどうかはケース・バイ・ケースになると思うので、開発の選択肢の１つとして捉えていただけると幸いです。コードはSwift2.3になります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820256945336512514">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">アプリ開発においてVCは画面のライフルサイクルと遷移を担い、AppleMVCパターンでアプリを構築する上で中心的な存在となっています。その上でVCは開発者にとって重量であると同時に悩ましい問題をたくさん抱える課題の宝庫でもあります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/YpgLc5bOV3">pic.twitter.com/YpgLc5bOV3</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820257083626901504">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">代表的な以下の課題を含め多様な視点でVCと試行錯誤しました。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a><br>- SwiftとStoryBoardの相性が悪い<br>- VCの肥大化問題と抽象化・共通化をどのように行うか<br>- 画面間(VC)を疎結合に扱いたい<br>- Depplinkで任意画面へのダイレクトな遷移を行いたい</p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820257222458384389">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">iOSの開発ではStoryBoardを活用して画面の構成を構築する方が多いのではないでしょうか。StoryBoardは直感的な操作でエンジニア、デザイナー共に扱いやすくビューの配置やレイアウトを視覚的に配置・把握が行える素晴らしいツールです。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/IVe5uasrdi">pic.twitter.com/IVe5uasrdi</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820257347935162369">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">AutoLayoutの設定ではリアルタイムで整合性を検証してくれるためあるべき正しい設定へと導いてくれます。また画面間の遷移も設定が行え画面遷移図としての仕様としても重宝します。そんな素敵なStoryBoardですが万能ではありません。使いづらい点を見てみましょう。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820257473806221312">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">StoryBoardとSegueによる画面遷移を行うコードです。画面遷移決定時に遷移先の画面へ渡したい引数が確定することが都度だと思いますが引数の設定はprepareメソッド内で行うため、渡したい引数はsenderというAny?型で引き回す必要があります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/MrLqKzz2hP">pic.twitter.com/MrLqKzz2hP</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820257599719231488">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/ae2e6a24f533500d811895d66e1a3532

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">このときいくつかの悩ましい問題に直面します。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a><br>- sender引数を利用すると遷移先の画面へ渡したい引数の型が失われてしまう、引数が複数の場合Dictionary型として扱う<br>- prepareメソッドで責務が集約されているように見えるけどなんとなく冗長</p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820257738043203584">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">- Identifierは文字列型を取るためコンパイラチェックできない<br>- SecondViewControllerのinitをコードから呼び出すことはできない、そのため初期化後のインスタンスのプロパティへ代入することで代替する必要がある<br> <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820257876379766784">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">また画面遷移先であるSecondViewControllerのプロパティtitleNameは、初期値が不変でイミュータブルな場合でもシンプルにlet定数で定義できずForceUnwrapか再代入が不要でもvar変数として定義する必要があります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/LbLIK6rSIQ">pic.twitter.com/LbLIK6rSIQ</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820258002410172416">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/f7334b192521afb64df7da3ab47920ac

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">イミュータブルであることは保証したいのでForceUnwrapを記述してお茶を濁すケースが多いのではないでしょうか。ただしForceUnwrapで定義したlet定数はviewDidloadなどで呼び出される前に事前に初期化しておく必要があります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820258128327372800">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">そのため以下のようなコードではクラッシュをしてしまいます。この点はコンパイル時に抑止できないので、プログラマーがしっかり安全性を担保する必要があります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/SV0yWhYRhb">pic.twitter.com/SV0yWhYRhb</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820258266760376324">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/f0c37c9f176ffbed4ab72a170b89a540

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">なんとStoryBoardはSwiftと相性が悪いことでしょう。この嘆かわしい状況に１点の光を指し示す発表が昨年のTry!Swiftでありました。「実践的 Boundaries」の中で紹介されたImmutable Coreの考え方です。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/gZd7hxABVA">pic.twitter.com/gZd7hxABVA</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820258396028796929">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">StoryBoardを利用せずVCのinitを活用したコードは以下のようになります。required initの記述など冗長な点はありますが我慢できる範囲内です。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/L4rLBt92gR">pic.twitter.com/L4rLBt92gR</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820258659267592192">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/d941e2f51cab365ea65a0e0ac00fc0a0

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">僕は思いました。そうだStoryBoardを捨てよう。。。よりSwiftらしくVCと向き合うために。型安全でイミュータブルで、プログラマーによる担保ではなく、コンパイラによる不整合の発生しないことが保証される世界のために。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820258785155379202">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ただし、この選択は上述のメリットと、裏腹にStoryBoardを利用できないというトレードオフによるデメリットの両方を内包します。以降ではデメリット部分とその対策について見ていきます。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820258923491930113">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">StoryBoardを活用しないとアプリの画面遷移がわかりにくくなるという課題があります。StoryBoardがないために途中参加のメンバーがある画面がどのVCに紐づくのかわからないため、ログを出してひとつづつ確認していったという話も聞いたことがあります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820259057688662016">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">StoryBoardはそれ自体が実装なので画面遷移図として見たときも図（仕様書）と挙動が乖離しないメリットがありますが、ここではStoryBoardを画面遷移図とする代替手段として画面遷移図を把握するための２つのツールをご紹介します。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820259195618410496">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">１つ目はGoodpatchのProttというプロトタイプツールです。iPhoneから利用し画面の一部をタップすると指定した画面へ遷移する動きを実際のアプリさながらに確認できます。この遷移の設定を行うと自動的に各画面が重ならないような画面遷移図を自動生成してくれます。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/bkQZUf5lGi">pic.twitter.com/bkQZUf5lGi</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820259321468502017">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">初期開発から導入しておけば、デザイン ➜ プロトタイプ/画面遷移図 ➜ iOS実装の流れで開発が進められるので大変重宝します。また遷移の変更などメンテナンスもしやすくワイヤーの画面遷移図よりも視認性も高くいので素晴らしい仕様書となります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820259447364689920">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">２つ目は37signalsで紹介されているUI Flowです。UI Flowの記述はテキストで行えるguiflowを利用しています。新規登録ログインなど条件により複雑に遷移先が変わる遷移部分について活用しています。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a><a href="https://t.co/ByfACqRuV1">https://t.co/ByfACqRuV1</a> <a href="https://t.co/abAQqPDuEP">pic.twitter.com/abAQqPDuEP</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820259585751588864">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">画面の遷移はStoryBoardのSegueや画面遷移図の遷移順だけではなく、アプリ外でのユニバーサルリンクやプッシュタップによって任意画面ダイレクトに遷移したい場合もあるでしょう。さらにその画面への遷移に伴う一連の遷移スタックも保持したい場合もあるかもしれません。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/rIBE7r8RGd">pic.twitter.com/rIBE7r8RGd</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820259724620754944">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">このような場合にSegueベースの遷移ではスタックを保持しつつの遷移は扱いにくいのですが、VCのinitで初期化するコードではシンプルなので比較的扱いやすいです。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/tjekd7WIWv">pic.twitter.com/tjekd7WIWv</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820259850030415873">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">先程のコードは一見うまく動作しそうに見えますが実はバグを孕んでいます。rootViewControllerのナビゲーションが起点のVCの表示ではなく、すでにある画面へ遷移していたりあるモーダルの表示がある場合は遷移スタックに不整合が発生します。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820259975922532353">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">しかもモーダルの表示はVCに限らずUIAlertControllerによるアラートやアクションシートも含まれます。そのため再帰的にすべてのモーダルをdismissしたり、すべてのナビゲーションをポップしたりする必要があるのですが漏れの危険もあり骨が折れます。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820260114137350144">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">そのため起点のVCを作り直してしまうことで現状の遷移状態を破棄し１から画面と遷移を構築します。このような処理が頻繁に発生する場合はVCのメモリリークがないかチェックもあわせて行います。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/vOipXUBbV6">pic.twitter.com/vOipXUBbV6</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820260252582965249">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/5146868cc71d97ebaa2cb725ee13637e

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">上記のような起点からVCを作りなおす実装を行うに限らず、すべてのVCがしっかりdeinitが呼ばれているかを確認することはとても大事なことです。普段から画面を閉じたり、戻った場合はVCのdeinitが呼ばれているかログなどでこまめに確認するのがよいでしょう。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/AieonKE1nz">pic.twitter.com/AieonKE1nz</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820260378688897028">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/9dfbc7b52df2c1eda65832683604a07d

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ダイレクトな遷移先が１つだとシンプルで良いのですが、多義に渡る遷移パターンがある場合は、ルーティングを担うロジックを導入することを検討すると良いでしょう。ライブラリがいくつかありますが、Swiftはenumが強力なので自前実装でも十分対応可能です。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/H2B8rTqURb">pic.twitter.com/H2B8rTqURb</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820260504278941697">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/f8e840e573996e0bda4ff0255f1f70e7

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">URIベースのルーティングテーブルはパスとIDで構成されます。パスは対応するVCのクラスを一意に定めます。VCが必要なデータそのものを引数とせず、データのIDのみを引数にとる設計だとURI情報のみである画面へ遷移が可能になりよりルーティングと相性がよくなります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/nE7dY5kMOk">pic.twitter.com/nE7dY5kMOk</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820260630150070272">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/a7e9fcf1e90f571bcb81d44715134d62

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">VCはデータのIDのみを自身の初期化時に受け付けて紐づく必要なデータをローカルのDBや通などから取得します。ルーティングや遷移元のVCから、遷移先のVCへ依存はIDのみになるので疎結合であり独立生が高くVCの責務がVCに閉じる設計となります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820260756411191296">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">また、VCの初期化時の引数により内部の分岐するような場合は、引数をenumで定義して分岐処理部分を極力enumに実装することでVCの可読性が高くなります。以下は１つのVCで自分と他人のプロフィールを表示する例です。次のtweetに続きます。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/33eQRuLEl0">pic.twitter.com/33eQRuLEl0</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820260894416326657">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/ca18fe34127194ebd7284ec85f5271f2

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">自分か他人かの差異はenumに閉じているので、VCではその差異を意識せず見通しの良い記述が行えます。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/g6Xk12iXgC">pic.twitter.com/g6Xk12iXgC</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820261030668288000">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/4718b98cd988c14779c92c03f4b3782d

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">さて、StoryBoardは使わない方針としていますが、やはり部分的にInterfaceBuilderを利用してビューのデザインを行いたいケースも多々あります。そのような場合はVCに紐づくStoryBoardで構築するのではなくビューに紐づくXIBを利用しています。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820261156539498498">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>


<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">XIBの初期化を行うコードは以下のように記述が長くForceUnwrapもあり、引数のnibNameは文字列をとるなど頻繁に記述するには使い勝手がよくありません。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/CH8WgIrUql">pic.twitter.com/CH8WgIrUql</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820261282204958729">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/7131ad5770c7ea13d34f52c6ba8aa376

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">そこで、Protocolを活用して記述しやすくかつ、ForeUnwarapを局所化しています。ビューのクラス名とXIBのファイル名を揃えることで文字列の介入を防いでいます。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/MEqLtlR33u">pic.twitter.com/MEqLtlR33u</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820261408147271681">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/74de0a4e652952f55fa4e8fe7cef5034

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">以下のようにInstantiatableFromNibプロトコルに準拠したビューを定義します。XIBの初期化を行うコードは文字列とForceUnwrapが排除され記述しやすく可読性も向上しました。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/ks9jXTgYGW">pic.twitter.com/ks9jXTgYGW</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820261545447821313">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/3f48a5eca2a316163363abf64d3d93a8

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">InterfaceBuilderを利用してビューの生成、AutoLayoutの設定を行うのもよいですが、強力なSwiftの世界で行えないものでしょうか。以降では左記の2点（AutoLayout、ビューの生成）について掘り下げていきます。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820261683847233537">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Swiftと言えどもAutoLayoutをそのままコードで扱うのはとても辛いです。以下はparentViewに追加したchildViewがparentViewと同じ４辺を共有するレイアウトの例です。２行ぐらいで済みそうな記述なのにこんなに記述する必用があります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/rki39eNnWz">pic.twitter.com/rki39eNnWz</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820261809709948928">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/eab51991cfe73eff6e2119c472bab901

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">そこでSnpaKitというライブラリを活用します。先程と同等のコードが端的で直感的な記述で行うことができます。非常に簡単にAutoLayoutを記述できるので自分はXIBによるレイアウトはごく一部でほとんどのAuotLayoutをSnapKitで記述しています。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/FTytHLJEmW">pic.twitter.com/FTytHLJEmW</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820261935543259136">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/c9cabf93d602d059e9776b68f66b0f97

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ビューのレイアウトの次は、コードで行うビューの生成について見ていきましょう。以下は生成とAutoLayoutを行うコードです。ViewDidLoad内ですべて記述しているので画面の規模が大きくなると煩雑になりそうです。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/SMMM94zx2f">pic.twitter.com/SMMM94zx2f</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820262061397528576">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/9e13d29ca48cea978b8e0f250f2a94b7

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">そこでビューの生成部分をクロージャーによるデフォルトプロパティを活用して生成と初期設定値をまとめて記述します。ViewDidLoadにいくつものビューを手続き的に記述するよりもビュー単位の初期化コードの範囲が明確に局所化され可読性も向上します。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/d8oPZENdXn">pic.twitter.com/d8oPZENdXn</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820262187277029376">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/6d699a0d6c5bc0404d981e8f99d36b97

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">注意点としてクロージャが実行される時点ではVCインスタンスが保有するその他のプロパティは初期化されていません。つまりクロージャ内部ではその他のプロパティへアクセスすることが出来ません。ということはselfプロパティも使えませんし、インスタンスメソッドも使えません。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820262325617725440">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">画面間の遷移がサクサクと滑らかに感じられるとユーザの体験は向上します。もしある画面への遷移時にもたつきを感じ、ある画面が遷移の初期状態で表示しないビューがいくつかある場合は、遷移時に表示しないビューを初期化しないことで遷移の速度を向上できることがあります。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820262464130465793">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">上記は遅延評価プロパティを活用することで容易に記述できます。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a><br>- 初期値の設定処理を参照が発生するまで遅延できる<br>- デフォルトプロパティと異なりselfを参照できる<br>- クロージャー内のself参照は循環参照を発生しない</p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820262589842092033">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">遅延評価については、Kumagaiさんが大変すばらしい資料にまとめてくださっているのでご参照ください。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a><a href="https://t.co/JlJ0H11o6D">https://t.co/JlJ0H11o6D</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820262715780251649">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">以下のコードはemptyViewの初期化を通信の結果件数が０件であると評価するまで遅延する例です。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/dlfKaYaWnf">pic.twitter.com/dlfKaYaWnf</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820262853110136832">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/d316547800b35d8380a2875b573619a6

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ビューの遅延評価による初期化だけでなく、子のVCの初期化遅延にも活用できます。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/N5z4mJXIId">pic.twitter.com/N5z4mJXIId</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820262991476060160">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/76b02bc09c3d8dcae247ac8d9df4927d

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">UITableViewはアプリにおいて頻出のコンポーネントです。画面のVCをUITbaleViewController(以下TVC)を継承して開発する方も多いのではないでしょうか。 そうするといくつか問題が発生することがあるのでTVCを継承しなくなりました。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820263117955350533">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>


<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">TVCを継承するとtablewViewがVCの起点ビューとなるため後々のUI/UXの変更によりtablewViewと並列やtablewViewよりも背後のビューを生成できずに作りなおすはめになることがあるからです。なのでTVCを子のVCとして扱うようにしています。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/orkWMZznSP">pic.twitter.com/orkWMZznSP</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820263243344023552">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/11b4f7e946960caa307e9e9edb6b18e2

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ちなみにtableViewのみを作成せずにUITableViewControllerを活用しているのはセルをタップしてUINavigationController#pushで遷移してpopで戻ってきた場合にセルのハイライトをアニメショーン付きでオフにしたいためです。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820263369139621888">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">TVCを継承しない話をしましたが、僕は基本的にアブストラクトなVCを設けて処理を共通化する方法は行わないようにしています。ある画面のVCは常にUIViewControllerを継承し、finalを明記することで自身も継承させてません。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820263507400671233">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>



<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">VCである処理を共通化したい場合は、独自のビューを持つ場合は任意のVCクラスと切り出して子のVCとして扱うか、プロトコルによる抽象化とプロトコルエクステンションによる実装を行います。これらにより肥大化しやすいVCをコンパクトに扱うよう心がけています。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/X416MrnBvq">pic.twitter.com/X416MrnBvq</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820263645732966400">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/3a3f1a06923994c1e32845a5e87e8a44

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">継承ではなくプロトコルによる共通化を行うのは共通化したい対象を責務の範囲として局所化しやすいためです。プロトコルはVCに多数適用できますが、継承だとどうしても最大公約数的な共通処理のアブストラクトなVCを継承しがちで多様な共通処理がクラスに記述されてしまいます。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820263781368352768">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">UIViewControllerをextensionすることで共通化を行うこともできますが、実装した影響範囲がすべてのVCになるので本当にすべてのVCで利用するような処理だけを記述するようにしています。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/uHDTry1zYe">pic.twitter.com/uHDTry1zYe</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820263906283175936">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

https://gist.github.com/c8d4f08aa270e5eb08c7400bd15298be

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">以上で「Swift時代に悩ましいUIViewControllerをどう扱うか」終了です。ObC時代から苦しめられたVCですがSwiftのお陰で悩みも軽減された感じがします。それでは、ごTweet聴ありがとうございました。m(_ _)m  <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/0FtaoCUqyd">pic.twitter.com/0FtaoCUqyd</a></p>&mdash; susieyy (@susieyy) <a href="https://twitter.com/susieyy/status/820264032154238976">2017年1月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>