環境
- Xcode
- 14.2
- Swift
- 5.7.2
- The Composable Architecture
- 0.52.0
- mac OS
- 13.1
はじめに
手順
-
ライブラリーの作成
-
Package.swiftを編集する
Package.swift// swift-tools-version: 5.7 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Todos", platforms: [ .iOS(.v15), .macOS(.v12), .tvOS(.v15), .watchOS(.v8), ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "TodosFeature", targets: ["TodosFeature"]), ], dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), .package(url: "https://github.com/pointfreeco/swift-composable-architecture", branch: "main") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "TodosFeature", dependencies: [ .product(name: "ComposableArchitecture", package: "swift-composable-architecture") ]), .testTarget( name: "TodosFeatureTests", dependencies: ["TodosFeature"]), ] )
-
TodosFeature.swiftを編集する
こちらをクリック
TodosFeature.swiftimport ComposableArchitecture import SwiftUI public struct Todos: ReducerProtocol { public struct State: Equatable { var todos: [Todo] = [] public init(todos: [Todo]) { self.todos = todos } } public enum Action { case todoCheckboxTapped(index: Int) case todoTextFieldChanged(index: Int, text: String) } public init() {} public func reduce(into state: inout State, action: Action) -> EffectTask<Action> { switch action { case .todoCheckboxTapped(index: let index): state.todos[index].isComplete.toggle() return .none case .todoTextFieldChanged(index: let index, text: let text): state.todos[index].description = text return .none } } } public struct Todo: Equatable, Identifiable { public var description = "" public let id: UUID public var isComplete = false public init(description: String = "", id: UUID = UUID(), isComplete: Bool = false) { self.description = description self.id = id self.isComplete = isComplete } } public struct TodosView: View { let store: StoreOf<Todos> public init(store: StoreOf<Todos>) { self.store = store } public var body: some View { NavigationView { WithViewStore(self.store) { viewStore in List { ForEach(Array(zip(viewStore.todos.indices, viewStore.todos)), id: \.1.id) { index, todo in HStack { Button { viewStore.send(.todoCheckboxTapped(index: index)) } label: { Image(systemName: todo.isComplete ? "checkmark.square" : "square") } .buttonStyle(.plain) TextField( "Untitled todo", text: viewStore.binding( get: { $0.todos[index].description }, send: { .todoTextFieldChanged(index: index, text: $0) } ) ) } .foregroundColor(todo.isComplete ? .gray : nil) } } .navigationTitle("Todos") } } } }
-
新しいiOSのプロジェクトをライブラリの中に作成する
-
App.swiftを編集する
App.swiftimport ComposableArchitecture import SwiftUI import TodosFeature @main struct TodosApp: App { var body: some Scene { WindowGroup { TodosView(store: Store(initialState: Todos.State(todos: [ Todo(description: "Hand soup"), Todo(description: "Milk"), Todo(description: "iPhone") ]), reducer: Todos())) } } }
成果物
感想
なんとなくマルチモジュールをどんな感じで行うのか分かった気がする。(Package.swift命)
isowordsではisowordsモジュールにAppフォルダがあるにも関わらず、表示されていない理由が分からないため知見のある方はぜひコメントをお願いいたします!