構成可能なアーキテクチャ-アプリケーションアーキテクチャの再検討

モバイルアプリケーションのバランスの取れたアーキテクチャは、プロジェクトと開発者の寿命を延ばします。



歴史



アレックスに会いましょう。彼はショッピングリストを作成するためのアプリケーションを開発する必要があります。Alexは経験豊富な開発者であり、まず最初に製品の要件を形成します。



  1. 製品を他のプラットフォーム(watchOS、macOS、tvOS)に移植する機能
  2. 完全に自動化されたアプリケーション回帰
  3. IOS13以降のサポート


Alexは最近pointfree.comプロジェクトに精通しました。そこでは、BrandonとStephenが最新のアプリケーションアーキテクチャに関する洞察を共有しました。これは、アレックスがComposableArchitecutreについて知った方法です。



構成可能なアーキテクチャ



構成可能なアーキテクチャのドキュメントを確認した後、アレックスは、設計要件に一致する単方向アーキテクチャを扱っていると判断しました。パンフレットからそれは続きました:



  1. プロジェクトをモジュールに分割する。
  2. データ駆動型UI-インターフェイス構成はその状態によって決定されます。
  3. すべてのモジュールロジックはユニットテストでカバーされています。
  4. インターフェイスのスナップショットテスト。
  5. iOS 13以降、macOS、tvOS、watchOSをサ​​ポートします。
  6. SwiftUIとUIKitのサポート。


建築の研究に飛び込む前に、スマートアンブレラなどのオブジェクトを見てみましょう。



画像代替

傘が配置されるシステムをどのように説明しますか?



アンブレラシステムには4つのコンポーネントがあります。



. : .



. .



. .



. 10 .



composable architecture . .





? , .



UI — [];



Action — ;



State — [];



Environment — [ ];



Reducer — , [] ;



Effect — , action reducer.



( 1)







.



. , .



struct ShoppingListState {
    var products: [Product] = []
}

enum ShoppingListAction {
    case addProduct
}


reducer :



let shoppingListReducer = Reducer { state, action, env in
    switch action {
    case .addProduct:
        state.products.insert(Product(), at: 0)
        return .none
    }
}


:



struct Product {
    var id = UUID()
    var name = ""
    var isInBox = false
}

enum ProductAction {
    case toggleStatus
    case updateName(String)
}

let productReducer = Reducer { state, action, env in
    switch action {
    case .toggleStatus:
        state.isInBox.toggle()
        return .none
    case .updateName(let newName):
        state.name = newName
        return .none
    }
}


, reducer , , . reducer .



UI .



UI





iOS 13+ Composable Architecture SwiftUI, .



, Store:



typealias ShoppingListStore = Store<ShoppingListState, ShoppingListAction>

let store = ShoppingListStore(
    initialState: ShoppingListState(products: []),
    reducer: shoppingListReducer,
    environment: ShoppingListEnviroment()
)


Store viewModel MVVM — .



let view = ShoppingListView(store: store)

struct ShoppingListView: View {
    let store: ShoppingListStore

    var body: some View {
        Text("Hello, World!")
    }
}


Composable Architecture SwiftUI. , store ObservedObject, WithViewStore:



var body: some View {
    WithViewStore(store) { viewStore in
        NavigationView {
            Text("\(viewStore.products.count)")
            .navigationTitle("Shopping list")
            .navigationBarItems(
                trailing: Button("Add item") {
                    viewStore.send(.addProduct)
                }
            )
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}


Add item, . send(Action) .



, , :



struct ProductView: View {
    let store: ProductStore

    var body: some View {
        WithViewStore(store) { viewStore in
            HStack {
                Button(action: { viewStore.send(.toggleStatus) }) {
                    Image(
                        systemName: viewStore.isInBox
                            ? "checkmark.square"
                            : "square"
                    )
                }
                .buttonStyle(PlainButtonStyle())
                TextField(
                    "New item",
                    text: viewStore.binding(
                        get: \.name,
                        send: ProductAction.updateName
                    )
                )
            }
            .foregroundColor(viewStore.isInBox ? .gray : nil)
        }
    }
}




. ? .



enum ShoppingListAction {
        //       
    case productAction(Int, ProductAction)
    case addProduct
}

//      
// ..   ,   
let shoppingListReducer: Reducer<ShoppingListState, ShoppingListAction, ShoppingListEnviroment> = .combine(
        //  ,     
    productReducer.forEach(
                // Key path
        state: ShoppingListState.products,
                // Case path
        action: /ShoppingListAction.productAction,
        environment: { _ in ProductEnviroment() }
    ),
    Reducer { state, action, env in
        switch action {
        case .addProduct:
            state.products.insert(Product(), at: 0)
            return .none
                //      productReducer
        case .productAction:
            return .none
        }
    }
)




. .



UI :



var body: some View {
    WithViewStore(store) { viewStore in
        NavigationView {
            List {
        //   
                ForEachStore(
            //  store
                    store.scope(
                        state: \.products,
                        action: ShoppingListAction.productAction
                    ),
            //  
                    content: ProductView.init
                )
            }
            .navigationTitle("Shopping list")
            .navigationBarItems(
                trailing: Button("Add item") {
                    viewStore.send(.addProduct)
                }
            )
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}


150 , .





2 — (in progress)



パート3-機能の拡張、製品の削除と並べ替えの追加(進行中)



パート4-リストキャッシングを追加してストアに移動します(進行中)



ソース



製品リストパート1:github.com



アプローチ作成者ポータル:pointfree.com



構成可能なアーキテクチャのソース:https//github.com/pointfreeco/swift-composable-architecture




All Articles