6

I want to programmatically be able to navigate to a link within a List of NavigationLinks when the view appears (building deep linking from push notification). I have a string -> Bool dictionary which is bound to a custom Binding<Bool> inside my view. When the view appears, I set the bool property, navigation happens, however, it immediately pops back. I followed the answer in SwiftUI NavigationLink immediately navigates back and made sure that each item in the List has a unique identifier, but the issue still persists.

Two questions:

  1. Is my binding logic here correct?
  2. How come the view pops back immediately?
import SwiftUI

class ContentViewModel: ObservableObject {
    @Published var isLinkActive:[String: Bool] = [:]
}

struct ContentViewTwo: View {
    @ObservedObject var contentViewModel = ContentViewModel()
    @State var data = ["1", "2", "3"]
    @State var shouldPushPage3: Bool = true

    var page3: some View {
        Text("Page 3")
            .onAppear() {
                print("Page 3 Appeared!")
        }
    }

    func binding(chatId: String) -> Binding<Bool> {
        return .init(get: { () -> Bool in
            return self.contentViewModel.isLinkActive[chatId, default: false]
        }) { (value) in
            self.contentViewModel.isLinkActive[chatId] = value
        }
    }

    var body: some View {
        return
            List(data, id: \.self) { data in
                NavigationLink(destination: self.page3, isActive: self.binding(chatId: data)) {
                    Text("Page 3 Link with Data: \(data)")
                }.onAppear() {
                    print("link appeared")
                }
            }.onAppear() {
                print ("ContentViewTwo Appeared")
                if (self.shouldPushPage3) {
                    self.shouldPushPage3 = false
                    self.contentViewModel.isLinkActive["3"] = true
                    print("Activating link to page 3")
                }
        }
    }
}

struct ContentView: View {
    var body: some View {
        return NavigationView() {
            VStack {
                Text("Page 1")
                NavigationLink(destination: ContentViewTwo()) {
                    Text("Page 2 Link")
                }
            }
        }
    }
}
Pranav Kasetti
  • 8,770
  • 2
  • 50
  • 71
Zorayr
  • 23,770
  • 8
  • 136
  • 129
  • Hey @Zorayr, the bug got fixed in the new SwiftUI release. Check my latest answer, hope it works for you! – Pranav Kasetti Jun 24 '20 at 01:14
  • check my answer for iOS 13 https://stackoverflow.com/questions/64121813/navigationlink-hides-the-destination-view-or-causes-infinite-view-updates – Vivienne Fosh Oct 03 '20 at 11:08

2 Answers2

5

The error is due to the lifecycle of the ViewModel, and is a limitation with SwiftUI NavigationLink itself at the moment, will have to wait to see if Apple updates the pending issues in the next release.

Update for SwiftUI 2.0:

Change:

@ObservedObject var contentViewModel = ContentViewModel()

to:

@StateObject var contentViewModel = ContentViewModel()

@StateObject means that changes in the state of the view model do not trigger a redraw of the whole body.

You also need to store the shouldPushPage3 variable outside the View as the view will get recreated every time you pop back to the root View.

enum DeepLinking {
    static var shouldPushPage3 = true
}

And reference it as follows:

if (DeepLinking.shouldPushPage3) {
    DeepLinking.shouldPushPage3 = false
    self.contentViewModel.isLinkActive["3"] = true
    print("Activating link to page 3")
}

The bug got fixed with the latest SwiftUI release. But to use this code at the moment, you will need to use the beta version of Xcode and iOS 14 - it will be live in a month or so with the next GM Xcode release.

Pranav Kasetti
  • 8,770
  • 2
  • 50
  • 71
  • 1
    Thanks for following through so quickly. – Zorayr Jun 24 '20 at 01:24
  • No worries. Let me know if you have any issues after you test it! – Pranav Kasetti Jun 24 '20 at 01:28
  • I wasted so much time on this and eventually had to kill all `UINavigationView` and `UINavigationLink` and replace with `UINavigationController`. Never again `UINavigationView` until maybe a year from now, the effort is not worth the result and it's going to take time before people adopt 14.0 – Zorayr Jun 24 '20 at 02:14
  • 1
    It's not really ready for production code unless you're Apple – Pranav Kasetti Jun 24 '20 at 02:20
  • on button click adding item to an array from viewmodel, view pops back immediately. Any idea why this is happening? – Mac_Play Oct 03 '21 at 11:13
3

I was coming up against this problem, with a standard (not using 'isActive') NavigationLink - for me the problem turned out to be the use of the view modifiers: .onAppear{code} and .onDisappear{code} in the destination view. I think it was causing a re-draw loop or something which caused the view to pop back to my list view (after approx 1 second). I solved it by moving the modifiers onto a part of the destination view that's not affected by the code in those modifiers.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Phi7
  • 33
  • 3