Auto Layoutのアルゴリズム | iOSDC Japan 2017 Day1

iOS 6から登場したAuto Layoutは、マルチデバイス・可変レイアウト対応が必要な現在のiOS開発において、無くてはならないものになりました。 しかしその裏側で、一体どんな仕組みでレイアウト計算が行われているのか、ご存知ですか? このプレゼンでは、Appleブラックボックスの中身を数学的手法で紐解いていくとともに、 Auto Layoutを再発明する上での型安全な設計のテクニックを紹介します。

f:id:niwatako:20170916102805j:plain

AbemaTVの@inamiyです。 台風が来る仲お越し下さりありがとうございます。

限られた時間ですべて把握するのは難しいテーマですが、流れを理解していただければ成功かと思います。

f:id:niwatako:20170916103115j:plain

Abemaからは8名登壇です!

f:id:niwatako:20170916103134j:plain

今は様々な画面サイズがあります。のどかな時代は終わりました。

ベゼルレス、画面が長方形ですら無いiPhoneが出ましたね。

f:id:niwatako:20170916103205j:plain

f:id:niwatako:20170916103214j:plain

昔から開発されていた人はFrameを計算したりAutoResizingMaskを使ったりしていました。

iOS6でAutoLayoutが登場。 PaddingやViewの感覚を指定できるようになりました。

f:id:niwatako:20170916103257j:plain

制約ベースのレイアウト。コードでもGUIでも指定することができます。

f:id:niwatako:20170916103318j:plain

f:id:niwatako:20170916103325j:plain

ビジュアルフォーマットというクールなものもあります。使っている人を見たことはないですが。

やるならこうですね

f:id:niwatako:20170916103340j:plain

AutoLayoutは属性や関係性を定数や式で表します。

f:id:niwatako:20170916103401j:plain

f:id:niwatako:20170916103407j:plain

f:id:niwatako:20170916103432j:plain

関係性、つまり順番に依存しない、宣言的に記述します。

関係式はイコールだけではなく不等式や優先度が指定できます。

f:id:niwatako:20170916103523j:plain

これらのアルゴリズムはどう実装出来るでしょう

f:id:niwatako:20170916103531j:plain

f:id:niwatako:20170916103540j:plain

f:id:niwatako:20170916103605j:plain

f:id:niwatako:20170916103621j:plain

f:id:niwatako:20170916103609j:plain

f:id:niwatako:20170916103625j:plain

f:id:niwatako:20170916103627j:plain

f:id:niwatako:20170916103651j:plain

最も簡単なパターンを考えていきます

f:id:niwatako:20170916103719j:plain

f:id:niwatako:20170916103724j:plain

f:id:niwatako:20170916103732j:plain

重なっている領域が実行可能領域

f:id:niwatako:20170916103807j:plain

f:id:niwatako:20170916103848j:plain

Autolayoutっぽい

f:id:niwatako:20170916103902j:plain

f:id:niwatako:20170916103905j:plain

シンプレックス法のほうが差分更新できて便利。どこかネットに落ちていないか探したら

f:id:niwatako:20170916103949j:plain

f:id:niwatako:20170916103952j:plain

CocoaはLion、iOSは6から対応

f:id:niwatako:20170916104023j:plain

地球上で最も危険他鳥、火喰い鳥?頭がモヒカンだからね

シンプレックス法

f:id:niwatako:20170916104045j:plain

x1, x2に非負条件が無いとシンプレックス法は使えない

f:id:niwatako:20170916104124j:plain

f:id:niwatako:20170916104146j:plain

f:id:niwatako:20170916104203j:plain

x1を大きくしていくとZはまだ小さくなれる

f:id:niwatako:20170916104226j:plain

f:id:niwatako:20170916104306j:plain

全体を3で割ってx1を全部消す、ガウス消去法。

f:id:niwatako:20170916104334j:plain

x2を増やすとまだzを小さく出来る

最終的に

f:id:niwatako:20170916104353j:plain

