Die ausgewogene Architektur der mobilen Anwendung verlÀngert die Lebensdauer des Projekts und der Entwickler.
Geschichte
Treffen Sie Alex. Er muss eine Anwendung entwickeln, um eine Einkaufsliste zu erstellen. Alex ist ein erfahrener Entwickler und bildet zunÀchst die Anforderungen an das Produkt:
- Die Möglichkeit, das Produkt auf andere Plattformen (watchOS, macOS, tvOS) zu portieren
- Vollautomatische Anwendungsregression
- IOS 13+ UnterstĂŒtzung
Alex hat kĂŒrzlich das pointfree.com- Projekt kennengelernt , bei dem Brandon und Stephen ihre Einblicke in die moderne Anwendungsarchitektur teilten. So erfuhr Alex von Composable Architecutre.
Zusammensetzbare Architektur
Nach Durchsicht der Dokumentation zur zusammensetzbaren Architektur stellte Alex fest, dass es sich um eine unidirektionale Architektur handelte, die den Entwurfsanforderungen entsprach. Aus der BroschĂŒre folgte:
- Aufteilung des Projekts in Module;
- Datengesteuerte BenutzeroberflÀche - Die Konfiguration der Schnittstelle wird durch ihren Status bestimmt.
- Die gesamte Modullogik wird durch Unit-Tests abgedeckt.
- Schnappschuss-Test von Schnittstellen;
- UnterstĂŒtzt iOS 13+, macOS, tvOS und watchOS;
- SwiftUI- und UIKit-UnterstĂŒtzung.
Bevor wir uns mit dem Studium der Architektur befassen, werfen wir einen Blick auf ein Objekt wie einen intelligenten Regenschirm.
Wie beschreibt man das System, nach dem der Regenschirm angeordnet ist?
Das Schirmsystem besteht aus vier Komponenten:
. : .
. .
. .
. 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)
Teil 3 - Erweiterung der FunktionalitĂ€t, HinzufĂŒgen und Entfernen von Produkten (in Bearbeitung)
Teil 4 - Listen-Caching hinzufĂŒgen und in den Store gehen (in Bearbeitung)
Quellen
Produktliste Teil 1: github.com
Ansatz Autorenportal: pointfree.com
Quellen fĂŒr zusammensetzbare Architekturen: https://github.com/pointfreeco/swift-composable-architecture