FlutterエンジニアのためのSwiftUI & TCA入門 Part1
FlutterエンジニアのためのSwiftUI & TCA入門 Part1: Riverpod/HooksユーザーがモダンなSwift開発を始めるためのガイド
はじめに
※自分の学習用メモとしての意味合いが強いです🚶
Part1である今回は、FlutterでRiverpodやflutter_hooksを使った状態管理に慣れているエンジニアが、iOS開発の最新トレンドであるSwiftUIとTCA(The Composable Architecture)を使った開発に挑戦するためのガイドです。
このガイドでは、SwiftUIの基本的な概念から、TCAのアーキテクチャ、そしてFlutterでの経験を活かすための具体的な方法まで、幅広く解説していきます。
なぜSwiftUIとTCAなのか?
SwiftUIの魅力
- 宣言的なUI: Flutterと同様に、SwiftUIも宣言的なUIフレームワークです。UIをどのように表示するかを記述することで、フレームワークが自動的にUIをレンダリングしてくれます。
- リアクティブなUI: 状態の変化に応じてUIが自動的に更新されるため、複雑なUIの管理が容易になります。
- iOS、macOS、watchOS、tvOSに対応: 一つのコードベースで複数のプラットフォームに対応できるため、開発効率が向上します。
- Appleのエコシステムとの親和性: Appleの最新技術を最大限に活用できるため、高品質なアプリ開発が可能です。
TCAの魅力
- 予測可能な状態管理: TCAは、状態、アクション、副作用を明確に分離することで、複雑な状態管理をシンプルにします。
- テスト容易性: 各コンポーネントが独立しているため、ユニットテストが容易に行えます。
- デバッグの容易性: TCAのアーキテクチャにより、状態の変化と副作用を追跡しやすくなります。
- 大規模アプリ開発: 複雑なアプリケーションでも、一貫性のあるアーキテクチャで開発を進めることができます。
SwiftUIの基礎
プロジェクト作成
まずは新しいSwiftUIプロジェクトを作成しましょう。Xcodeを起動し、「Create a new Xcode project」を選択します。
iOS
> App
を選択し、プロジェクト名を入力してNext
をクリック。Interface
でSwiftUI
を選択してプロジェクトを作成します。
基本的な構文
SwiftUIは、View
プロトコルに準拠した構造体やクラスでUIを記述します。
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, SwiftUI!")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
-
ContentView
は、View
プロトコルに準拠した構造体で、UIを記述します。 -
body
プロパティは、UIのレンダリング方法を記述します。 -
Text
は、テキストを表示するためのViewです。 -
ContentView_Previews
は、Xcodeのプレビューを表示するための構造体です。
レイアウト
SwiftUIでは、HStack
, VStack
, ZStack
を使ってUIをレイアウトします。
- HStack (Horizontal Stack): 水平方向にViewを配置します。
- VStack (Vertical Stack): 垂直方向にViewを配置します。
- ZStack (Z-Axis Stack): Viewを重ねて配置します。
struct LayoutExampleView: View {
var body: some View {
VStack {
HStack {
Text("Left")
Text("Right")
}
ZStack {
Rectangle()
.fill(.blue)
.frame(width: 100, height: 100)
Text("Overlay")
.foregroundColor(.white)
}
Spacer()
Text("Bottom")
}
}
}
@State
と@Binding
)
状態管理(SwiftUIでの状態管理は、@State
と@Binding
を使います。
-
@State
: View内部で管理される状態を定義します。状態が変化すると、Viewが再レンダリングされます。 -
@Binding
: 親Viewから渡された状態を共有します。親Viewの状態が変化すると、子Viewも自動的に更新されます。
struct StateExampleView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Count: \(count)")
Button("Increment") {
count += 1
}
// 子ViewへのBinding
CounterView(count: $count)
}
}
}
struct CounterView: View {
@Binding var count: Int
var body: some View {
Button("Decrement") {
count -= 1
}
}
}
StateExampleView
は、count
という状態を持っており、Button
を押すとcount
が増加します。
CounterView
は、@Binding
で親Viewのcount
を共有しています。CounterView
のボタンを押すと、親Viewのcount
が減少します。
Viewのカスタマイズ
SwiftUIでは、modifier
を使ってViewをカスタマイズします。
struct CustomViewExampleView: View {
var body: some View {
Text("Custom Text")
.font(.title)
.foregroundColor(.white)
.padding()
.background(.blue)
.cornerRadius(10)
}
}
-
.font(.title)
は、フォントサイズをタイトル用に変更します。 -
.foregroundColor(.white)
は、テキストの色を白に変更します。 -
.padding()
は、テキストの周りに余白を追加します。 -
.background(.blue)
は、背景色を青に変更します。 -
.cornerRadius(10)
は、角を丸くします。
TCA(The Composable Architecture)の概要
TCAは、状態管理、副作用、テストをシンプルにするためのアーキテクチャです。TCAは、主に以下の要素で構成されています。
-
State
: アプリケーションの状態を保持する構造体。 -
Action
: ユーザーインタラクションやシステムイベントを表現するenum。 -
Environment
: 副作用(API呼び出し、データベースアクセスなど)を処理する依存関係を保持する構造体。 -
Reducer
:State
とAction
を受け取り、新しいState
と副作用を返す関数。 -
Store
:State
,Action
,Environment
,Reducer
を保持し、状態の変化を管理するクラス。
TCAの基本的な流れ
- ユーザーがUIを操作して
Action
が発生します。 -
Store
がAction
をReducer
に渡します。 -
Reducer
は、現在のState
とAction
に基づいて、新しいState
と副作用を生成します。 -
Store
は新しいState
を反映し、必要に応じて副作用を実行します。 - UIは
Store
から新しいState
を受け取り、再レンダリングされます。
TCAのコード例
import ComposableArchitecture
import SwiftUI
// 1. State
struct CounterState: Equatable {
var count = 0
}
// 2. Action
enum CounterAction {
case incrementButtonTapped
case decrementButtonTapped
}
// 3. Environment (今回は副作用がないので空の構造体)
struct CounterEnvironment {}
// 4. Reducer
let counterReducer = Reducer<CounterState, CounterAction, CounterEnvironment> { state, action, _ in
switch action {
case .incrementButtonTapped:
state.count += 1
return .none
case .decrementButtonTapped:
state.count -= 1
return .none
}
}
// SwiftUIでのView実装
struct TCAExampleView: View {
// Storeの定義
let store = Store(
initialState: CounterState(),
reducer: counterReducer,
environment: CounterEnvironment()
)
var body: some View {
WithViewStore(store) { viewStore in
VStack {
Text("Count: \(viewStore.count)")
HStack {
Button("Increment") {
viewStore.send(.incrementButtonTapped)
}
Button("Decrement") {
viewStore.send(.decrementButtonTapped)
}
}
}
}
}
}
-
CounterState
は、カウント数を保持する構造体です。 -
CounterAction
は、ボタンがタップされたことを表現するenumです。 -
CounterEnvironment
は、副作用がないため空の構造体です。 -
counterReducer
は、State
とAction
に基づいて新しいState
を生成する関数です。 -
TCAExampleView
は、Store
を保持し、UIを構築するViewです。WithViewStore
を使用して、Store
の状態をUIにバインドします。
Riverpod/Hooksとの比較
状態管理
-
Flutter(Riverpod/Hooks):
- Providerで状態を管理し、Hooksで再利用可能なロジックを記述します。
- 状態の変化を監視しやすく、UIの再レンダリングを制御できます。
-
SwiftUI(TCA):
-
State
で状態を保持し、Action
で状態の変化を表現します。 - Reducerで状態の変化を管理し、副作用を分離します。
- 予測可能でテストしやすい状態管理を実現します。
-
副作用
-
Flutter(Riverpod/Hooks):
- Riverpodの
Provider
やFutureProvider
を使って非同期処理を管理します。 -
useEffect
を使って副作用を処理します。
- Riverpodの
-
SwiftUI(TCA):
-
Environment
で副作用の依存関係を管理します。 -
Reducer
で副作用を返し、Effect
を使って副作用を実行します。 - 副作用を明確に分離し、テスト容易性を向上させます。
-
アーキテクチャ
-
Flutter(Riverpod/Hooks):
- 特定のアーキテクチャに依存せず、柔軟に構成を組み立てられます。
-
SwiftUI(TCA):
- 厳格な単方向データフローアーキテクチャを採用します。
- コードの一貫性を維持し、大規模アプリケーション開発に適しています。
Flutterの知識をSwiftUI/TCAで活かす
宣言的なUI
Flutterで慣れ親しんだ宣言的なUIの考え方は、SwiftUIでも同様に適用できます。UIをどのように表示するかを記述することで、フレームワークが自動的にUIをレンダリングしてくれます。
State Management
FlutterでProviderやRiverpodを使って状態管理をしていた方は、TCAの単方向データフローの考え方に比較的スムーズに移行できるはずです。状態、アクション、副作用を明確に分離することで、複雑な状態管理をシンプルにできます。
テスト
Flutterでユニットテストを書いていた経験は、TCAのテスト容易性を最大限に活かすことができます。各コンポーネントが独立しているため、ユニットテストを容易に書くことができます。
まとめ
このドキュメントでは、FlutterエンジニアがSwiftUIとTCAを使ってiOS開発を始めるための基礎知識を解説しました。SwiftUIの宣言的なUIとTCAの単方向データフローアーキテクチャは、Flutterの経験を活かしてスムーズに学習できるはずです。
ぜひ、このガイドを参考にSwiftの世界に飛び込んでみてください。
新しい技術を学ぶことは、エンジニアとしての成長を加速させるはずです。
Discussion