0

I'm currently having all sorts of problems with a NavigationView in my multi-platform SwiftUI app.

My goal is to have a NavigationView with an item for each object in a list from Core Data. And each NavigationLink should lead to a view that can read and write data of the object that it's showing.

However I'm running into many problems, so I figured I'm probably taking the wrong approach.

Here is my code as of now:

struct InstanceList: View {
    @StateObject private var viewModel = InstancesViewModel()
    
    @State var selectedInstance: Instance?
    
    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.instances) { instance in
                    NavigationLink(destination: InstanceView(instance: instance), tag: instance, selection: $selectedInstance) {
                        InstanceRow(instance)
                    }
                }
                .onDelete { set in
                    viewModel.deleteInstance(viewModel.instances[Array(set)[0]])
                    
                    for reverseIndex in stride(from: viewModel.instances.count - 1, through: 0, by: -1) {
                        viewModel.instances[reverseIndex].id = Int16(reverseIndex)
                    }
                }
                
            }
            .onAppear {
                selectedInstance = viewModel.instances.first
            }
            .listStyle(SidebarListStyle())
            .navigationTitle("Instances")
            .toolbar {
                ToolbarItemGroup {
                    Button {
                        withAnimation {
                            viewModel.addInstance(name: "4x4", puzzle: "3x3") // temporary
                        }
                    } label: {
                        Image(systemName: "plus")
                    }
                }
            }
        }
    }
}

and the view model (which probably isn't very relevant but I'm including it just in case):

class InstancesViewModel: ObservableObject {
    @Published var instances = [Instance]()
    
    private var cancellable: AnyCancellable?
    
    init(instancePublisher: AnyPublisher<[Instance], Never> = InstanceStorage.shared.instances.eraseToAnyPublisher()) {
        cancellable = instancePublisher.sink { instances in
            self.instances = instances
        }
    }
    
    func addInstance(name: String, puzzle: String, notes: String? = nil, id: Int? = nil) {
        InstanceStorage.shared.add(
            name: name,
            puzzle: puzzle,
            notes: notes,
            id: id ?? (instances.map{ Int($0.id) }.max() ?? -1) + 1
        )
    }
    
    func deleteInstance(_ instance: Instance) {
        InstanceStorage.shared.delete(instance)
    }
    
    func deleteInstance(withId id: Int) {
        InstanceStorage.shared.delete(withId: id)
    }
    
    func updateInstance(_ instance: Instance, name: String? = nil, puzzle: String? = nil, notes: String? = nil, id: Int? = nil) {
        InstanceStorage.shared.update(instance, name: name, puzzle: puzzle, notes: notes, id: id)
    }
}

and then the InstanceView, which just shows some simple information for testing:

struct InstanceView: View {
    @ObservedObject var instance: Instance
    
    var body: some View {
        Text(instance.name)
        Text(String(instance.id))
    }
}

Some of the issues I'm having are:

  • On iOS and iPadOS, when the app starts, it will show a blank InstanceView, pressing the back button will return to a normal instanceView and pressing it again will show the navigationView
  • Sometime pressing on a navigationLink will only highlight it and won't go to the destination
  • On an iPhone in landscape, when scrolling through the NavigationView, sometimes the selected Item will get unselected.
  • When I delete an item, the InstanceView shows nothing for the name and 0 for the id, as if its showing a "ghost?" instance, until you select a different one.

I've tried binding the selecting using the index of the selected Instance but that still has many of the same problems.

So I feel like I'm making some mistake in the way that I'm using NavigationView, and I was wondering what the best approach would be for creating a navigationView from an Array that works nicely across all devices.

Thanks!

Cameron Delong
  • 454
  • 1
  • 6
  • 12
  • is "Instance" a ObservableObject? This is what is expected in InstanceView. Could you show some code about "Instance". Try also: "ForEach(viewModel.instances, id: \.id) { ... }" – workingdog support Ukraine Jul 17 '21 at 02:42
  • Instance is an NSManagedObject, which I'm like 95% sure conforms to ObservableObject. Here's the class definition and extensions, mostly generated by Xcode from my xcdatamodeld file. Also, thanks for the suggestion with the foreach, I'll try it when I have a chance later today. – Cameron Delong Jul 17 '21 at 08:15
  • Opps forgot to put the link lol here it is: https://pastebin.com/06NmRFtu @workingdog – Cameron Delong Jul 17 '21 at 08:21

0 Answers0