البنية التركيبية - نظرة جديدة على هندسة التطبيقات

تطيل البنية المتوازنة لتطبيقات الهاتف المحمول من عمر المشروع والمطورين.



التاريخ



قابل أليكس. يحتاج إلى تطوير تطبيق لعمل قائمة تسوق. أليكس هو مطور متمرس وأولًا أشكال متطلبات المنتج:



  1. القدرة على نقل المنتج إلى منصات أخرى (watchOS و macOS و tvOS)
  2. الانحدار الآلي الكامل للتطبيق
  3. دعم IOS 13+


تعرف أليكس مؤخرًا على مشروع pointfree.com ، حيث شارك براندون وستيفن رؤيتهما في هندسة التطبيقات الحديثة. هذه هي الطريقة التي اكتشف بها أليكس حول Composable Architecutre.



بنية قابلة للتركيب



بعد مراجعة وثائق Composable Architecture ، قرر أليكس أنه كان يتعامل مع بنية أحادية الاتجاه تتوافق مع متطلبات التصميم. من الكتيب الذي يتبعه:



  1. تقسيم المشروع إلى وحدات ؛
  2. واجهة مستخدم تعتمد على البيانات - يتم تحديد تكوين الواجهة حسب حالتها ؛
  3. يتم تغطية كل منطق الوحدة من خلال اختبارات الوحدة ؛
  4. اختبار اللقطات للواجهات ؛
  5. يدعم iOS 13+ و macOS و tvOS و watchOS ؛
  6. دعم SwiftUI و UIKit.


قبل الغوص في دراسة الهندسة المعمارية ، دعنا نلقي نظرة على شيء مثل المظلة الذكية.



صورة بديلة

كيف تصف النظام الذي يتم ترتيب المظلة به؟



يتكون نظام المظلة من أربعة مكونات:



. : .



. .



. .



. 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