Skip to content

Latest commit

 

History

History
255 lines (193 loc) · 7.31 KB

File metadata and controls

255 lines (193 loc) · 7.31 KB

Flow

Swift 6.2+ Platforms License

SwiftUI向けの型安全な状態管理ライブラリです。単方向データフローアーキテクチャを採用し、ObservationとSwift 6 Concurrencyに対応しています。

Flow Architecture Diagram

カウンターアプリの実装例

import Flow
import SwiftUI

struct CounterFeature: Feature {
    @Observable
    final class State {
        var count = 0
    }

    enum Action: Sendable {
        case increment
        case decrement
    }

    func handle() -> ActionHandler<Action, State, Void> {
        ActionHandler { action, state in
            switch action {
            case .increment:
                state.count += 1
                return .none

            case .decrement:
                state.count -= 1
                return .none
            }
        }
    }
}

struct CounterView: View {
    @State private var store = Store(
        initialState: .init(),
        feature: CounterFeature()
    )

    var body: some View {
        VStack(spacing: 20) {
            Text("\(store.state.count)")
                .font(.largeTitle)

            HStack(spacing: 16) {
                Button("-") {
                    store.send(.decrement)
                }

                Button("+") {
                    store.send(.increment)
                }
            }
        }
    }
}

5つのコア原則

1. 単方向データフロー

すべての状態変更は一方向に流れます:Action → Handler → State → View。これによりアプリの動作が予測可能でデバッグしやすくなります。

Button("読み込み") {
    store.send(.load)  // Actionがhandlerに流れる
}

// Handlerが状態を更新
case .load:
    return .run { state in
        state.data = try await api.fetch()  // StateがViewに流れる
    }
  • 予測可能なデータフロー
  • 状態変更を追跡しやすい
  • 明確な入力と出力

2. ビューローカルな状態

各ビューが@Stateで自身の状態を保持します。SwiftUIの哲学に沿った設計で、グローバルストアもストア階層もありません。

struct CounterView: View {
    @State private var store = Store(
        initialState: .init(),
        feature: CounterFeature()
    )

    var body: some View {
        VStack {
            Text("\(store.state.count)")
            Button("Increment") {
                store.send(.increment)
            }
        }
    }
}
  • 明確なライフサイクル(StoreはViewと連動)
  • グローバル状態の管理が不要
  • メモリ効率的

3. 結果を返すアクション

アクションは型付きの結果を返すことができ、関数的なパターンを実現し、副作用を明示的にします。

enum ActionResult: Sendable {
    case saved(id: String)
}

// Handler内
case .save(let title):
    return .run { state in
        let todo = try await api.create(title: title)
        return .saved(id: todo.id)  // 結果を返す
    }

// View側
Button("保存") {
    Task {
        let result = await store.send(.save(title: title)).value
        if case .success(.saved(let id)) = result {
            await navigator.navigate(to: .detail(id: id))
        }
    }
}
  • アクションが関数のように値を返す
  • 親がナビゲーションや副作用を制御
  • 型安全なコントラクト

4. MainActor隔離

非同期処理内で直接状態を更新できます—安全に。FlowはSwift 6のdefaultIsolation(MainActor.self)を活用してコンパイル時の安全性を提供します。

case .fetchUser:
    state.isLoading = true
    return .run { state in
        // 非同期コンテキスト内で直接状態を更新!
        let user = try await api.fetchUser()
        state.user = user
        state.isLoading = false
    }
  • コードの局所性(ローディング、取得、エラーが1箇所に)
  • 直感的(通常のSwiftコードと同じ感覚で書ける)
  • コンパイル時安全性(データ競合はコンパイル時に検出)

5. @Observable準拠

@ObservableObject@Publishedではなく、SwiftUI標準の@Observableを使用します。

@Observable
final class State {
    var count = 0  // @Publishedは不要
}
  • Combine依存が不要
  • ボイラープレートが少ない
  • SwiftUIとプラットフォーム連携

ドキュメント

📖 完全なドキュメント

インストール

Swift Package Manager

Package.swiftにFlowを追加:

dependencies: [
    .package(url: "https://github.com/ViewFeature/Flow.git", from: "1.3.1")
],
targets: [
    .target(
        name: "YourApp",
        dependencies: [
            .product(name: "Flow", package: "Flow")
        ],
        swiftSettings: [
            .defaultIsolation(MainActor.self)  // 推奨
        ]
    )
]

Xcode

  • File → Add Package Dependenciesを選択
  • 以下のURLを入力:https://github.com/ViewFeature/Flow.git
  • バージョンを選択:1.3.1以降

推奨: ターゲットのBuild Settings → Other Swift Flags-default-isolation MainActorを追加してください。

動作環境

  • iOS 18.0+ / macOS 15.0+ / watchOS 11.0+ / tvOS 18.0+
  • Swift 6.2+
  • Xcode 16.2+

貢献

コントリビューションを歓迎します!

プルリクエストを送信する前に、貢献ガイドを確認してください。質問やアイデアがある場合は、ディスカッションを開始してください。

コミュニティ

クレジット

Flowは以下のライブラリとコミュニティにインスパイアされています:

メンテナー

ライセンス

FlowはMITライセンスの下で配布されています。詳細はLICENSEファイルを参照してください。