0
0

More than 1 year has passed since last update.

TCAのチュートリアルのTodoをモジュール化してみた

Posted at

環境

  • Xcode
    • 14.2
  • Swift
    • 5.7.2
  • The Composable Architecture
    • 0.52.0
  • mac OS
    • 13.1

はじめに

フォルダ構成はisowordsを参考にしています。
image.png

手順

  1. ライブラリーの作成

    1. File>New>Project>Swift Packageでライブラリーの作成(TodoFeature)
      image.png
  2. 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"]),
        ]
    )
    
  3. TodosFeature.swiftを編集する

    こちらをクリック
    TodosFeature.swift
    import 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")
                }
            }
        }
    }
    
    
  4. 新しいiOSのプロジェクトをライブラリの中に作成する

    1. ⇧⌘Nで新しいアプリを作成する
      image.png
    2. Groupやファイル等の名前を適当に変更する
    3. Swiftファイルが配置されているフォルダの名前を変更した際にはDevelopment Assetsを変更する必要あり。
      image.png
      image.png
      image.png
  5. File>Add Package>Local Packageで、TodosFeatureを選択する
    image.png
    image.png

  6. TodoFeatureをドラッグ&ドロップでPackagesの上に持ってくるそして、Packagesは削除する
    image.png

  7. TARGETSにTodosFeatureを追加する
    image.png

  8. App.swiftを編集する

    App.swift
    import 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()))
            }
        }
    }
    

成果物

image.png
image.png

感想

なんとなくマルチモジュールをどんな感じで行うのか分かった気がする。(Package.swift命)
isowordsではisowordsモジュールにAppフォルダがあるにも関わらず、表示されていない理由が分からないため知見のある方はぜひコメントをお願いいたします!
image.png
image.png

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0