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.