How do I store previous state

Is it possible to store previous state in a swiftUI view, and if so, how?


I have a view that can take an optional value as an input. If this value is nil, then I would like the view to change color (to show something is wrong) and show the previous valid value it had.


Below is an example showing what I hope to achieve.


The IntView takes an optional Int as an input. It will show "Not a int." in red if it is nil, but in that case, I would like to show the previous valid value, but in red.


In the example given, I have a simple ContentView with a TextField in which you can type an int value. The conversion of string to Int (on line 4) will return nil if the string typed in the TextField is not a valid integer.


I've tried doing it with state in my IntView but I cannot update the state when the body is computed.


struct ContentView: View {
  @State var intAsString: String = ""
  var body: some View {
    let intValue = Int(intAsString)
    
    return VStack {
      IntView(value: intValue)
      TextField("", text: $intAsString)
    }
  }
}

struct IntView: View {
  var value: Int?
  
  var body: some View {
    let foreColor: Color
    let text: String
    
    if value == nil {
      foreColor = Color.red
      text = "Not a int."
    } else {
      foreColor = Color.primary
      text = "\(value!)"
    }
    return Text(text).foregroundColor(foreColor)
  }
}

Replies

This is how I would do it in Swift.

Could it be adapted for SwiftUI syntax ? (I have not installed Catalina, so cannot test it yet)


Define another var in struct

var oldValue: Int?

with a convenience init:


    init(value: Int) {
        val = value
    }


And set this oldValue when the new is changed


    if value == nil { 
      foreColor = Color.red 
      text = "Not a int." 
    } else { 
      foreColor = Color.primary 
      text = "\(value!)" 
     oldValue = value
    }

If only it could be that simple 🙂


IntView cannot be mutated inside body and even if it could, it would not make sense because you would mutate the state of a swiftUI view inside of the method that updates the view based on state.


I tried to see what would happen if I used a @State variable but that (correctly so) gives a runtime warning sayng that you are modifying state inside the body method (runtime warning as opposed to compile-time error using your suggestion).


It seems like the only way to do this is to move the previous state out of the swiftUI view. The problem I have with that, is that I want the operation of my view to be transparent to the application.

Thanks for feedback.


That confirms that SwiftUI is really powerful but we have to trade some flexibility…

Isn't there a way in SwiftUI to save oldValue to userDefaults ?

Or create another hidden textField that contains the oldValue, update when newvalue is valid ?


So far, I still prefer UIKit and storyboards (personal opinion).

The golden rule of swiftUI is that the view is a function of its state. That comes with various advantages, and makes it much easier to reason about what is going on.


I am sure there is a solution to my problem that I just have not found yet. I still hope that someone reads this, and gives me some pointers on how this may be done.


My general feeling is that swiftUI is a huge step forward, but it takes some getting use to, and the framework is not without its problems yet.


By the way, if you wanted to play with it, but have not installed Catalina yet, you can do so by just installing the latest Xcode, and using the iOS simulator. You cannot play with the live preview feature in Xcode, but you can compile your iOS swiftUI code, and experiment with it at run time.