この記事で伝えたいこと
普段Androidエンジニアをやっている私がSwiftUIに挑戦してみて、理解するのに時間がかかった、あるいはまだ理解できていないことをつらつら書いていきます。
Swift自体ちゃんとやったことがないので、そこからわからないことが多かったです。
SwiftUIをやってみた経緯
KMP(Kotlin Multiplatform)やCMP(Compose Multiplatform)、Flutterといったクロスプラットフォームに興味を持っていたので、まずはKMP+SwiftUIという構成でiOSアプリを作ってみることにしました。
CMPを選ばなかったのは、まずはSwiftUIを使ってみてJetpack Composeとの違いを感じてみたかったからです。
まだSwiftUIのチュートリアルしかやっていないので、今回の記事ではKMPには触れません。
それにしても、昨今のクロスプラットフォームは選択肢が多くて迷いますよね。
すぐに理解できなかったものたち
まずはコードの全体像から
struct LandmarkList: View {
@Environment(ModelData.self) var modelData
@State private var showFavoritesOnly = false
var filteredLandmarks: [Landmark] {
modelData.landmarks.filter { landmark in
(!showFavoritesOnly || landmark.isFavorite)
}
}
var body: some View {
NavigationSplitView {
List {
Toggle(isOn: $showFavoritesOnly) {
Text("Favorites only")
}
ForEach(filteredLandmarks) { landmark in
NavigationLink {
LandmarkDetail(landmark: landmark)
} label: {
LandmarkRow(landmark: landmark)
}
}
}
.animation(.default, value: filteredLandmarks)
.navigationTitle("Landmarks")
} detail: {
Text("Select a Landmark")
}
}
}
struct(構造体)
struct LandmarkList: View {
SwiftUIでは、Viewに準拠したstructを使うことでUIを構築します。
そこまではわかりやすいのですが、他でも多々使われているようで。
struct Landmark: Hashable, Codable, Identifiable {
var id: Int
var name: String
var park: String
var state: String
var description: String
var isFavorite: Bool
private var imageName: String
var image: Image {
Image(imageName)
}
private var coordinates: Coordinates
var locationCoordinate: CLLocationCoordinate2D {
CLLocationCoordinate2D(
latitude: coordinates.latitude,
longitude: coordinates.longitude)
}
struct Coordinates: Hashable, Codable {
var latitude: Double
var longitude: Double
}
}
こんな感じで使われているのですが、Kotlinには構造体がないので、どういった目的で使われてるのか最初はピンときませんでした。
調べてみて、値渡し(copy)をできたり、継承できないといった特徴がわかり、Kotlinでいうところのdata classに近いものと理解しました。
ひとまとまりのデータを表現するのにはstructを使っておくのが良さそうです。
@State
@State private var showFavoritesOnly = false
値の更新を監視するためのもの。これを使わないとstruct内でプロパティを変更することができないので、値が変わった時の再描画もできない
Kotlin(Compose)で言うところのmutableStateFlowあたりが近いかなと思いました。
@environment
@State private var showFavoritesOnly = false
これが一番わからなかったです。最初は環境変数やグローバル変数みたいな話なのかと思っていましたが、そうでもないようで。
どうやら、親ビューから暗黙的に値を注入できるようになるみたいで、DIコンテナのような役割を果たすようです。
DIコンテナは値の扱いを楽にしますが、暗黙的に行なってしまうことから、データの流れを追いにくくなる気がしますね。
特に初学者は理解が必要な部分だと感じました。
おわりに
「これはKotlinにおける〜」って考え方で一つずつ調べていけば、概念的にはそんなに難しくないことが多かったです。
全然関係ないですが、Android Studioのショートカットに慣れすぎたせいで
Xcodeの操作が辛いです。