Swift を使い始めて2ヶ月弱の雑魚ですこんにちは。Swift やるぞーってなった時には try! Swift の申し込みは終わってました。悲しかったです。とても。
で、わけあって Swift で RSS リーダのアプリを作りましたので諸々雑多なことを含めて紹介します。
やはり最初はオプショナルが思ったように扱えなくてハマりました。ま、でもこれはすぐ慣れるので地道に ?! していきましょう。しかしいまでも慣れないのがジェネリクスです。こいつは使いこなすのが難しそう。新しい概念を習得するのは大変だ!
Swift やってみての感想は、スッキリしててわかりやすいですね。Objective-C と比べて余計なことを書かなくていいように感じます。型の束縛が不自由に感じる場面もあるのですが、たぶんここはもっと慣れると良い面になるんじゃないかと期待しています。
あとはなんだ、class を使わずに struct を使ったほうがいいとか enum との使い分けとか悩むというかよくわからん。なんかやり方が色々あるような、そんな感触がしていてどういう道を歩いてけばいいのかまだまだ見えるのに時間がかかりそうな印象です。ファー〜。
勉強方法
読んだ書籍は 詳解Swift オンリーです。ひと通りのことが網羅されているので、これだけで足りるんじゃないかと。書籍版を購入したすぐあとに Kindle 版がでたので両方買ったんですが、結局ほとんど Kindle 版でしか読まなかったな。移動中とか空き時間にひたすら読み進めることができたので電子版は最高ですね。むしろ電子版なかったらやばかったかも…。
詳解Swift には UIKit の解説はないからアプリを作るならまた別の書籍なりで勉強しないといけないと思うけど、自分は Objective-C 歴が長かったから特に問題なし。
で、わかんないことは随時ぐぐったりしながらと…。
以下使わせてもらったツールとかライブラリの紹介です。
SwiftLint
Realm が提供している SwiftLint っていうコーディング規約をチェックしてくれるコマンドラインツールがあって、それを使わせてもらいました。GitHub の Swift コーディング規約 に沿ってチェックをしてくれるらしい。
たとえば Force Cast (as!) とかやってると怒られるので、こういうのは避けるような意識づけができてきました。まだまだだけどね。
SwiftTask
登録されているフィードから最新エントリを取得してくる場合、通信の非同期処理が発生するのでその辺をラッピングしてもらうために Promise 的なやつを使いたくて調べたら SwiftTask がいいらしいよーってことだったのでこちらを使ってみました。
ジェネリクスを活用しまくってて Task<Progress, Value, Error>
とかそのまま書いても動かないしほんと意味わかんなくてなかなか思うようにいかなくて苦労しました。
RSS リーダーの場合、タスクを作るのはおもに FeedManager クラス で実装しています。
Realm
データの永続化には Realm を使っています。Feed クラス と Entry クラス が Realm の Object クラスを継承しています。Feed クラスから Entry クラスに1対多の関係を付けました。リレーションする場合はまだ自分で書かないといけない部分があるみたいだけど、このくらいの軽い使い方なら特にハマらず実装できました。
CoreData との比較
CoreData だと GUI でデータモデルの定義を用意しておくとか、データ取得するために何やら沢山書いたりとか、そもそも使いはじめるのにある程度気構えが必要なんだけど、Realm の場合はさっと使い始められてしかもパフォーマンスがいいらしくて、やりたいことに集中できるのがいいですね。
もう一個、CoreData には NSFetchedResultsController っていうなかなか便利な仲間がいるんだけど、後述する RealmResultsController で十分代替できそう。
あとは iCloud の同期すかねえ。CoreData + iCloud ってなかなかバギーな印象があってもうイイヤってなったんだけども…。そこを Realm がなんとかしてくれたら相当インパクトあるね。ないと思うけど。
RealmResultsController
Realm で NSFetchedResultsController したい人のためのやつです!
データが追加された時や削除された時に delegate メソッドを呼び出してくれるので、表示されてる内容の反映に気を回さなくてよくなります。テーブルのアニメーションなんかは自分で実装する必要があるのですが、RSS リーダーアプリでは RealmResultsControllerDefaultDelegate としてプロトコル拡張にまとめて使い回しできるようにしました。
Realm にデータを追加するときに、realm.add(...)
ではなく realm.addNotify(...)
といった具合に Notify をつけた(RealmResultsController が用意してる)メソッドを使わないといけない部分がややクセがあるなと思いました。
Mockingjay
ネットワークまわりのテストで使うモックライブラリです。FeedManagerTests で使ってます。
実際に非同期のテストを実行する際に、stub で登録したローカルファイルにアクセスしてる様子がなくてだいぶハマりました。結論からいうと非同期テスト自体の書き方に問題があったので Swift とかあんま関係ないんですが…。
func testSome() {
let expectation = self.expectationWithDescription("description")
{ // 非同期処理をするブロック
// なんか非同期の処理をいろいろ
expectation.fulfill() // ← 終わったら実行
}
self.waitForExpectationsWithTimeout(5, handler: nil)
}
上のような感じで書くわけですが、最後の waitForExpectationsWithTimeout
を忘れると登録してある stub がクリアされてしまうものの、非同期処理自体はキャンセルされずに実行されていたのでなかなか原因に気付かずそれはもう盛大にハマりました。このアプリ作ってる中で一番ハマったのここかも。
最後に Vim
相変わらず Vim で開発してるわけなんですがね…。このまえ ATOM 触ったらいい感じだったので今年中に移行したいなという気持ちになってきてるんですが(Xcodeじゃないのか)。
プラグインまわりは以下を使わせてもらいました。
- swift.vim シンタックスハイライト・インデント用
- syntastic-swiftlint.vim ファイル保存時に SwiftLint 実行
以上