これが最適値。

f:id:niwatako:20170916104412j:plain

どのようにこのステップを通してグラフ的に変わっていっているか

f:id:niwatako:20170916104438j:plain

f:id:niwatako:20170916104442j:plain

シンプレックス法は外側の辺をなぞっていく

f:id:niwatako:20170916104459j:plain

f:id:niwatako:20170916104505j:plain

f:id:niwatako:20170916104527j:plain

初回で解が求まらないなら

f:id:niwatako:20170916104551j:plain

f:id:niwatako:20170916104615j:plain

更に効率的な方法を求めるなら

f:id:niwatako:20170916104635j:plain

f:id:niwatako:20170916104638j:plain

f:id:niwatako:20170916104648j:plain

f:id:niwatako:20170916104658j:plain

f:id:niwatako:20170916104712j:plain

シンプレックス法を理解したところで本題

f:id:niwatako:20170916104746j:plain

f:id:niwatako:20170916104810j:plain

f:id:niwatako:20170916104824j:plain

f:id:niwatako:20170916104918j:plain

f:id:niwatako:20170916104945j:plain

結果

f:id:niwatako:20170916105001j:plain

f:id:niwatako:20170916105005j:plain

f:id:niwatako:20170916105052j:plain

難しいパターン

f:id:niwatako:20170916105113j:plain

f:id:niwatako:20170916105152j:plain

今まで目的関数について余り考えてこなかったが、目的関数を作って重みを付けている。

こんな感じで解ける

f:id:niwatako:20170916105238j:plain

小刻みにサイズが変わる時

f:id:niwatako:20170916105303j:plain

f:id:niwatako:20170916105325j:plain

f:id:niwatako:20170916105358j:plain

f:id:niwatako:20170916105402j:plain

わかった人もわからなかった人もいると思うが、足し算掛け算、簡単な行列操作のみです。

f:id:niwatako:20170916105410j:plain

f:id:niwatako:20170916105441j:plain

f:id:niwatako:20170916105530j:plain

2属性以上をやりだすと人類に早すぎるというAppleのメッセージなのかな

One more thing …

f:id:niwatako:20170916105554j:plain

OSS化しているのでぜひみて見て下さい。コード読まなくてもぜひスターを押していただいて

(会場笑い)

f:id:niwatako:20170916105626j:plain

QA

  • Cassowaryや双対シンプレクス法を使っているというのはどこがソースですか
    • ハッカーニュースでもとAppleのエンジニアだよという人がいて、それらしい話をしていたとか一般に総噂されているとかこれらが確立してからAutolayoutが登場したという背景
  • Cassowaryを知っていて役立ったことはありますか
    • 作ったばかりでまだわからないですね。AutoLayoutという便利なものがあるんでそれを使えばいいと思います。趣味で作ったので業務で使おうと思わないほうがいいです
  • UIKitではできない等間隔配置、Cassowaryでは簡単にできるのですか
    • 簡単ですが、まぁ、仕事では使わないようにして下さい
  • 今までの話を聞いて思ったのが、AutoLayoutの計算量が多いのではないかと思ったのですが、実際AutoLayoutでここまで計算して制約をつけるメリットは有るのでしょうか
    • すごい遠い子孫関係のレイアウトまで指定できる。とはいえ人間の理解を超える部分もある、難しいと言われるのはそういうところにゆえんがあると思っている。
  • いまGithubにあるCassowaryにスターを付けました。聴きたいのですが、このフレームワークはNSLayoutConstraintに依存していますか
    • 全く依存していません
      • AutoLayoutのフラグは不要?
        • 不要です。Linuxでやろうと思えばそちらに応用もできる。UIKitに乗らずにやることもできます。

節子、それViewControllerやない...、FatViewControllerや...。 | iOSDC 2017 前夜祭

