日経電子版の iOS アプリ(電子版アプリ)は、昨年の 11/5 に、バージョン 4.0.0 をリニューアルリリースした。メジャーアップデートされたこのバージョンでは、前のバージョンに比べて、内部的に大きな変更が行われた。ほとんど Objective-C で書かれた既存のコードから脱却し、Swift 4.0 を用い新しい設計方針のもとで全面的な刷新が行われたのである。
Swift
Objective-C、Swift とは、Apple が主導して開発するプログラミング言語だ。Objective-C は、古くは NeXT に搭載され、主に Apple 製品のソフトウェアの開発言語として使われてきた。Swift は、Objective-C の後に登場した新しいプログラミング言語である。Swift は、現在のところ、主に iOS, macOS に関するソフトウェアでしか使われていないが、Github 上で開発されている OSS であり、Linux でも実行可能だ。後発の言語であるから、既存のプログラミング言語の美味しいところを上手く取り入れており書きやすい。たとえば、Objective-C から取り入れた名前付き引数によって、読みやすい API を実現している。
また、型推論により冗長ではないコードを書くことができる。行末にセミコロンを置く必要もなく、条件式をカッコで括る必要もない。Generics を使用でき、Struct や Enum はメソッドを持てる。map や filter などの関数型プログラミングに使えるメソッドも備える。Optional 型を持ち、nil 安全に書ける。ネイティブで try か catch などの例外処理構文をサポートするほか、多くのケースで Objective-C よりも高速であるというのも嬉しいポイントだ。
電子版アプリは、リニューアル前、ほぼ Objective-C で書かれていた。Swift 3 が登場したあと、新しい画面が必要な場面では、部分的に Swift を利用することもあった。Swift の Struct や Generics などを Objective-C から利用できないなどの制限により、共存することはできても前述したような Swift の良さを完全に活かしきることができていなかった。
Model や Manager など、複数画面にまたがることがあるクラスでは、Objective-C を意識し、制限された Swift を書く必要があった。Swift から Objective-C で書かれたコードを利用するために、Bridging Header に載せるヘッダファイルも増え、少しの変更でクリーンビルドが必要になることも多くなっていった。

電子版アプリ 4.0.0 の開発が始まった当初、Swift 4 はまだ Beta であった。電子版アプリ 4.0.0 をリリースする頃には、Swift 4 は正式版が公開されているという予想と、仮に公開されていなくても当時リリースされていた Swift 3.2 に書き換えることは可能だろうという判断で、Xcode 9 Beta と Swift 4 Beta を使うこととした。
設計
リニューアル前の電子版アプリでは、伝統的な MVC モデルを採用していた。学習コストは低かったものの、理解や意見の違いなどから人によってコードの書き方が変わってしまうという問題があった。例えば Manager を、ときに View から、ときに View Controller から使用していることなどである。ここでいう Manager とは、主にサーバー側 API とアプリ内で使用する Model をつなぐような役割をするクラスだ。
こういった反省を踏まえて、4.0.0 の開発では、MVC よりも各役割を細かく具体化し、Functional Reactive Programming (FRP)を取り入れた MVVM を採用することとした。画面遷移を管理する Router、また MVVM の各レイヤー間を疎結合にするための Dependency Injection (DI)も合わせて利用する。

