2

Sorry for the title and length I struggled to summerise

I have a class of @ObserableObject @Published data which I want to change and use the new data on a second page accessed via NavigationLink

class Data: ObservableObject {
     @Published var number = 5

          var correctNumber: Int {
         number + 1
     }
}

I then have a Picker to choose/change the number

Picker("What times tables do you want to test your knowledge on?", selection: $data.number) {
      ForEach(0 ..< data.numberRange.count) {
         Text("\(self.data.numberRange[$0])")
      }
}

This works as expected when I have the Text show the corrected number on the main screen and changes/updates when the picker is changed

Text ("Number = \(data.correctNumber)")

I then move to a second screen with NavigationLink

NavigationLink(destination: QuestionView()) {
    Text("Start")
}

On the second screen, I set the @ObservedObject and have the text display again.

struct QuestionView: View {
    @ObservedObject var data = Data()
        
var body: some View {
    Text("Number = \(self.data.correctNumber)")
}

When pressing Start it only shows the number as 5 + 1 and not the number it changed to using the picker.

I have tried;

  • Not using self. but there was no noticeable difference
  • Swapping to a binding $ and got the following issues; Cannot assign to property: 'correctNumber' is a get-only property and Instance method 'appendInterpolation' requires that 'Binding' conform to '_FormatSpecifiable'
  • Adding @Published to correctNumber but I cannot add that to a computed state

Now I am out of ideas, can anyone help a noob, please

PS I am sorry if the terminology is wrong, feel free to correct me so I learn for next time

SD449
  • 97
  • 1
  • 7

2 Answers2

1

You are initializing the Data observed object all over again so it is actually expected for the view to always display 5 + 1.

In my opinion you don't really need the computer property in the given case so you can ditch it. My suggestion is instead pass the number property as binding and simply display it incremented by 1 in your QuestionView. Another approach would be to move the computed property correctNumber inside your QuestionView but still pass the number property as binding.

Here is how those scenarios would look in your case:

  • Directly displaying the value incremented by 1
class Data: ObservableObject {
     @Published var number = 5         
}

NavigationLink(destination: QuestionView(number: $data.number)) {
    Text("Start")
}

struct QuestionView: View {
    @Binding var number : Int
        
    var body: some View {
        Text("Number = \(self.number + 1)")
    }
}
  • and very similarly, if you decide to move your computed property just change your QuestionView's Text field to use it instead like this:
struct QuestionView: View {
    @Binding var number : Int
    
    var correctNumber: Int {
         self.number + 1
    }
        
    var body: some View {
        Text("Number = \(self.correctNumber)")
    }
}
bde.dev
  • 729
  • 9
  • 9
  • Judging by the sample code in the question it may be enough to just pass a number to the QuestionView. However, this doesn't allow any flexibility when you decide to use another property of `Data` in this view. It may be better to pass the `Data` object to the QuestionView. I agree with the rest. – pawello2222 Jul 04 '20 at 21:39
  • Thank you @bde.dev this was a great answer especailly as it explained why my code was behaving as it was. – SD449 Jul 05 '20 at 09:41
1

All other answers are correct. You'd need to choose what is best for your scenario.

I believe you don't need an @EnvironmentObject (it will become globally available for all your views in the hierarchy). And passing just a number may be enough now but it's not scalable (what if you want to use another property of Data in the QuestionView?).

I recommend passing the Data object, but as an @ObservedObject:

NavigationLink(destination: QuestionView(data: data)) {
    Text("Start")
}
struct QuestionView: View {
    @ObservedObject var data: Data
        
    var body: some View {
        Text("Number = \(self.data.correctNumber)")
    }
}

This way changes to your code will be minimal. And if you plan to use correctNumber in the QuestionView only - you can safely move it there *as suggested in another answer).

pawello2222
  • 46,897
  • 22
  • 145
  • 209