Display Binding<String> inside of SwiftUI Text element

I'm trying to display the value of a Binding<String> inside of a Text() element in SwiftUI.


A brief example would be:

struct ContentView: View {
     @State var textValue: String = Controller.message

     var body: some View {
          Text($textValue)
     }
}


The error I am running into is:


Initializer 'init(_:)' requires that 'Binding<String>' conform to 'StringProtocol'



This seems like a very obvious problem that I just don't understand. Any help would be appreciated.


Thanks.

Matt

change with


Text(textValue)


Does it work now ?

That doesn't seem to show the new value if the @State var is changed.

You're right, that will not, but you did not specify that.


Do you want to change the content of text ?


Then, why don't you use

TextField(text: $textValue)

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.
Try .wrappedValue

struct ContentView: View {
   @State var textValue: String = "HELLO"

   var body: some View {
    Text($textValue.wrappedValue)
   }
}
Display Binding&lt;String&gt; inside of SwiftUI Text element
 
 
Q