SwiftUI MVVM 绑定列表项

SwiftUI MVVM Binding List Item

我正在尝试创建这样的列表视图和详细屏幕:

struct MyListView: View {
   @StateObject var viewModel: MyListViewModel = MyListViewModel()

   LazyVStack {
      // https://www.swiftbysundell.com/articles/bindable-swiftui-list-elements/
      ForEach(viewModel.items.identifiableIndicies) { index in
         MyListItemView($viewModel.items[index])
      }
   }
}

class MyListViewModel: ObservableObject {
   @Published var items: [Item] = []
   ...
}

struct MyListItemView: View {
   @Binding var item: Item

   var body: some View {
      NavigationLink(destination: MyListItemDetailView(item: $item), label: {
         ...
      })
   }
}

struct MyListItemDetailView: View {
   @Binding var item: Item
   @StateObject var viewModel: MyListViewItemDetailModel

   init(item: Binding<Item>) {
      viewModel = MyListViewItemDetailModel(item: item)
   }

   var body: some View {
      ...
   }
}

class MyListViewItemDetailModel: ObservableObject {
   var item: Binding<Item>

   ...
}

我不确定它有什么问题,但我发现 item 变量彼此不同步,即使在 MyListItemDetailView 和 MyListItemDetailViewModel 之间也是如此。 有没有人可以提供最佳实践并让我知道我的实现有什么问题?

我认为你应该考虑对你的代码进行一次小的重组,并且只使用 1 @StateObject/ObservableObject。这是您使用的代码的简化版本 只有一个 StateObject 真实来源:

注意:AFAIK 绑定旨在用于视图结构而不是“普通”类。

PS:什么是 identifiableIndicies?

import SwiftUI

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct Item: Identifiable {
    let id = UUID().uuidString
    var name: String = ""
}

struct MyListView: View {
    @StateObject var viewModel: MyListViewModel = MyListViewModel()
    
    var body: some View {
        LazyVStack {
            ForEach(viewModel.items.indices) { index in
                MyListItemView(item: $viewModel.items[index])
            }
        }
    }
}

class MyListViewModel: ObservableObject {
    @Published var items: [Item] = [Item(name: "one"), Item(name: "two")]
}

struct MyListItemView: View {
    @Binding var item: Item
    
    var body: some View {
        NavigationLink(destination: MyListItemDetailView(item: $item)){
            Text(item.name)
        }
    }
}

class MyAPIModel {
    func fetchItemData(completion: @escaping (Item) -> Void) {
        // do your fetching here
        completion(Item(name: "new data from api"))
    }
}

struct MyListItemDetailView: View {
    @Binding var item: Item
    let myApiModel = MyAPIModel()
    
    var body: some View {
        VStack {
            Button(action: fetchNewData) {
                Text("Fetch new data")
            }
            TextField("edit item", text: $item.name).border(.red).padding()
        }
    }
    
    func fetchNewData() {
        myApiModel.fetchItemData() { itemData in
            item = itemData
        }
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            MyListView()
        }.navigationViewStyle(.stack)
    }
}

编辑 1:

设置一个 API 来调用一些函数,你可以使用这样的东西:

class MyAPI {
    func fetchItemData(completion: @escaping (Item) -> Void) {
        // do your stuff
    }
}

并使用它从服务器获取您需要的任何数据。

EDIT2:添加了一些代码来演示 API.

的使用