UIViewController(以下UIVC, VC)クラスにおいてよく起こりがちな問題があります。iOS開発者であれば一度は耳にしたことがあるでしょう、「VCの肥大化問題」です。VCの肥大化の原因は、UIVCクラスの責務過多にあります。 このトークでは、UIVCクラスの責務を見つめ直し、「VCの肥大化問題」に対して、設計の観点からどのように解消できるかを紹介します。

扱う内容(仮) ・UIVCの責務を振り返る ・よくある解決策: MVP ・責務分けにチャレンジ!

スピーカーの方の登壇についての記事・スライド

dev.classmethod.jp

節子、それViewControllerやない…、FatViewControllerや…。

f:id:niwatako:20170915190802j:plain

f:id:niwatako:20170915190822j:plain

akibaの方から来ました。最近はNode.js書いたりしています。ダンボールのアカウントです。

概要

f:id:niwatako:20170915190857j:plain

責務を分けると→どんないいことがあるか 責務を分けるためにMVPを紹介

f:id:niwatako:20170915190922j:plain

f:id:niwatako:20170915190932j:plain

ViewControllerが重要なので、その近くにロジックがあると便利??

f:id:niwatako:20170915190951j:plain

f:id:niwatako:20170915191004j:plain

f:id:niwatako:20170915191023j:plain

f:id:niwatako:20170915191031j:plain

f:id:niwatako:20170915191040j:plain

ライフサイクルイベントに組み込まれた通信処理や、複雑な条件分岐。

どうやってテストを書けばいいんだろう

f:id:niwatako:20170915191108j:plain

Fatとは、行数が多いことが問題なのではありません。責務が多くなっている状態のことです。

f:id:niwatako:20170915191140j:plain

Fatだと。。。デグレやバグを発生させる、発見を難しくするetc

f:id:niwatako:20170915191206j:plain

テスト・メンテナンスしやすいとは?

f:id:niwatako:20170915191227j:plain

f:id:niwatako:20170915191236j:plain

責務が適切に分割、依存関係が少ない、副作用が少ないなどが良い状態

無駄な実装重複がない、バグが起きない、発見が早くなる

f:id:niwatako:20170915191310j:plain

ロジックの発火がライフサイクルやユーザーイベントからのみ発生すると

f:id:niwatako:20170915191350j:plain

f:id:niwatako:20170915191355j:plain

テストを書かなかったとしてもバグを見つけやすく・起きにくく出来る

f:id:niwatako:20170915191401j:plain

足場づくりが大事

f:id:niwatako:20170915191425j:plain

f:id:niwatako:20170915191434j:plain

ViewControllerの話しに戻ります

f:id:niwatako:20170915191452j:plain

VCがFatになるのはフレームワークとして重要な役割を担っているゆえ

f:id:niwatako:20170915191529j:plain

そこできょうはMVPで解決します

f:id:niwatako:20170915191538j:plain

f:id:niwatako:20170915191555j:plain

Pの役割は、ビジネスロジックとビューの処理を疎結合化する。

冒頭の例、白枠を切り出します

f:id:niwatako:20170915191622j:plain

f:id:niwatako:20170915191648j:plain

3つの役割の説明をコードをみてします

f:id:niwatako:20170915191701j:plain

f:id:niwatako:20170915191729j:plain

↑ViewController側でDatamodelを持たないことにしているのが大事

DataModelをViewControllerが持つと、扱いや加工をViewControllerがしなければならなくなる。

f:id:niwatako:20170915191809j:plain

↑Presenterの指示でUIを更新する処理

f:id:niwatako:20170915191828j:plain

プレゼンターから受け付けるインターフェースを定義

f:id:niwatako:20170915191844j:plain

プレゼンターは実際に通信して取ってくる処理を持つ

f:id:niwatako:20170915191911j:plain

プロトコルに定義した処理を呼び出す

f:id:niwatako:20170915191930j:plain

f:id:niwatako:20170915191937j:plain

MVPで責務を切り分けることができました。でもしれが必ずしも正義でしょうか。

f:id:niwatako:20170915191957j:plain

