Arsitektur Komposabel - Pandangan Baru pada Arsitektur Aplikasi

Arsitektur yang seimbang dari aplikasi seluler memperpanjang umur proyek dan pengembang.



Sejarah



Temui Alex. Ia perlu mengembangkan aplikasi untuk membuat daftar belanjaan. Alex adalah pengembang berpengalaman dan pertama-tama persyaratan untuk produk:



  1. Kemampuan untuk mem-port produk ke platform lain (watchOS, macOS, tvOS)
  2. Regresi aplikasi yang sepenuhnya otomatis
  3. Dukungan IOS 13+


Alex baru-baru ini berkenalan dengan proyek pointfree.com , di mana Brandon dan Stephen berbagi wawasan mereka tentang arsitektur aplikasi modern. Inilah cara Alex mengetahui tentang Composable Architecutre.



Arsitektur yang Dapat Disusun



Setelah meninjau dokumentasi Arsitektur Kompos, Alex memutuskan bahwa dia berurusan dengan arsitektur searah yang sesuai dengan persyaratan desain. Dari brosur itu diikuti:



  1. Membagi proyek menjadi modul;
  2. UI berbasis data - konfigurasi antarmuka ditentukan oleh statusnya;
  3. Semua logika modul dicakup oleh pengujian unit;
  4. Pengujian snapshot antarmuka;
  5. Mendukung iOS 13+, macOS, tvOS dan watchOS;
  6. Dukungan SwiftUI dan UIKit.


Sebelum terjun ke studi arsitektur, mari kita lihat objek seperti payung pintar.



alt gambar

Bagaimana cara mendeskripsikan sistem pengaturan payung?



Sistem payung memiliki empat komponen:



. : .



. .



. .



. 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)



Bagian 3 - memperluas fungsionalitas, menambahkan penghapusan dan penyortiran produk (dalam proses)



Bagian 4 - tambahkan cache daftar dan pergi ke toko (dalam proses)



Sumber



Daftar Produk Bagian 1: github.com



Portal penulis pendekatan: pointfree.com



Sumber Arsitektur Komposabel: https://github.com/pointfreeco/swift-composable-architecture




All Articles