24

I have a list in a view (lets call it view A) where I push a view B via NavigationLink from view A and manipulate the CurrentSubjectValue to the initial view A. The moment the data is added and the didChangd.send(value) is dispatched to view A I get a warning that updating a cell that does not belong to a view. It seems that view A is removed from window which is unintuitive based on my previous UINavigationController behavior. Any idea?

View A (MainView)

struct MainView: View {

    @ObjectBinding var currentProfileIdStore = CurrentProfileIdStore.shared

    var body: some View {

        NavigationView {
            if (currentProfileIdStore.didChange.value == nil) {
                NavigationLink(destination: ProfilesView()){ Text("show profiles view")}
            } else {
                List {
                    Section {
                        ForEach(0..<1) { _ in
                            PresentationLink(destination: ProfilesView()) {
                                Text("Profiles")
                            }
                        }
                    }

                    Section {
                        ForEach(0..<1) { _ in
                            Text("Items")
                        }
                    }
                }.listStyle(.grouped)
            }
        }.navigationBarTitle(currentProfileIdStore.didChange.value ?? "unknown")

    }
}

Then profiles view is presented

struct ProfilesView : View {

    @ObjectBinding var profileManager = CurrentProfileIdStore.shared
    @ObjectBinding var profileProvider = ProfileProvider.shared

    var body: some View {

        NavigationView {

            List {
                Section {
                    ForEach(0..<1) { _ in
                        NavigationLink(destination: NewProfile()) {
                            Text("Add Profile")
                        }
                    }
                }


                Section {
                    ForEach(self.profileProvider.didChange.value) { profile in
                        VStack {

                            if profile.id == self.profileManager.didChange.value {
                                Image(systemName: "g.circle").transition(.move(edge: .top))
                            }

                            ProfileCellView(profile: profile)
                            Text(self.profileManager.didChange.value ?? "unknown").font(.footnote).color(.secondary)
                        }
                    }
                }

            }
            .navigationBarTitle("Profiles")
                .listStyle(.grouped)
        }
    }
}

and then we could add a new profile

struct NewProfile: View {

    @State var name: String = ""
    @State var successfullyAdded: Bool = false

    @ObjectBinding var keyboardStatus = KeyboardStatus()
    var profileProvider = ProfileProvider.shared

    var body: some View {

        NavigationView {
                VStack {
                    if self.successfullyAdded  {
                        Text("Successfully added please go back manually :)").lineLimit(nil)
                    }
                    Form {
                        Section(header: Text("Enter name")) {
                            TextField("Enter your name", text: $name)
                        }
                    }
                    if name.count > 3 {
                        Button(action: {
                            self.profileProvider.addProfile(new: Profile(name: self.name, id: UUID().uuidString)) {
                                assert(Thread.isMainThread)
                                self.successfullyAdded = true
                            }
                        }) {
                            Text("Add")
                        }.transition(.move(edge: .bottom))
                    }
                }.padding(.bottom, self.keyboardStatus.didChange.value)
        }.navigationBarTitle("Add Profile")

    }
}

and then there is provider

class ProfileProvider: BindableObject {

    enum Query {
        case all

        var filter: (Profile) -> Bool {
            switch self {
            case .all:
                return { p in true }
            }
        }
    }

    static var samples: [Profile] = []

    static var shared: ProfileProvider = {

        return ProfileProvider(query: .all)
    }()

    var didChange = CurrentValueSubject<[Profile], Never>([])

    private var query: Query?

    private init(query: Query? = nil) {

        self.query = query
        assert(Thread.isMainThread)

        dispatchDidChange()
    }

    func dispatchDidChange() {
        assert(Thread.isMainThread)
        if let valid = self.query {
            didChange.send(ProfileProvider.samples.filter {  valid.filter($0) })
        } else {
            didChange.send([])
        }
    }

    func addProfile(new: Profile, comp: () -> Void) {

        ProfileProvider.samples.append(new)
        comp()
        dispatchDidChange()
    }

keep in mind that multiple views share the same provider subject. so the issue happens when the new profile is added.

self.profileProvider.addProfile(new

there are two more issues, one that when back in mainView the button no longer works. Also when modally presented I don't yet know how to go back manually.

Kevin Renskers
  • 3,331
  • 2
  • 30
  • 66
Arash
  • 1,007
  • 1
  • 9
  • 18
  • #1. It's a warning, right? The important question is - does it work as you expect it to? Remember, `SwiftUI` is barely in beta 3 of version 1! I get those warnings too. Hopefully, this will be improved in future betas. (1) To turn off these warnings, set `OS_ACTIVITY_MODE` to `disable` in your builds. Not necessarily recommending that - but it does get rid of noise. (2) If the behavior is *not* what you'd expect, post some code. So we can help. – dfd Jul 14 '19 at 14:02
  • #2. Could it be a timing issue? If so, look at a question I had answer Friday: https://stackoverflow.com/questions/57008251/index-out-of-range-when-binding-a-slider-value-to-a-nested-array-in-environmento – dfd Jul 14 '19 at 14:03
  • Consider adding some code. A minimal example that shows us the error. in action Otherwise, it's all speculation. See https://stackoverflow.com/help/how-to-ask – kontiki Jul 14 '19 at 14:13
  • 3
    I'm not using SwiftUI but I started seeing that warning in the console after I upgraded to Xcode 11. I'm seeing it on UITableViewControllers that are pretty stock. There were no errors before Xcode 11 and now they're there almost all the time. These are pretty bare bones view controllers. I certainly haven't made any changes to how the table view gets inserted into the view hierarchy. For now I'm choosing to ignore them. The warning is still there in Xcode 11.1. – Murray Sagal Oct 13 '19 at 14:50
  • 1
    I'm also seeing this. Xcode 11.2.1 – Diogo Souza Dec 14 '19 at 14:03
  • Something odd about your code which may cause issues: You put your `NavigationLink` for adding a new profile in a `ForEach` for no reason at all. Whats more, the object you give the `ForEach` is not `Identifiable`. Which means that when your data changes the old NavigationLink shouldn't exist. Remove the `ForEach` it does nothing since it goes through `0..1` (one index). I don't know if this solves your problem but its useless code.. so remove it. – Kubee Dec 20 '19 at 01:22

0 Answers0