Viper、クリーンアーキテクチャなどもあります。やりすぎると開発コストになります。アプリの開発は色んなパラメータがあります。納期、成長度合い、予算、習熟度など。ナマモノです。設計は使い分けなければならないものだと思っています。

プレゼンターを切り出すところまでは、そんなに重くなく出来ると思っています。

f:id:niwatako:20170915192113j:plain

責務分けについて最近読んだものではこの記事が面白かったです。

オブジェクト指向の復習や、そこにプロトコルが入ってきたらどうするかなどが面白いと思います。

まとめです。

f:id:niwatako:20170915192149j:plain

責務を分けることが正義なのかという話でも取り上げましたが分ければいいという問題ではない。アプリやチームの状況に合わせて適切に使い分けを。それが、設計に答えはないと言われるところかと思います。

明日以降、の設計関係のセッションです。

f:id:niwatako:20170915192256j:plain

他にもありますか?? ぜひ見てみて下さい。

参考です

f:id:niwatako:20170915192326j:plain

ありがとうございました。

f:id:niwatako:20170915192336j:plain

QA

  • MVPってアプリを組む時によく使われるライブラリなどでおすすめのものはありますか?
    • MVPはアーキテクチャなので、どのライブラリということはそんなに無くて、作っていく中で構造を変えるという感じです。使うライブラリは普段のものと変わらずにできると思います。
  • どうも節子です。プレゼンターを切り出す時にどのようにしているか参考にお伺いしたい。ビジネスロジックをViewControllerにいれてしまって、あとで切り出すのか、最初にプロトコルをきちんと決めるのが良いのか
    • 最初大きなVCがあるとしたら、そこから切り分けるのでも良い。なれてくると最初から分けて考えても良いと思う。プレゼンター川に切り出すといった時に、できるだけViewControllerにごちゃごちゃ書かないというポリシーでやっていて、ロジックを書かないし、データを持ちたくなったら、持たずにプレゼンターに問い合わせるというようにしています。データをAPIから取ってくるというのは、ビジネスロジックとしてわかりやすいので、そういったところから抜き出したりすると良いと思います。

Swaggerで始めるAPI定義管理とコードジェネレート | iOSDC 2017 前夜祭

twitter.com

REST APIインターフェイス仕様をSwaggerのAPIドキュメントで一元的に定義・管理することで、モバイルとサーバエンジニア間のコミュニケーションを齟齬なく円滑に進めることができます。

さらに、SwaggerのSwiftコードのジェネレート機能を活用することで、工数の削減と品質の向上を図り生産性を高める方法をご紹介します。

Swaggerで始めるAPI定義管理とコードジェネレート

f:id:niwatako:20170915184547j:plain

f:id:niwatako:20170915184614j:plain

4 月からフリーランスです。

f:id:niwatako:20170915184635j:plain

サーバーの方とコラボレーションしながら開発していると思いますが、思っているものと違うものが入ってくることがあると思う。

齟齬をなくして、円滑にすすめるために重要かと思う。値の方がJSONからはわからないとか、本番ではNULLがはいってきたとか。そうしたことを定義することで手戻りが少なくなる

f:id:niwatako:20170915184646j:plain

大手が立ち上げた規格化?プロジェクトで採用されている

f:id:niwatako:20170915184758j:plain

このような定義内容があります。

f:id:niwatako:20170915184824j:plain

専用エディタが提供されていてブラウザで動きます。Specファイルからドキュメントをジェネレイトして見られるようにしたり、モックサーバーを作ることができます。

エディタです。

f:id:niwatako:20170915184920j:plain

ローカルでも動きます。右手が、定義が可視化されたものです。

左に定義がSwagger SpecとしてYAMLで書かれています。

Path=EndpointとDefinition=JSONのモデル定義です。

定義を間違えるとすぐ分かる

f:id:niwatako:20170915185016j:plain

Getエンドポイントを定義してみます。

f:id:niwatako:20170915185034j:plain