URL Scheme や Universal Links を使った画面表示を想定して、アプリの画面は可能な限り疎結合にする必要がある。主に画面ごとに分割して、たとえば、「Web 刊一覧画面」、「記事詳細画面」、「ログイン画面」などといった単位で、モジュールを作成するようにした。 Modules というディレクトリの中に、各モジュールを配置する。各モジュールは、原則として View-ViewModel-Model, Builder, Router の機能を一通り持つ。疎結合、メンテナンス性を両輪で維持するために、若干の冗長性は許容する。ただ、多くのモジュールで共通して利用する View、ViewModel、Model は、Common ディレクトリの中に配置する。
ここまでをまとめると、ディレクトリは以下のような構造となる。
Nikkei/Classes/
├── Common
│ ├── Application # AppDelegate
│ ├── Extension # Extensions
│ ├── Model
│ │ ├── Entity # Real Data from Realm or Network
│ │ ├── Logic # Business Logic (Fetching Data and processing Data)
│ │ ├── Realm
│ │ └── Network
│ ├── Rx # Rx Extensions
│ ├── Util
│ ├── View # Common View
│ │ ├── ArticleTableViewCell
│ │ └── PullToRefresh
│ └── ViewModel # Common ViewModel
└── Modules
├── Root
│ └── Router
├── Article
│ ├── Builder
│ ├── Model
│ ├── Router
│ ├── View
│ └── ViewModel
└── WebEdition
├── Builder
├── Model
├── Router
├── View
└── ViewModel
モジュールは、Generambdaにより、テンプレート化しておく。予め、ある程度の実装を、Model、View などのファイルに用意しておくことで、各レイヤーの役割が明確になり、開発者が行うコピペの手間を削減できる。
Generambda を利用したテンプレートの利用は、以下のコマンドで行う。
generamba gen WebEdition frp
これにより、図 2 の WebEdition ディレクトリ以下が作成される。xcodeproj にもファイルが登録されるので、Xcode でファイルを開いてすぐにコーディングを始めることができる。
CI と管理ツール
旧アプリでは、サードパーティーのライブラリ管理には CocoaPods を利用していたが、クリーンビルドが遅くなるため、新バージョンの開発では、Carthage を利用することとした。Carthage では、利用するライブラリをビルドされた framework として事前に用意しておくため、アプリのビルド時間短縮に貢献する。Cartfile に新しい framework の依存情報を追加して、github に push すると、Bitrise CI 上の Carthage によりビルドされ S3 上にキャッシュされる。こうして、開発者の各端末では、都度ビルドを行うことなく、S3 からビルド済みの framework をダウンロードして使用することができる。
開発サイクル
現在我々のチームでは、大体2週間ごとに1回のアップデートを目指して、開発を進めている。2週間後にリリースする内容を大まかに決めて、master ブランチから次にリリースするバージョンのためのブランチを切る (develop-4.0.0)。そのブランチから、各機能追加、修正のためのブランチ (feature-XXX, YYY) を切り、バージョンのブランチ (develop-4.0.0) にマージをする。

開発者は手動で、任意の時点のアプリを Fabric Beta, Bitrise, iTunes Connect を通じてベータ配信することができるが、バージョンのブランチにマージをした段階で、CI によりビルドされたベータ版が、社内のテスターに配布される。機能ごとのブランチがコードレビューを経てマージされるごとに、バージョンのブランチを使って QA を行う。バグがあった場合、それを修正するためのブランチを切る。
どうなったか
我々のアプリでは、Swift の ! (強制的な nil のアンラップ) を極力使わずに、nil 安全に書いていくことを心がけた。結果として、かつてのアプリよりも極めて落ちにくいアプリになったことは、Swift 導入により分かりやすく表れた成果の一つだ。Crashlytics によると、クラッシュフリー率は 100% をマークしている。また、本稿で取り上げた設計は、スタートの時点で全て確定していたものではなく、試行錯誤の末、ときに全てを書き直しながら練っていったものだ。MVVM やテンプレートの利用によって、その時々で設計に悩む機会が減ったし、FRP (RxSwift) によって、状態を意識する手間を大幅に省けるようになった。
Xcode 9 の Beta、Swift 4 の Beta で進めてきた開発プロジェクトであったが、リリースの 2 週間ほど前に、無事、正式版の Xcode 9, Swift 4 がリリースされた。Xcode 9 の Beta では、度重なるエディタのクラッシュや、Beta のバージョンが進むにつれて、必ずしも安定性が高まらなかったり、謎のエラーでアプリがビルドできなくなるバグに苦しまされることもあった。リリースされた正式版の Xcode 9 では、問題なくアプリをビルドすることができ、半年間かけて作ってきたアプリも、無事に提出し、ついに App Store から日の目を見ることとなった。
1から書き直したアプリで、今後は動作高速化や、ヌルヌル化といった UX の改善はもちろん、多様なコンテンツの見せ方を検討し、ユーザーに更なる価値を届けていきたい。