わふうの人が書いてます。

iOSアプリケーション開発、BLEのファームウェアとハードウェア試作のフリーランスエンジニア。

日本独自のCOVID-19に対する曝露通知アプリケーションのソースコードを読んでみる

日本独自のCOVID-19に対する曝露通知のiOSアプリケーションのソースコードが、いろいろ勉強になると思ったので、読んでみるメモです。
レポジトリ: https://github.com/mamori-i-japan/mamori-i-japan-ios

ポイント:

フォルダ配置

Xcodeのフォルダ配置は、アプリケーションのトップの下にリソースとソースコードがあり、それぞれその下に細分化したフォルダが並びます。

  • Resources
    • Config
  • Sources
    • Models
    • Navigations
    • Networks
    • Screens
    • Services
    • Utilities
    • Views

Sourcesの下には、データモデル、画面遷移のナビゲーション、ネットワーク関連、UIViewControlに相当するScreens、ネットワークアクセスなどの機能の窓口となるサービス、ユーティリティ、Viewがが並びます。よくあるグループの分け方で、わかりやすいです。

ライブラリのライセンスの自動生成

アプリケーションで使用しているライブラリのライセンスを、設定画面に一覧表示する部分を、自動生成しています。

iOSの”設定”アプリに、それぞれのアプリケーションごとに通知などの設定画面があります。Settings.buldleで、その画面にアプリケーション独自の項目を追加できます。Settings.bundle, https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/UserDefaults/Preferences/Preferences.html

自動生成に、A license list generator of all your dependencies for iOS applications, https://github.com/mono0926/LicensePlist を使っています。ビルド時にシェルスクリプトを動かして、Settings.bundle以下に、ソフトウェアラインセスの一覧を追加します。

デバッグと本番の設定分け

Firebaseの設定など、デバッグと本番とで必ず設定が異なる部分は、xcconfigを使っています。ちょっとした差分設定で、かつXcodeがデフォルトで対応していない設定項目には、この使い方はいい感じです。

サーバを利用するアプリケーションは、デバッグで用いるサーバと、本番運用で用いるサーバが異なります。Xcodeで、デバッグと本番とで設定を変える方法は、ターゲットを使う方法があります。ターゲットそれぞれは独立したものですから、1つのターゲットの変更は、その他のターゲットに何らの影響を与えません。これは、もしもデバッグと本番でターゲットを作っていた場合、その両方に共通した設定変更が出てきた場合には、手動でそれぞれのターゲット設定を変更しなければなりません。この構成は、ミスしたり忘れたりしてしまう原因になりえます。

xcconfigの使い方で、Xcodeが対応しているプロビジョニングなどまでxcconfigで仕込む場合もあるかもしれません。ビルドサーバーを管理運営する専任の人がいて、開発者がXcodeで何か変なことをしても影響させないなら、全てをxcconfigに持ち込むのもアリかもしれません。ですが通常は、Xcodeで扱えるものまで、設定ファイルに持ち込むと、Xcodeでいくら設定変更してもその変更が反映されず、原因がわからずに混乱してしまうだけです。

設定はその設定内容に適した場所で、かつ開発者ならどこで設定されているものが影響を与えるのかを思いつけるように、合理的に。

シェルスクリプトなどで、Mintというコマンドを使っています。

A package manager that installs and runs Swift CLI packages, https://github.com/yonaskolb/Mint

Mint で Swift 製のコマンドラインツールを管理する, https://qiita.com/usamik26/items/be813a224a2146daffb7

そんな場合に Mint が便利です。Mint は Swift Package Manager ベースで開発された Swift 製コマンドラインツールを管理するためのツールです。
例えば、SwiftLint や Carthage といったツールが管理できます。Mint を使うと、複数のバージョンをインストールしておき、バージョンを指定して実行することが可能になります。

