swiftuiobservabletextfieldbindableios17

Handling data flow in iOS 17 and using observed variables inside a TextField


I'm trying to migrate to iOS 17 using @Observable.

I have a global view model of the app where I call an Exam model with all the values ​​I need inside.

According to Apple guidelines I'm using my view model like this

import Foundation
import Observation

struct Exam: {
    var title: String = ""
}


@Observable
final class Global {
    var exam = Exam()
}

import SwiftUI

@main
struct TestApp: App {
    @State private var model = Global()
    var body: some Scene {
        WindowGroup {
            InfoView()
                .environment(model)
                
        }
    }
}

Next I use my model within the InfoView like this

struct InfoView: View {
   // @Bindable private var model = Global()
    @Environment(Global.self) private var model

    var body: some View {
         VStack {
             TitledField(text: $model.exam.title, isMandatory: true, descriptionLabel: "", placeholder: "")
             .submitLabel(.continue)

          }
     }
}

As you can see in the first custom textField I use $model.exam.title but it doesn't seem to work because it gives me this error

Cannot find '$model' in scope

So I tried with @Bindable but the value that the user inserts in the textField is not read and always returns an empty string where am I going wrong? Has anyone started working on iOS 17?


Solution

  • The following example code with @Bindable works for me on iOS17. When the $model.exam.title is changed in the TextField, the UI is refreshed and displays the changed value.

    import SwiftUI
    import Observation
    
     @main
     struct TestApp: App {
         @State private var model = Global()
          var body: some Scene {
              WindowGroup {
                  InfoView(model: model) // <-- here
              }
          }
     }
     
     struct InfoView: View {
         @Bindable var model: Global // <-- here
         var body: some View {
              VStack {
                  TextField("", text: $model.exam.title).border(.red)
                      .submitLabel(.continue)
                  
                  Text(model.exam.title) // <-- for testing
               }
          }
     }
    
    struct Exam {
        var title: String = "exam-title"
    }
    
    @Observable
    final class Global {
        var exam = Exam()
    }