What exactly is being changed that doesn't cause the view to update? If you assign a new value directly to your state variable, it ought to cause your body to be re-fetched, which will use the new value:
struct RandomizedNumberView: View {
@State var textValue: String = "Press the button..."
var body: some View {
VStack {
Text(textValue) // SwiftUI detects that you use this @State value
Button(action: {
// This updates the @State property, so SwiftUI will invoke `body` again.
self.textValue = "\(Int.random(in: 0...Int.max))"
}) {
Text("Randomize!")
}
}
}
}
If you try that out, you'll see that assigning a new value to the textValue property will cause the view to update. However, in your sample I see you're assigning it from another object:
@State var textValue: String = Controller.message
Now, if the view doesn't update when something else does
Controller.message = "new value"
, then that is expected behavior. String in swift is a value type, so your
textValue
property is taking a
copy of the value, and SwiftUI is monitoring that copy, not the actual value in
Controller.message
. What you want here is a binding or an observed object—exactly which depends on whether
Controller
is a
struct
or a
class
type.
If it's a
struct
, then changing
message
will mean the
Controller
instance has changed, so a binding to Controller itself would work:
struct Controller {
var message: String = "hello"
}
...
struct MyView: View {
@Binding var controller: Controller
var body: some View {
Text(controller.message) // SwiftUI knows you use `controller`, so will update if its contents change
}
}
@State let controller = Controller()
let myView = MyView(controller: $controller)
Alternatively, this view might be in charge of the controller's content, so you'd use
@State:
struct MyView: View {
@State var controller: Controller = Controller()
var body: some View {
Text(controller.message)
}
}
If it's a
class
, you'd make it conform to
ObservableObject
and mark the properties you'll use as
@Published
, after which you have several options. First, you can use it in your view with the
@ObservedObject
attribute, which is the class-type equivalent of
@State
:
import Combine
class Controller: ObservableObject {
@Published var message: String = "hello"
}
...
struct MyView_OwnsController: View {
@ObservedObject var controller = Controller()
var body: some View {
Text(controller.message)
}
}
struct MyView_ReceivesController: View {
@ObservedObject var controller: Controller
var body: some View {
Text(controller.message)
}
}
let owningView = MyView_OwnsController() // creates a Controller instance
let controller = Controller() // something else owns this
let nonOwningView = MyView_ReceivesController(controller: controller)
Alternatively, once you have Controller setup as an
ObservableObject
, you can
bind to its published properties with the
$
prefix:
struct MyView: View {
@Binding var textValue: String
var body: some View {
Text(textValue)
}
}
let controller = Controller()
let view = MyView(textValue: $controller.message)
If, however, your Controller is intended to manage some global state (or at least 'global for this view stack') then you are probably better off using the environment to make it available to any subview that wants it, while retaining all the benefits of the
@ObservedObject
approach:
struct MyView: View {
@EnvironmentObject var controller: Controller
var body: some View {
Text(controller.message) // uses content of environment object, so SwiftUI monitors this & redraws
}
}
struct RootView: View {
var body: some View {
MyView()
}
}
let controller = Controller() // global state somewhere
let rootView = RootView().environmentObject(controller) // makes `controller` available to all subviews
Lastly, I note that you're using
Controller.message
, which looks like a static or class property (usually types are given capitalized names, properties lowercase). If that's the case, then you might be able to use
@State static var String: message
within the Controller definition, but I'm not sure. Usually static/class properties wouldn't be used as mutable state: you'd create a global class that contains the mutable state as instance content, and have a single instance of the class. This is the expected use case for
@EnvironmentObject
, in fact.