1
votes

Consider a rather simple SwiftUI app: a list view showing a list of models, where each row is a NavigationLink to a detail view. With SwiftUI, any changes to the currently viewing model automatically result in an updated UI; the detail view is always showing the latest version of the model. Hooray! :)

But what about when the model is deleted while you're on the detail view? That doesn't do anything, you're left on the detail view. See below for a very simple example that does illustrate the problem:

struct Model: Identifiable {
  let id: Int
  var title: String
}

class Store: ObservableObject {
  @Published var models = [Model(id: 0, title: "a")]
}

struct ListView: View {
  @EnvironmentObject private var store: Store

  var body: some View {
    NavigationView {
      List(store.models) { model in
        NavigationLink(destination: DetailView(model: model)) {
          Text(model.title)
        }
      }
      .navigationBarTitle("List")
    }
  }
}

struct DetailView: View {
  @EnvironmentObject private var store: Store

  var model: Model

  var body: some View {
    Text(model.title)
      .navigationBarTitle("Detail")
      .navigationBarItems(trailing: trailingNavigationBarItems)
  }

  private var trailingNavigationBarItems: some View {
    HStack {
      Button("Change title") {
        self.store.models[0].title = "AAA"
      }

      Button("Delete model") {
        self.store.models.remove(at: 0)
      }
    }
  }
}

How would the detail view recognize that its model no longer exist and pop back to the list view? As I said, currently you're just left on the detail view, looking at a model that really no longer exists. When you manually go back to the list, that is updated and the model is gone.

If you want to run this code, just use ListView().environmentObject(Store()) as the rootView in the SceneDelegate.

(Before anyone says that I could just pop back in the delete button action, that button is just there to demonstrate the problem. In reality the model could be deleted on the server for example, so not by an action initiated from the detail view.)

1
Well, if I would be a user of such app, it would be very confusing for me to read some details on screen and got suddenly navigate back w/o my intervention. While there are technical possibilities to do this, I do not recommend such behavior, and seems it would be AHIG violation. - Asperi
Makes sense, except in my case the model could be deleted by the user themself on a different device, which then syncs over for example. - Kevin Renskers

1 Answers

2
votes

I would solve the problem like this way. For example, if I want to know if the model has no content, i automatically go back to the list view by programatically popup the current view. And it works.

struct DetailView: View {
  @EnvironmentObject private var store: Store
  @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

  var model: Model

  var body: some View {
    ZStack {
        Text(model.title)
    }
    .navigationBarTitle("Detail")
    .navigationBarItems(trailing: trailingNavigationBarItems)
    .onReceive(self.store.$models) { model in
        if model.count == 0 {
            self.presentationMode.wrappedValue.dismiss()
        }
    }
  }