Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI cannot set value to @State property in init()

Tags:

swift

I'm probably not understanding the basics and confused, but I couldn't figure out the reason of things below. Ok, what I want to do is pass Int64 to sub view and show Toggles based on bits and set the bits back to parent view like this:

struct ContentView: View {
    @State var bits: Int64 = 5

    var body: some View {
        Form {
            Section {
                SectionView(title: "Section", withBits: $bits)
            } header: {
                Text("Toggle")
            }
        }
    }
}

struct SectionView: View {
    let title: String
    @Binding var bits: Int64

    @State private var first: Bool
    @State private var second: Bool
    @State private var third: Bool

    init(title: String, withBits: Binding<Int64>) {
        self.title = title
        self._bits = withBits
        self.first = self.bits & 1 == 1
        self.second = self.bits & 2 == 2
        self.third = self.bits & 4 == 4
    }

    var body: some View {
        VStack {
            HStack {
                Text(self.title)
            }
            HStack {
                Toggle("Toggle 1", isOn: self.$first)
                    .onChange(of: self.first) { newValue in
                        if newValue {
                            self.bits |= 1
                        }
                        else {
                            self.bits &= ~1
                        }
                    }
            }
            HStack {
                Toggle("Toggle 2", isOn: self.$second)
                    .onChange(of: self.second) { newValue in
                        if newValue {
                            self.bits |= 2
                        }
                        else {
                            self.bits &= ~2
                        }
                    }
            }
            HStack {
                Toggle("Toggle 3", isOn: self.$third)
                    .onChange(of: self.third) { newValue in
                        if newValue {
                            self.bits |= 4
                        }
                        else {
                            self.bits &= ~4
                        }
                    }
            }
        }
        .onChange(of: self.bits) { newValue in
            print("bits: \(bits)")
        }
    }
}

But it won't compile. Saying "'self' used before all stored properties are initialized" error.

So I set default values to all Bool vars like this:

    @State private var first: Bool = false
    @State private var second: Bool = false
    @State private var third: Bool = false

It compiles now but cannot overwrite them in int(), below lines are completely ignored.

        self.first = self.bits & 1 == 1
        self.second = self.bits & 2 == 2
        self.third = self.bits & 4 == 4

Moving above three lines to onAppear() worked. But still I want to know why they won't be overwritten in init() and cleanest way.

like image 245
J.K Avatar asked Oct 18 '25 15:10

J.K


1 Answers

Ok things are clear now. To make a long story short, we can't initialize @State property in init() just doing like this:

@State var bla: Int = 7
init() {
    // This line won't work
    self.bla = 0
}

because "bla" is a just reference to storage like pointer and so setting 0 to it won't reach to real storage in init(), it will be replaced with newly allocated real property with initial value 7 instead. That's the way SwiftUI is designed. Whole discussion is here: https://forums.swift.org/t/assignment-to-state-var-in-init-doesnt-do-anything-but-the-compiler-gened-one-works/35235/37

So the answer what we should do here to achieve to set initial value to @State real property in init() is like this:

self._first = State(wrappedValue: withBits.wrappedValue & 1 == 1)
self._second = State(wrappedValue: withBits.wrappedValue & 2 == 2)
self._third = State(wrappedValue: withBits.wrappedValue & 4 == 4)

Above actually allocate storage and set them to @State properties. By doing this, we can also have initial values.

like image 110
J.K Avatar answered Oct 21 '25 18:10

J.K