NavigationLink and the linked Views

Hi,


say I have a List. Each entry in the List is a NaviagtionLink, which has the same View (NextView) as the destination. I want NextView to display a different random number each time I navigate to that view and I want give this number as a parameter to NextView. How would I do that?

(Of course, this is a contrived example to illustrate the issue, what I really want to do is inject a newly generated CoreData child managed context each time a navigation happens.)


(Please see the code sample at the end of the post.


If I just use NavigationLink(destination: NextView(n: Int.random(in: 0 ... 9))) it will always show the same number, because NextView[...] will be evaluated only, if the "outer" view body is evaluated.

NavigationLink(destination: NextView(n: Int.random(in: 10 ... 20)), isActive: $showEdit) somehow retriggers the "outer" view body evaluation, so I get a new random number each time. But this seems like a hack.

Ideally there would be a way, that evaluate NextView at the point in time when the navigation happens, but I can't think of a way to do this.


Thanks in advance for any ideas, pointers, hints on what's wrong and how it should be done right ;-)


Cheers, Michael


import SwiftUI

struct ContentView: View {
  @State var showEdit = false

  var body: some View {
    NSLog("ContentView Body")
    return
      NavigationView {
        List {
          NavigationLink(destination: NextView(n: Int.random(in: 10 ... 20)), isActive: $showEdit) {
            Text("With binding")
          }
          NavigationLink(destination: NextView(n: Int.random(in: 0 ... 9))) {
            Text("Without binding")
          }
        }
      }
  }
}

struct NextView: View {
  var n: Int

  init(n: Int) {
    self.n = n
    NSLog("init: \(n)")
  }

  var body: some View {
    NSLog("Next View body")
    return
      VStack {
        Text("Next View - What else")
        Text("** \(n) **")
      }
  }
}

func getNew(number: Int) -> TheObject {
  let obj = TheObject(number: number)
  NSLog("getNew \(obj.number)")
  return obj
}

class TheObject: ObservableObject {
  @Published var number: Int

  init(number: Int) {
    self.number = number
  }

  deinit {
    NSLog("Deinit \(self.number)")
  }
}

hi,


i'm still working to get up to speed on SwiftUI, but i'd say that when the body of the ContentView is evaluated, that's when the random number is computed as the value for the parameter in NextView.


segueing (if i'm allowed to say that in SwiftUI) to SecondView from the "without binding" link and then coming back to ContentView does not have any obvious effect on the body of ContentView, since no state variable has been changed. nothing triggers a recomputation of the random number.


it's curious that the "with binding" link causes this, since showEdit has not been changed, but that's for someone else to think about. it's probably just following a rubric to be sure to reevaluate anything that depends on a @State variable, in case it's been changed underneath it (it's a struct, not a class that's an ObservableObject)


nevertheless, my suggestion would be: use the .onAppear() modifier to create a new random number and setting a @State variable to its result. something like this:


struct ContentView: View {

  @State private var nextInteger: Int = 0
  func newRandomInteger() {
    nextInteger = Int.random(in: 0 ... 9)
  }

  var body: some View {
    NavigationView {
      List {
        NavigationLink(destination: NextView(n: nextInteger)) {
          Text("List Item")
        }
        .onAppear(perform: newRandomInteger)
      }
    }
  }
}


i think this works.


hope this helps,

DMG

Hi DMG,


thanks for the reply. That would definitely work for the concrete code example I gave, but unfortunately not in my real use case, where I need to inject that value from outside of NextView.

(The real scenario is passing a child NSManagedObjectContext to "NextView" right before the navigation takes place.)


Thanks again for the reply, Michael

NavigationLink and the linked Views
 
 
Q