Arrayだとか、requireで必須だとか定義できる。

レスポンスは200と400で定義して、それぞれどのような内容が返るかを定義できる。

モデル定義。左に定義すると右に可視化されている。モデル定義の入れ子も可能。Int32とInt64を分けることが出来る。Statusのようなものはenumで定義できる。

f:id:niwatako:20170915185159j:plain

exampleを定義しておくと、モックサーバーがその値を返す。何も定義しなければその型のデフォルト値が返る。

f:id:niwatako:20170915185323j:plain

モックサーバーができて、APIサーバーが完成していなくてもクライアントを開発できるし、仕様に齟齬がない。

f:id:niwatako:20170915185359j:plain

コードジェネレート出来る。なぜコードジェネレートしたいか。折角定義したのに人間が実装する時に間違えたら勿体無い

f:id:niwatako:20170915185415j:plain

Swagger Codegen 様々な言語に対応している。Kotlinも5月ぐらいに対応。新しい言語にも積極的に対応している

f:id:niwatako:20170915185505j:plain

f:id:niwatako:20170915185519j:plain

Swiftの場合

f:id:niwatako:20170915185555j:plain

実行方法

f:id:niwatako:20170915185621j:plain

Podのバージョンをインクリメント出来ると便利

どんなオプションが有るかはこちらで確認

f:id:niwatako:20170915185655j:plain

リクエスト部のAPIと、モデル、通信用クラスなどがGenerateされる。

f:id:niwatako:20170915185709j:plain

f:id:niwatako:20170915185739j:plain

f:id:niwatako:20170915185747j:plain

Cadableに準拠したモデルができました。requireでないところはOptionalになります。

f:id:niwatako:20170915185754j:plain

通信部分はSuccessかErrorかのコールバックが基本です。

f:id:niwatako:20170915185829j:plain

リクエスト部分は柔軟にカスタマイズできます

f:id:niwatako:20170915185856j:plain

f:id:niwatako:20170915185916j:plain

f:id:niwatako:20170915185922j:plain

f:id:niwatako:20170915185937j:plain

f:id:niwatako:20170915185941j:plain

f:id:niwatako:20170915185950j:plain

f:id:niwatako:20170915190000j:plain

f:id:niwatako:20170915190012j:plain

MultifileSwaggerというのを作っている人がいる

f:id:niwatako:20170915190039j:plain

f:id:niwatako:20170915190045j:plain

タグを打ってCocoaPodsをタグのバージョンに合わせるように徹底する

f:id:niwatako:20170915190108j:plain

f:id:niwatako:20170915190114j:plain

成熟してきている中で、ノウハウも溜まってきている。開発効率や品質向上がフォーカスされていくのではないか。そうした中でAPI定義やコードジェネレートが一助になれば

QA

  • 既にSwaggerで作られたわけではないサーバーがあるとして、別に定義を用意するという使い方もあるでしょうか
    • 両サイドの認識合わせに定義を明示することは意味があるのではないかと思います。ジェネレートしたコードにリプレースする意味があるか。うまく入れ替えられるなら良いと思う。インターフェースさえうまく導入できそうであれば活用できると思う
  • 定義を書き出したがサーバーが準拠していない場合
    • 曖昧だったところとの不整合が起きてクラッシュが起きる可能性はあるが、それはその曖昧な運用だったということだと思うので、リリースまでに修正できるなら、より品質の高い状態でリリースでき、今後の運用もしやすくなると思います。
  • API以外とかで使えますか、たとえば、定数や型などでやりたい時にAPI以外に、アプリ側と他のサーバーサイドと定義をあわせたい時、たとえば、Push通知の形式とか。運用によると思うが、たとえば、GoogleAnalyticsなどの計測の定義など
    • RESTAPI以外にも活用できる部分はあると思う。Swaggerはエンドポイントとモデルを定義できる。APIで使わないならモデルの部分だけになると思う。そのような使い方は可能だと思います。