Vikas Bhartiya
Vikas Bhartiya

Reputation: 75

Init for a SwiftUI class with a @Binding var

I have a class which I want to initialize with a Binding var that is set in another View.

The View:

struct CoverPageView: View {
    @State var numberOfNumbers: Int

    var body: some View {
        NavigationView {
            GeometryReader { geometry in
                VStack(alignment: .center, spacing: 0){
                    TextField("Multiplication Up to:", value: self.$numberOfNumbers, formatter: NumberFormatter())
                }
            }
        }
    }

The class which needs to be initialized using the @Binding var $numberOfNumbers:

import SwiftUI

class MultiplicationPractice: ObservableObject {
    @Binding var numberOfNumbers: Int
    var classNumOfNumbers: Int

    init() {
        self.classNumOfNumbers = self.$numberOfNumbers
    }
}

The init statement obviously gives the error that self is not initialized and the instance var is being used to initialize, which is not allowed.

How do I circumvent this? The class needs to be initialized with the number the user enters in the first view. I have written approx. code here so ignore any typos please.

Upvotes: 6

Views: 12232

Answers (2)

Samuel-IH
Samuel-IH

Reputation: 813

Typically you'd initialize MultiplicationPractice in CoverPageView with a starting value:

@ObservedObject var someVar = MultiplicationPractice(NoN:123)

And of course, add a supporting init statement:

class MultiplicationPractice:ObservableObject {
    init(NoN: Int) {
        self.numberOfNumbers = val
    }

and you wouldn't want to wrap your var with @Binding, instead wrap it with @Published:

class MultiplicationPractice:ObservableObject {

@Published var numberOfNumbers:Int
...

In your particular case I would even drop the numberOfNumbers var in your CoverPageView, and instead use the direct variable of the above someVar:

struct CoverPageView: View {
    //removed @State  var numberOfNumbers:Int
    @ObservedObject var someVar = MultiplicationPractice(123)
    
    ...
    
    TextField("Multiplication Upto:", value: self.$someVar.numberOfNumbers, formatter: NumberFormatter())

You'll notice that I passed in the sub-var of the @ObservedObject as a binding. We can do this with ObservableObjects.


Edit

I see now what you're trying to do, you want to pass a binding along across your ViewModel, and establish an indirect connection between your view and model. While this may not be the way I'd personally do it, I can still provide a working example.

Here is a simple example using your struct names:

struct MultiplicationGame {
    @Binding var maxNumber:String
    init(maxNumber: Binding<String>) {
        self._maxNumber = maxNumber
        print(self.maxNumber)
    }
}

class MultiplicationPractice:ObservableObject {
    var numberOfNumbers: Binding<String>
    @Published var MulGame:MultiplicationGame
    
    init(numberOfNumbers: Binding<String> ) {
        self.numberOfNumbers = numberOfNumbers
        self.MulGame = MultiplicationGame(maxNumber: numberOfNumbers)
    }
    
}
struct ContentView: View {
    @State var someText: String
    @ObservedObject var mulPractice: MultiplicationPractice
    init() {
        let state = State(initialValue: "")
        self._someText = state
        self.mulPractice = MultiplicationPractice(numberOfNumbers: state.projectedValue)
    }
    var body: some View {
        TextField("put your text here", text: $someText)
    }
}

Upvotes: 3

Sam
Sam

Reputation: 2446

Okay, I don't really understand your question so I'm just going to list a few examples and hopefully one of them will be what you're looking for.


struct SuperView: some View {
    @State var value: Int = 0

    var body: some View {
        SubView(value: self.$value)
    }
}
struct SubView: View {
    @Binding var value: Int

    // This is the same as the compiler-generated memberwise initializer
    init(value: Binding<Int>) {
        self._value = value
    }

    var body: some View {
        Text("\(value)")
    }
}

If I misunderstood and you're just trying to get the current value, do this

struct SuperView: some View {
    @State var value: Int = 0

    var body: some View {
        SubView(value: self.value)
    }
}
struct SubView: View {
    let value: Int

    // This is the same as the compiler-generated memberwise initializer
    init(value: Int) {
        self.value = value
    }

    var body: some View {
        Text("\(value)")
    }
}

Upvotes: 0

Related Questions