How to change a non state variable within a struct in swift ui

import SwiftUI

struct SwiftUIView: View {
    var sample = "Hello"
    var body: some View {
        Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
            .onTapGesture(perform: {
                self.changeText();
            })
    }
    
    func changeText(){
        sample = "Tap worked"
    }
}

struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        SwiftUIView()
    }
}

Above is the code I have typed and I just want to change the text in the variable sample. I don't want it to be a state and just want that variable to execute some logic inside that screen. But I am not sure why I cant change its value. Xcode gives an error

Cannot assign to property: 'self' is immutable

Please any direction how I can change non state variables within a method in swiftui will be greatly appreciated. Thanks in advance.

Why don't you want to make it a @State? To me this looks exactly like a use case for @State.

Hey, yeah. I don't want to perform a UI update while changing this variable. That's why I thought of not using a state.

When changing the variable you need to update whatever is reading it. Looks like UI is not reading it, right? But what does?

You could use a Binding that way you could pass value types and mutate them without updating the UI

Model:

class Model: ObservableObject {
    //sample is deliberately not marked as @Published so it doesn't update the UI
    var sample = "Hello World"
}

ContentView

struct ContentView: View {
    
    var sample: Binding<String>
    //@Binding var sample : String //Available in Xcode 12.5 beta
    //@Binding is more convenient but does the same thing, you would have the convenience not having to write sample.wrappedValue
    
    var body: some View {
        Text(sample.wrappedValue)
            .padding()
            .onTapGesture {
                sample.wrappedValue = "Tap worked"
            }
    }
}

App

@main
struct TestApp: App {
    var sample = "Hello World"
    @StateObject var model = Model()
    
    var sampleBinding: Binding<String> {
        Binding {
            model.sample
        } set: {
            model.sample = $0
            print("did tap")
        }
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView(sample: sampleBinding)
        }
    }
}

In the future, you might want to ask questions like this about how to use Apple's UI frameworks at the Apple developer forums.

1 Like