はじめに
最近、ゼロスクラッチからiOSアプリの設計・開発をする機会がありまして、その構成を色々妄想しております
そこで、ゼロからスクラッチでiOSアプリ開発をやるとしたら、どういう構成やツールを使うかなー、というのを妄想していて、自分なりの考えをまとめてみました。
誰向けの記事?
- アプリ開発の勉強を一通り終えたんだけど、実際にアプリをゼロから作る場合にどういう作りにすればいいか悩んでいる初心者の人
- ゼロから新しいアプリの設計を検討している人
- 既存アプリの構成を変更したい人
アーキテクチャ
アーキテクチャ選定については、アプリの開発規模にもかなり依存すると思います。
規模が小さく、開発者の人数も少ないアプリなら、
- MVC(Model - View - Controller)
- MVP(Model - View - Presentor)
あたりが候補になるでしょう。
多少大きくなってくるのであれば
- VIPER
- Clean Architecture
とかが候補になってきます。
これら全部扱ったことが、個人でスピード重視ならMVC、チームならVIPERが好みです。
VIPERについては以下の記事がとても参考になります。
【参考】iOS Project Architecture : Using VIPER [和訳]
ただ、VIPERはファイル数が多いので、大した機能がないアプリの場合、ファイルを行ったり来たりするのが超絶めんどくさくなる可能性が高いです。
以下のようなVIPERを使うべきかどうかのフローチャートがあったので、参考までに。
【参考】#8 VIPER to be or not to be?
また、MVVM(Model - View - ViewModel)を採用しているプロジェクトも多いかと思います。
Modelのデータ変更が多く、それをViewに反映させるようなシーンが多いアプリであれば、MVVMも選択しても良いかと思いますが、個人的には、MVVMを採用するのであればほぼ利用するであろうライブラリである…
RxSwiftの学習コストが大きい!
なので、開発メンバーの経験の有無によって、選択するかどうかは慎重になります。
私はそう行った理由で、あまり積極的にはMVVMは採用できていないです。
外部パッケージ管理ツール
基本的に、Swift Package Manager
と Cocoapods
のどちらかでいいかと思います。
(Carthage
派の人すみません🙇♂️)
Swift Package Manager
2019年に出て、歴史が浅いため、サポートしていないパッケージも多いというデメリットがありますが、利用したいパッケージがSwift Package Managerをサポートしていれば、そちらを使うのがベターです。
理由は、
- Xcodeに標準で付いてくるので、CocoapodsのようにRuby環境を設ける必要がない。
これくらいでしょうか。
どちらかというと、Cocoapodsに疲弊して消去法的に利用してる感はあります笑。。。
Cocoapods
私が知る限り、Cocoapodsをサポートしていないパッケージは見たことがないので、SwiftPMをサポートしていない場合は、Cocoapodsになるかと思います。
デメリットとしては、
- ビルド時にソースコードビルドが走るので遅い
- XCodeのバージョンに依存するので、XCodeをアップデートするとちょいちょいトラブル
特に後者でトラブったことが何度かありますので、なんとかしたいところ。
コーディングルール
eureka Swift Style Guide
eureka社さんが公開されている、コーディングスタイルガイドが、自分のコーディングスタイルには一番マッチしていました。
チーム内ではこのルールをベースにさせてもらっています。
コード管理
GitHub
ここは GitHub を使った運用で特に問題はないかと思います。
GItHub を使った、Git-Flow ですね。
【参考】Gitを最大限に活用できる「Git flow」で、効率よく開発を進めよう!
Podsディレクトリを管理するか否か
Cocoapods で pod install
を行ってインストールしたライブラリファイルが入っている、Pods/
ディレクトリをGitHubで管理するか否かについて。
私は、管理しない 方針を採っています。
PodfileやPodfile.lock でバージョンの固定化はできますし、変にコンフリクトしたりすることを避けるためにも、GitHubにはアップロードしないようにしています。
UI実装
Storyboard
Storyboardは多用しており、基本的には、「1画面1Storyboard」 としています。
ただし、使い回しをしない、ポップアップ的な画面をContainerViewを使って実装するような画面感の繋がりが明示的に強い場合は、必ずしも「1画面1Storyboard」ではなく、複数画面を一つのStoryboardに配置することはあります。
ただ、基本的は、Storyboardには複数画面は実装しないです。
理由は
- 複数人での作業をしやすくするため
- セグエがスパゲティ状態にならないようにするため
といったところです。
余談ですが、初心者の方で、一つのStoryboardに全画面を押し込んで、さらにセグエを繋ぎまくった結果、ものすごいスパゲティ状態になったStoryboardを作ってしまっているのをよく見かけます。
あれもメンテナンス性が悪くなるので、私はStoryboardは画面毎に分割するようにしています。
Layout
**AutoLayout で実装できるか?**をまず考えます。
そして、ほぼAutoLayoutで実装できます。
コード上でのレイアウトはほとんど使いません。
また、Constraint の Priority も私は積極的に利用して AutoLayoutを実装しています。
複雑なレイアウトで、コード上での操作が必要な場合は、Constraint を Outlet接続して、コード上から操作するようにしています。
SwiftUI
UIまわりを語るのであれば、SwiftUIについても書いておかなければ、と思います。
ただ、恥ずかしながら、SwiftUIについてはまだあまりキャッチアップできておりません。
すでにいじっている方々のQiita記事などを見る限り、まだまだ機能的に足りていないところもあるようですが、iOSエンジニアであり続ける限りはキャッチアップはしていかないといけないトピックであることは間違いないかと思います。
ただ、「今」新しいプロジェクトでアプリを作り始めるのであれば、SwiftUIは選択肢には入らないかなと思います。
やはり知見がまだまだ少なく、メリットが少なく感じます。
[2022/03更新]
SwiftUIは少し触っていますが、うーむ。。。あんまり好きになれず。
好みの問題ですが、ReactやFlutter、Rx系のフレームワークに慣れている人にはSwiftUIもいいかもしれません。
画像読み込み
KingFisher
サーバーに保存してある画像を読み込んで表示させるツールには、AlamofireImage を最初は利用し、現在は、KingFisher を愛用しています。
どちらも画像をキャッシュしてくれたり最低限の機能はありますので、使い勝手はそれほど変わらないかなという感じ。
Nuke
特にKingFisherに不満はないのですが、Nuke というライブラリも評判が良いようなので、触ってみたい。
画像を非同期取得してセットするくらいならわざわざライブラリ使わずとも自分で書いて済ませて良いのでは?と思うかもしれませんが、キャッシュとか色々細かいところ考慮するとライブラリ使うのが得策だと思っています。キャッシュ周りの制御、ライブラリによって差がある気がしていますが、Nukeはデフォルトであるべき姿の実装になっていると感じています。
【引用】iOSアプリを作るときのおすすめ構成
Asset管理
R.swift
画像や音声といったアセットは、
すべて R.swift を使って利用する
ようにするのが良いかと思います。
他にも、R.swift を使って、カスタムしたTableViewCellの呼び出しを行ったり、画面遷移時に次の画面のViewControllerを取得したりする際にも、R.swift を使います。
理由としては、画像などのアセットへアクセスする際に、通常だと文字列でファイル名を指定するので、たとえファイル名の指定をミスっていてもビルドが通ってしまい、実際に実行して初めてクラッシュしたりしてしまう隠れたバグになってしまいます。
一方、R.swift を使うとビルド時にエラーとなってくれるため、このような隠れたバグを防ぎやすいです。
また、Color情報や多言語対応のリソースもR.swiftで一括管理ができて便利です。
CI
CircleCI + fastlane
CircleCI + fastlane の組み合わせで問題ないかなと思います。
配信周りは、Deploygateを利用しています。
Deploygateの代わりに、fabricも使ってみたいところですが、こちらは使ったことがないのであまりよくわかりません…
また、CircleCI の他にも Bitrise も人気です。
私は両方とも使ったことがありますが、Bitriseの方がUIツールが使いやすかったですが、諸々の事情で CircleCI に変更しましたw
好みで使い分けて良いかと思います。
流れとしては以下のようなイメージ。
1. GitHubにマージ実行
2. CircleCIに通知
3. CircleCIにて、fastlane スクリプト実行
4. fastlaneに記述のスクリプトによって、以下の動作
ビルド実行@CircleCI および Slack通知
5. マージされたブランチによって、
Deploygateへの配信 or App Storeへのアップロード
を分類実行
API Client
REST API Client
少し前までは、
Alamofire (HTTP Requestライブラリ)
+
ObjectMapper (JSONデコーダ)
でAPI Clientは作成していましたが、今からやるなら
Alamofire (HTTP Requestライブラリ)
+
Codable (JSONデコーダ)
でやると思います。
Codableは、ObjectMapperよりも直感的に利用できますし、コードもかなりシンプルに記述することができます。
また、CodableでJSONを扱う際のEntityを自動生成してくれる quicktype というサービスが超絶便利ということをTwitterで知りました。
ちょうど先週にJSONパース部分を実装したばっかりで、なんとタイムリーな!
— ケンタロウ@ソフトウェアエンジニア (@kenny_J_7) August 6, 2019
すごい便利!もっと早く知りたかったなぁ。 https://t.co/3rbkmiYdB3
利用するJSONを入力してやると、自動的にSwiftのコードを生成してくれます。
利用方法も簡単なので、直感的にすぐに利用できるかと思います。
こんな感じで、画面の左側に自分が扱いたいJSONを書き込んでやると、自動的に右側にCodableのEntityコードを吐き出してくれます。
ね、簡単でしょ?
ちなみに、Swift以外にも様々な言語に対応しています。
GraphQL API Client
GraphQLについてはまだ利用したことがないのですが、最近よく耳にしますし、サーバーサイドがRESTではなくGraphQLでAPI対応するかもしれません。
その場合のClient実装には、apollo-ios が選択肢になってきます。
【参考】Swiftでもapollo-iosで快適にGraphQL
ローカルデータ保存
Database (Realm & CoreData)
Databaseとしてのデータ保存であれば
- Realm
- CoreData
になるかと思います。
普通にローカルにデータを保存したいのであれば、Realmで全く問題ないでしょう。
情報がたくさん出回っていますし、公式のドキュメントも非常に丁寧で読みやすいです。
一方、CoreDataについては、iCloudを使った端末間のデータ同期を使うのであれば選択肢に入ってきますが、そうでないなら選ぶ理由はあまりないかなと思います。
iCloud ではありませんが、 Realm Platform(Cloud) を使えば、端末間のデータのリアルタイム同期が実現できます。
利用量に合わせて従量課金されます。
Database (Firestore)
ローカルデータベースという意味だと忘れられがちですが、
Firebase Firestoreをローカルデータベースとして代用してしまうのも一つの手だと思います。
オフラインキャッシュの機能もありますので、Realmだとデータの書き換え時にスレッドを意識する必要があったりと、多少癖がありますから、Firestoreの方がデータの取り扱いが楽な点もありますから、Forestoreにすでに慣れ親しんでいるのであれば、そのままローカルデータベースとして利用してしまっても良いと思います。
無料枠も大きいので、個人開発で小規模なアプリなら、料金的にもそれほど問題にならないはずです。
その他のデータ保存
簡単な設定値の保存やIDの保存については、
- UserDefaults
- Keychain
のどちらかになるかと思います。
Keychain の特徴として、アプリをアンインストールしてもデータを保持できたり、複数アプリ間でデータの共有ができたりすることがありますから、こういった用途が必要な場合はKeychainを利用し、それ以外は、UserDefaults を選択します。
特に、アプリのユーザーIDやTokenといった、アプリをアンインストールしたあと、再インストールした際にユーザー情報を復帰させたい場合に、Keychainを利用したりですかね。
プロトタイピングツール
ProtoPie
ProtoPie はアプリのプロトタイピングが簡単にできるツールです。
公式サイトのサンプルをみてもらうとわかりますが、アニメーションやユーザー操作による変化がかなり細かいところまで設定ができます。
さらに、実装も直感的にわかりやすいUIとなっているので、とても使いやすいです。
難点としては少しお高めといったところ。
Adobe XD
私が一番愛用しているのが、Adobe XD です。
Adobe XDがドンドン進化してます。
— ケンタロウ@ソフトウェアエンジニア (@kenny_J_7) September 19, 2019
ベータ版だった時から使ってるけど、画面遷移の表現とかめちゃくちゃ楽になってる😂
ProtoPieがアニメーションが細かく設定できるのに対して、Adobe XDはそこまで細かく設定はできません。
ですがその分、操作はかなり楽で、素早くプロトタイプが実装できるメリットがあります。
イメージ的にはパワポ使うような感覚で使えてしまうので、デザイナーじゃなくてもおすすめ!
しかも無料枠があるので、試しに使う分には無料で問題ありません。
私はこれまでかなりたくさんのプロトタイピングツールを国産・外国産含めて利用してきましたが、機能性・使いやすさ・学習コストの低さのバランスがもっとも優れているのが、Adobe XDだと思っています。
また、初期の頃は機能が貧弱な面もありましたが、どんどん進化しており、今後も機能改善は続いていく点もプラスです。
バックエンド
Firebase
もはやアプリエンジニアで Firebase を使ったことが全くないという人も少ないかもしれません。
バックエンドエンジニアがいない、あるいはプロトタイピングのためのアプリを作りたい、といった場合には、Firebaseを積極的に使っています。
個人で作っているアプリでも、ほぼほぼFirebaseでバックエンドは完結させることが多いです。
アプリ開発全体の構成を考える際には、最低限以下の機能については把握しておき、必要に応じて積極的に利用していくことをお勧めします。
- Authentication(各種SNS認証)
- Cloud Functions(サーバレスコンピューティング)
- Cloud Firestore(NoSQLデータベース)
- Cloud Storage(ストレージ)
- Crashlytics(クラッシュログ管理)
- Cloud Messaging(プッシュ通知)
特に、 Cloud Firestore は強力で、SDKを使ってやれば、クライアントから直接データベースである Firestore への読み書きが実行できるため、サーバーをほとんど意識する必要がありません。
ただ、Firestore ではクエリは使えるとはいえ、RDBに比べると複雑なクエリが苦手だったりするので、検索がよく使われるようなアプリだと相性が悪いかもしれません。
それでも、これまでは Firebase での唯一のデータベース機能だった、 Firebase RealtimeDB ではクエリが全く使えず、データの抽出がすごーく面倒だったのに比べると涙が出る便利さです。
まとめると、
- クライアントから直接 Cloud Firestore にアクセスして完結できないか考える
- データの整形などの処理が必要なら、Firebase Functions を挟んで実現できないか考える
といった感じの思考をしています。
最後に
以上、思いついたものを一気に書いてみました。
SwiftUIについては全然知識がキャッチアップできていないのでまずいなーっていう感じですね。
iOS13もリリースされましたし、そろそろ時間を見つけて勉強しないと…
少しでも参考になったら嬉しいです😄