#shibuyaswift 第3回Appleにまつわる情報を共有できる勉強会に参加してきたまとめ
shibuya.swift #3に参加してきたのでそのまとめです。
発表もしてたので
Sengiri OS X App
- アニメーションなどのインタラクションチェックを簡単に伝えるためにgifを頑張って作っていた
- licecapを使ったら結構簡単にgifができた
- しかし、もっと踏み込んで、自動でトップウィンドウ短形で録画できるものを作ってみた
- MacOSの勉強をするためにDocumentを読んだ
- 本はCocoa Programming for OSXという本を読んだ
- Screen Captureする方法
- AVCaptureScreenInputとディスプレイの固有IDであるCGDirectDisplayIDを用いる。
- CGDirectDisplayID
- 関数を利用すればMacのディスプレイの情報が取得できる
- CGMainDisplayID()を使えばメインのディスプレイの固有IDを取得できる
- デュアルディスプレイの時は頑張らないといけない
- メニューバーの録画停止
- NSMenuを使って録画ボタンを作った
- Menuをopenした時にNSMenuDelegateが呼び出されるのでstopの処理を入れた
- gifの処理は時間がかかるので、NSMenuItemでインジケーターみたいなのを表示させるようにした
- crop window
- 録画開始前にwindowの移動を許可し、開始後にマウスイベントを透過する
- NSWindowのスタイルの設定を透過色に指定するだけ
- recordボタン
- ボタン押した時にイベントを透過するフラグ(ignoresMouseEvents)をtrueにするだけ
- NSWindowController
- Windowを管理するもの
- NSViewController
- OS10.10で便利に作られるようになった
- UIViewControllerと同じ振る舞いができるようになった
- storyboardやコンテナビューも設定できるようになっている
- GIF変換
- 画像を取り出す作業としてAVAssetImageGeneratorを使う
- GIFを作るのでCGImageを使う
- AVAssetで動画を作り、Generatorに渡す
- RegiftというiOSのものがあったがPRしてOSXでも使えるものを作った
- もっとシンプルにするために
- ショートカットキーを設定した
- QuartzWindowServicesRefereceを用いてMainScreenからWindow情報を取得する
- sengiriの由来は画像がいっぱい切り出せるから
MVCで辛いこと
- 複雑な画面のアプリだとMVCではつらくないかという話とMVP使えばうまくいくんじゃないかという話
複雑な画面のアプリで辛いこと
- 複数のコンポーネントが配置してあり、複数のTableViewやCOllectionViewを使っている
- コードレビューが辛い
- viewcontrollerの行数が多くなると、確認と修正に時間がかかる
- テストが辛い
- 複雑なViewControllerのUnitテストは辛い
- →ViewとViewControllerは絡み合っていることが多い
- 複雑な実装と複雑なテストはセットで、保守が難しい
- 複雑なViewControllerのUnitテストは辛い
- UIテストの辛さ
- テストを書くのと、実行時間と保守に時間がかかる
- 壊れやすい
- つらい原因
- ViewControllerに役割、責務が集中しているので、役割、関心事で分割すればいい→MVPを使おう!
MVPっぽくする
- Model-View-Presenterの略
- ObserverSynchronizationと、Passive Viewの二通りあるが、今回は従来のMVCと親和性が高く、Viewを薄くできるのでPassiveViewを採用した
- 基本的な違いは、ViewからはModelを直接触らない、View<=>Presenterはメソッドを用意して触るというだけ
- メリット
- Viewが複雑になった時、MVCだとViewControllerが全部集約する形になる
- MVPだとviewごとにPresenterがを対応する形になるので関心事が小さくなり、役割がはっきりする
- Presenterは使いまわしやすいので重複実装が減る
- なぜMVPを進めたか
- MVVMやDDDも検討したが、登場人物が多かったり、文化を浸透させるのが大変だった
- DataBindingは便利だが、今解決したいことはViewControllerを減らしたいことだったので今回は対象外
- 他のMVPのメリット
- MVCからの第一歩に最適
- MVVMやDDDへ発展できる
実装方法
- サンプル
- TableのViewControllerには何も書いておらず、delegateだけ書いているPresenterがあり処理している
- テキストの部分はsetTextをしたらtextが出るようにpresenterを作っている
- collectionviewの部分はcollectionviewのdelegateをpresenterに書いてあるだけになる
- セルはtableviewと同じように、imageを渡されたら自身にセットするだけ
まとめ
- ViewControllerが複雑になって大変になったら分割してみるとわりといいかも
- MVPだと最低限の変更でできる
NullabilityとLightweightGenericsで使いやすく
swift移行の過渡期
- swift公開からそろそろ2年たち、既存のobjctive-cのコードをどうやって載せ替えていくか悩んでいるはず
- objective-cで書かれたプロジェクトをswiftに書き換える方法については、iOSレガシーコード改善ガイド参照
- 延命パターン
- obj-cのコードをswiftから使いやすくする
- swiftからobj-cのコードにアクセスしやすくする
- Nullability, lightweight genericsで既存のobj-cのコードに追加する
Nullability
- nullable: nilを許容する
- nonnull: nilを許容しない
- null_resettable: 初期設定時のみnilを許容する
Lightweight Generics
- NSArrayやNSDictionaryに
クラス名<型 *> *
のように型引数を指定する
UIViewをみてみた
- NS_ASSUME_NONNULL_BEGINとNS_ASSUME_NONNULL_ENDで囲むといちいちnonnullを書かなくていい
- subviewsはnullableではない
- swiftでどう見えるかの確認は、Xcodeにある、Generated Interfaceを使うと確認できる
NullabilityとGenericsを指定してサンプルを書いてみた
- 書き換える流れはoptionall→nonnull指定→Generics設定
- optionalにする
- obj-cでポインタ型ならnullableを指定しておく
- 一つでもnonnullとかつけると全てに指定しろとwarningでるが、Genericsでは出ません
- ちゃんとnonnullも指定する
- objective-cのコードを調べてnonnullになるかを調べないといけない
- nonnullを指定するとnilについて考慮すべきかどうかインターフェースから判断できる
- 大きいファイルだと確認が大変
- Genericsの設定
- AnyObjectを使うと、キャストしないといけないが、Genericsを使うと型をうまく指定できる
- Objective-cの実装でnsarrayやnsdictionaryに入っている場合は片付け可能な形にリファクタリングする
- nonnullな文字列を返すheyメソッドでobj-cで間違えてnilをreturnしたらどうなるか?
- swiftでnilを返すようなラッパーメソッドを書いてみたら、Bridgeする際に解決しているぽく、空文字が入ってきた
まとめ
- swiftからobj-cのコードを使う際に扱いやすくなる
- generate interfaceでインターフェースを確認できる
- nullabilityやLightweight Genericsを指定することでswiftから利用しやすくなる
- obj-cのコードをしっかりと理解した上で指定しましょう
- 詳しくは: NullabilityとGenericsでObjective-CのコードをSwiftから使いやすくする
Quickはいいぞ
Quickの概要
- Quickはswift, obj-cのためのBDDテストフレームワーク
- NimbleというQuickが使っているmatcherを使うことで、期待値と実際値を明確に表せる
XCTestとQuickの比較
- XCTestと違って振る舞いで書ける
- XCTestだと、引数が3つだが、Quickは2つだけでいい
- Quickはテンプレートでかけるが、カスタマイズも出来ます
- XCTestが失敗した時はシンプルに十分なエラー情報がでます
Nimbleでできることを学ぼう
- 非同期のテストがシンプルに出来ます
- matcher関数
- .to(equal(expected))を==で置き換えて書くことができたりする
- エラーハンドリングのテストもできる
- CollectionMembershipを使うと配列の中の要素チェックもできる
- Stringsに対しても文字列を含むか、始まるか、終わるか、空かなども使える
- 配列の要素に対して、すべての要素がNより小さいかなどのチェックもできる
- to(satisfyAnyOf(beLessThan(10), beGreaterThan(20)) どっちかの要素が満たしているかということもできる
Quickのイイトコロ
- エラーメッセージを書かなくてもいい
- Nimbleで柔軟なAssertがかける
- describeやcontextを使ってテストのまとまりが分かりやすい
It’s easy to use swift JSON decoder:UNBOX
- UNBOX
- JSONからデータを取ってくるときはいろんなアプローチがあるが、UNBOXはモデルにマッピングするタイプです
- エラーハンドリングにも対応している
- NSStringで来たものをNSURLに変換するようなtransformationsにも対応している
- NSURLに変換するのは内部で対応しています
- それ以外はUnboxbleByTransformという型に対応していればできる
- ObjectMapperに似ている!が、違うところがあるとすると、、、
- ENUMサポートしている
- ある、JSONのデータをデコードしている最中に、無関係のデコードをするようなContextual Objectsにサポートしている
- →UnboxableWithContextに準拠していればできます
- realmとも連携ができます
それでもObjective-Cとともにやっていく話
- objective-cのコードをswiftから型安全に使う話
- Clientの挙動をハンドリングしたAPIClientがあった
- APIのパスはStringで都度渡し、レスポンスはもちろんAnyObjectが入ったDictionary
- APIClientの問題点
- あらゆるところからcallされており、dictionaryに渡すパラメータで行っている
- 認証処理やTokenの保持なども実装されている
- 邪悪なインターフェイスで処理を呼び出すコードが今後も増えると、swiftの良さを生かせない
- →swiftで型安全に使えるwrapperクラスを使おう!
- swiftらしいAPIClientのインターフェースは石川さんの記事を参照
- APIKitと同様なインターフェイスで、レガシーなApiClientを呼び出すラッパーを作ることにした
- RequestTypeプロトコルに適合したRequest型を引数として受け取る
- Genericsを利用してRequestType型でかえし、Eitherで処理する。
- このクラスを使うと、Requestを作成して、渡してあげると、成功したらentityになった変数を受け取ることができるし、失敗したら失敗の変数を受け取れる
- うまくラップすることができれば型安全に使えることができるようになる
まとめ
- 型という概念がないobj-cのコードもwrapして安全に使える
- 昔のコードはwrapperナシでは触れないものとして扱う
- 今までと違うものがあった時だけ通して、その結果からさらにリクエストを叩き、メインスレッドを書くといったこともできるコードも書いてみました
パンが無ければお菓子を食べ続けることができるのか Swiftとマリー・アント○ネットを用いて検証してみる
- ゲームを作って、歴史上の人物をいじったのを作ってみたかったので、マリーアント○ネットのアプリを作ろうと思った
- SpriteKitの所感
- サーバーサイドエンジニア向き
- 当たり判定、パーティクル作るのがゆとり向け
- しかし、モック画像の用意や配置などに苦労する
- Reference:demo bots
【watchOS2】Timetravelを実装したComplicationをタップした時の情報を取得する
小生の発表です。
詳細は#shibuyaswift #3 でwatchOS2のTimetravelのタップした情報取得について発表してきた
継承関係の親子クラスとprotocol extensionについて
- ProtocolExtensionはプロトコルにデフォルトの実装をつけられるもの
- 親子関係にあるクラスとProcotolExtensionでは、whereを使うとその時だけ使えるprotocolというのができる
- genericsを使って型情報を持たせるとデフォルト実装が採用される
- AとBのpersonがライブラリの中にあって触りたくないとか、UITableViewCellから持ってきたらclassの生成段階でgenerics情報を与えられないのでgenericsを適用できないといったことがある
- 初期化する時に、型をみて代入するようにする
- 型情報を制限できるといい
@mogmetの所感
protocol extensionやgenericsの話などがちょいちょいと出てきていますが、swiftで作る際にうまく取り入れることが全然できていないので、リファクタリングする時などに意識してみて書けるパターンを増やせるようになっておきたいなと思いました。
また、@kazu0620様の最初の15分Talkでやっていた@mo_to_44様のネタをパクって発表されていたのがだいぶ印象的でした。
場の雰囲気に合わせてリアルタイムに資料を変えて作っていくのも一つのテクニックで勉強になりますね!