Live changes of depending State values

Hi all!

I have an issue how to handle depending data with live changes via .onReceive action. To show my issue I made a simple example with decimal/hex calculation in Swift Playground on macOS 26:

import SwiftUI
import Combine

@Observable
class AppData {
    @Published var baseValue : Int = 0
    
    var intValue: String {
        get { String(baseValue) }
        set {
            guard let v = Int(newValue) else { fatalError("Can't set int!")}
            baseValue = v
        }
    }

    var hexValue : String {
        get { String(baseValue, radix: 16).uppercased() }
        set {
            guard let v = Int(newValue, radix: 16) else { fatalError("Can't set hex!")}
            baseValue = v
        }
    }
}

struct ContentView: View {
    @State var model = AppData()
    
    var body: some View {
        VStack {    
            HStack {    
                Text("Decimal Value")
                TextField("Decimal Value", text: $model.intValue)
                    .textFieldStyle(.roundedBorder)
                    .onReceive(Just(model.intValue)) { newValue in
                        let allowedCharacters = "0123456789"
                        let filtered = newValue.filter { allowedCharacters.contains($0) }
                        if let v = Int(filtered) {
                            model.baseValue = v 
                        } else {
                            fatalError("Eeek! (Decimal Value)")
                        }
                    }
            }
            HStack {    
                Text("Hex Value")
                TextField("Hex Value", text: $model.hexValue)
                    .textFieldStyle(.roundedBorder)
                    .onReceive(Just(model.hexValue)) { newValue in
                        let allowedCharacters = "0123456789ABCDEFabcdef"
                        let filtered = newValue.filter { allowedCharacters.contains($0) }
                        if let v = Int(filtered, radix: 16) {
                            model.baseValue = v 
                        } else {
                            fatalError("Eeek! (Hex Value)")
                        }
                    }
            }
        }
    }
}

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Somehow the TextField "Decimal Value" does no longer accept input after the "Hex Value" field was added. What do I miss here?

Answered by GreatOm in 902470022

I got it now working by using no longer calculated properties but an update function. Here is my working code (Swift Playground on macOS 26):

import SwiftUI
import Combine

@Observable
class AppData : ObservableObject {
    var baseValue : Int = 0
    var intValue  : String = "0"
    var hexValue  : String = "0"
    
    func updateValue(_ v : Int) {
        //if self.baseValue == v { return } // no short cut here so that illegal chars are filtered
        self.objectWillChange.send()
        self.baseValue = v
        self.intValue  = String(v)
        self.hexValue  = String(v, radix: 16).uppercased()
    }
}

struct ContentView: View {
    @State var model = AppData()
    
    var body: some View {
        VStack {
            Text("Base Value: \(model.baseValue)")
            HStack {
                Text("Decimal Value")
                TextField("Decimal Value", text: $model.intValue)
                    .textFieldStyle(.roundedBorder)
                    .onReceive(Just(model.intValue)) { newValue in
                        let allowedCharacters = "0123456789"
                        let filtered = newValue.filter { allowedCharacters.contains($0) }
                        if let v = Int(filtered) {
                            model.updateValue(v)
                        }
                    }
            }
            HStack {
                Text("Hex Value")
                TextField("Hex Value", text: $model.hexValue)
                    .textFieldStyle(.roundedBorder)
                    .onReceive(Just(model.hexValue)) { newValue in
                        let allowedCharacters = "0123456789ABCDEFabcdef"
                        let filtered = newValue.filter { allowedCharacters.contains($0) }
                        if let v = Int(filtered, radix: 16) {
                            model.updateValue(v)
                        }
                    }
            }
        }
        .padding()
    }
}

I cannot compile your code.

I get the following error (in Swift generated code):

Init accessor cannot refer to property '__baseValue'; init accessors can refer only to stored properties

Generated code:

    @storageRestrictions(initializes: __baseValue)
    init(initialValue) {
      __baseValue = initialValue
    }

Which version of Xcode and Swift do you use ?

I used Swift Playground on macOS 26 for the example. There is something wrong. I look into this and respond later.

Accepted Answer

I got it now working by using no longer calculated properties but an update function. Here is my working code (Swift Playground on macOS 26):

import SwiftUI
import Combine

@Observable
class AppData : ObservableObject {
    var baseValue : Int = 0
    var intValue  : String = "0"
    var hexValue  : String = "0"
    
    func updateValue(_ v : Int) {
        //if self.baseValue == v { return } // no short cut here so that illegal chars are filtered
        self.objectWillChange.send()
        self.baseValue = v
        self.intValue  = String(v)
        self.hexValue  = String(v, radix: 16).uppercased()
    }
}

struct ContentView: View {
    @State var model = AppData()
    
    var body: some View {
        VStack {
            Text("Base Value: \(model.baseValue)")
            HStack {
                Text("Decimal Value")
                TextField("Decimal Value", text: $model.intValue)
                    .textFieldStyle(.roundedBorder)
                    .onReceive(Just(model.intValue)) { newValue in
                        let allowedCharacters = "0123456789"
                        let filtered = newValue.filter { allowedCharacters.contains($0) }
                        if let v = Int(filtered) {
                            model.updateValue(v)
                        }
                    }
            }
            HStack {
                Text("Hex Value")
                TextField("Hex Value", text: $model.hexValue)
                    .textFieldStyle(.roundedBorder)
                    .onReceive(Just(model.hexValue)) { newValue in
                        let allowedCharacters = "0123456789ABCDEFabcdef"
                        let filtered = newValue.filter { allowedCharacters.contains($0) }
                        if let v = Int(filtered, radix: 16) {
                            model.updateValue(v)
                        }
                    }
            }
        }
        .padding()
    }
}

Yes, it works properly.

One point:

It would be better to have

    var intValue  : String = ""
    var hexValue  : String = "" 

for placeholders to be displayed at start.

If everything OK, don't forget to close the thread by marking the answer as correct.

Live changes of depending State values
 
 
Q