Node.jsのnpmなどではデフォルトですが、開発で混乱を避けるためには、開発ツールやライブラリのバージョンを固定するなどして、どの端末でビルドしようが同じ結果になるようにしなければなりません。Xcodeのみを使うのであれば、最新版のXcodeでビルドできるようにメンテナンスすればよく、開発ツールのバージョン管理までは必要ではないかもしれません。ですが実際には、Xcodeにその時点ではない使いたい何かがあれば、外部ツールとして取り込んで使っていくでしょうから、Mintのようなツールは必要になります。

受託開発での iOS アプリプロジェクト新規作成プラクティス(上編:Xcode 編),https://qiita.com/lovee/items/38cdfd5d2f5827f27427

UIとストーリーボードの使い方

画面遷移は、ソースコードの内部に実装されています。画面遷移は、独立したファイルで指定されています。それぞれの画面は、UIViewContrllerを継承するクラス名と同じファイル名のSwiftファイルと、それと同じファイル名のストーリーボードで、1画面単位で作られています。画面のインスタンスは、依存性注入のライブラリを利用して、生成しています。

通常は、ストーリーボードに画面遷移も作り込みます。ですが、チーム開発では1画面の修正が、その1つのストーリーボードのファイルを変えてしまうために、コンフリクトがよく生じます。これは、1画面の修正が、その他の画面も含まれるストーリーボードを変えてしまうからです。ストーリーボード1つでの画面遷移管理は、見た目と遷移が一致するので、間違いが起きにくいのですが、コンフリクトの発生は厄介です。

ストーリーボードからUIViewControllerのインスタンスを、依存性注入しながら、生成するライブラリ, https://github.com/Swinject/SwinjectStoryboard

依存性注入は、デバッグと本番とで環境が異なる場合に、ネットワークなどの外部とのやりとり部分を、モックにするか実際に動くものにするかを、設定により切り替えができます。もしもテストで切り分ける必要がないならば、あるいはビジネス層はユニットテストで十分なテストができて、UI層は人間の目で確認していくものならば、このソースコードでサービスとなっている部分は、コードの中でそれぞれインスタンスしていけばいいのですが… その辺りは、開発運用の部分かなと思います。

使われている便利機能

Viewも、xibファイルとswiftでファイル分離しているけど、それをするとxibを反映させるためには、コードでViewをインスタンスして貼り付けないといけない。その時にコンストレインの設定などが面倒だけど、

HomeViewController.swift https://github.com/mamori-i-japan/mamori-i-japan-ios/blob/0a82ccb2bd8d31344ac936894740353e84fee31a/TraceCovid19/Sources/Screens/Home/HomeViewController.swift にあるように、

1
2
3
4
5
6
7
8
let header = HomeUsualHeaderView(frame: headerBaseView.frame)
header.set(contactCount: count) { [weak self] in
self?.pushToTraceHistory()
}
headerBaseView.addSubview(header)
header.snp.makeConstraints { make in
make.edges.equalToSuperview()
}

ダミーで空Viewを作っておいて、そのサブビューに追加して、エッジをsuperViewに貼り付けさせている。エッジのコンストレイン定義は、http://snapkit.io を使っている。

ログ出力は、Swift.log()などSwift名前空間のものを使うのが、今時なのかしらん? Obj-C時代からの週間で、今もdebugLog()などを使っていたけど。
https://github.com/mamori-i-japan/mamori-i-japan-ios/blob/0a82ccb2bd8d31344ac936894740353e84fee31a/TraceCovid19/Sources/Utilities/PrintUtility.swift

デバッグメッセージ表示は、UIWindowの上にテキストViewをスクロール表示させて、デバッグメッセージのアプリ内表示ができるようにしている、っぽい。その場確認は、確かに便利そう。
https://github.com/mamori-i-japan/mamori-i-japan-ios/blob/0a82ccb2bd8d31344ac936894740353e84fee31a/TraceCovid19/Sources/Utilities/PrintUtility.swift