This is a general question about SwiftUI and the architecture, so I'll take a simple but problematic example.
Initial project :
I have a first View which displays a list of Items. This list is managed by a class (which I called ListViewModel here). In a second view I can modify one of these Items, and save these modifications with a "save" button. In a simplified version, I can do this easily using @Binding. Thanks SwiftUI:
struct ListView: View {
    @StateObject var vm = ListViewModel()
    var body: some View {
        NavigationView {
            List(Array(vm.data.enumerated()), id: \.1.id) { index, item in
                NavigationLink(destination: DetailView(item: $vm.data[index])) {
                    Text(item.name)
                }
            }
        }
    }
}
struct DetailView: View {
    @Binding var initialItem: Item
    @State private var item: Item
    init(item: Binding<Item>) {
        _item = State(initialValue: item.wrappedValue)
        _initialItem = item
    }
    var body: some View {
        VStack {
            TextField("name", text: $item.name)
            TextField("description", text: $item.description)
            Button("save") {
                initialItem = item
            }
        }
    }
}
struct Item: Identifiable {
    let id = UUID()
    var name: String
    var description: String
    static var fakeItems: [Item] = [.init(name: "My item", description: "Very good"), .init(name: "An other item", description: "not so bad")]
}
class ListViewModel: ObservableObject {
    @Published var data: [Item] = Item.fakeItems
    func fetch() {}
    func save() {}
    func sort() {}
}
Problem :
Things get more complicated when the detail / edit view gets more complex. Its number of properties increases, we must set up code that does not concern the View (networking, storage, etc.), possibly an FSM, so we have another class to manage the DetailView (in my example: DetailViewModel).
And now the communication between the two Views, which was so easy with the @Binding becomes complicated to set up. In our example, these two elements are not linked, so we have to set up a two-way-binding :
class ListViewModel: ObservableObject {
    @Published var data: [Item]     <-----------
    func fetch() {}                             |
    func save() {}                              |
    func sort() {}                              |
}                                               | /In Search Of Binding/
                                                |
class DetailViewModel: ObservableObject {       |
    @Published var initialItem: Item <----------
    @Published var item: Item
                                                
    init(item: Item) {
        self.initialItem = item
        self.item = item
    }
    func fetch() {}
    func save() {
        self.initialItem = item
    }
}
Attempts
1. An array of DetailViewModels in the ListViewModel + Combine
Rather than storing an Array of Item, my ListViewModel could store a [DetailViewModel]. So during initialization it could subscribe to changes on DetailViewModels :
class ListViewModel: ObservableObject {
    @Published var data: [DetailViewModel]
    var bag: Set<AnyCancellable> = []
    init(items: [Item] = Item.fakeItems) {
        data = items.map(DetailViewModel.init(item:))
        subscribeToItemsChanges()
    }
    func subscribeToItemsChanges() {
        data.enumerated().publisher
            .flatMap { (index, detailVM) in
                detailVM.$initialItem
                    .map{ (index, $0 )}
            }
            .sink { [weak self] index, newValue in
                self?.data[index].item = newValue
                self?.objectWillChange.send()
            }
            .store(in: &bag)
    }
}
Results : Ok, that works, although it's not really a two-way-binding. But is it really relevant that a ViewModel contains an array of other ViewModels? a) It smells weird. b) We have an array of references (and no data types). c) we end up with that in the View:
List(Array(vm.data.enumerated()), id: \.1.item.id) { index, detailVM in
                NavigationLink(destination: DetailView(vm: detailVM)) {
                    Text(detailVM.item.name)
                }
            }
2. Give to DetailViewModel the reference of ListViewModel (Delegate style)
Since the DetailViewModel does not contain the array of Items, and since the Item it handles no longer has a @Binding: we could pass the ListViewModel (which contains the array) to each DetailViewModel.
protocol UpdateManager {
    func update(_ item: Item, at index: Int)
}
class ListViewModel: ObservableObject, UpdateManager {
    @Published var data: [Item]
    init(items: [Item] = Item.fakeItems) {
        data = items
    }
    func update(_ item: Item, at index: Int) {
        data[index] = item
    }
}
class DetailViewModel: ObservableObject {
    @Published var item: Item
    private var updateManager: UpdateManager
    private var index: Int
    init(item: Item, index: Int, updateManager: UpdateManager) {
        self.item = item
        self.updateManager = updateManager
        self.index = index
    }
    func fetch() {}
    func save() {
        updateManager.update(item, at: index)
    }
}
Results : It works but : 1) It seems like an old way which doesn't quite match the style of SwiftUI. 2) We must pass the index of the Item to the DetailViewModel.
3. Use a closure
Rather than passing a reference to the entire ListViewModel, we could pass a closure (onSave) to the DetailViewModel.
class ListViewModel: ObservableObject {
    @Published var data: [Item]
    init(items: [Item] = Item.fakeItems) {
        data = items
    }
    func update(_ item: Item, at index: Int) {
        data[index] = item
    }
}
class DetailViewModel: ObservableObject {
    @Published var item: Item
    var update: (Item) -> Void
    init(item: Item, onSave update: @escaping (Item) -> Void) {
        self.item = item
        self.update = update
    }
    func fetch() {}
    func save() {
        update(item)
    }
}
Results: On one hand it still looks like an old approach, on the other hand it seems to match the "one view - one ViewModel" approach. If we use an FSM we could imagine sending an Event / Input.
Variant:
We can use Combine and pass a PassthroughSubject rather than a closure :
class ListViewModel: ObservableObject {
    @Published var data: [Item]
    var archivist = PassthroughSubject<(Int, Item), Never>()
    var cancellable: AnyCancellable?
    init(items: [Item] = Item.fakeItems) {
        data = items
        cancellable = archivist
            .sink {[weak self ]index, item in
                self?.update(item, at: index)
            }
    }
    func update(_ item: Item, at index: Int) {
        data[index] = item
    }
}
class DetailViewModel: ObservableObject {
    @Published var item: Item
    var index: Int
    var archivist: PassthroughSubject<(Int, Item), Never>
    init(item: Item, saveWith archivist: PassthroughSubject<(Int, Item), Never>, at index: Int) {
        self.item = item
        self.archivist = archivist
        self.index = index
    }
    func fetch() {}
    func save() {
        archivist.send((index, item))
    }
}
Question :
I could also have used an @Binding in my ObservableObject, or even wrapped my Item array in an other ObservableObject (and therefore have an OO in an OO). But it seems even less relevant to me.
In any case, everything seems very complicated as soon as we leave a simple Model-View architecture: where a simple @Binding is enough.
So I ask for your help : What do you recommend for this kind of scenario? What do you think is the most suitable for SwiftUI? Can you think of a better way?
@Published var data: [Item]to@Published var data: [DetailViewModel] and change yourDetailView` to use@ObservedObject var item: ItemItemis astruct. Did you mean the first attempt is ok?