4 Replies
      Latest reply on Dec 8, 2019 9:01 PM by Jim Dovey
      jpcoates Level 1 Level 1 (0 points)

        I have a SwiftUI Form() that I use to enter inital data using TextField()s. I use the data to record into Core Data. I want to be able to re-edit the data using a Form() but cannot figure out how to preset the TextField()s.

         

        How can I preset the TextField()s?

        • Re: SwiftUI How to preset a TextField
          Jim Dovey Level 3 Level 3 (240 points)

          You bind the value into a TextField using a binding operator. To get one of those from your NSManagedObject instance/subclass, you need to drop it into a property that uses the @ObservedObject property wrapper. That will then allow you to use $-prefix syntax to obtain a binding to the internal value types held by the object, such as string values.

           

          An off-the-cuff example, not tested:

           

          class MyObject: NSManagedObject {
              @NSManaged var name: String
              @NSManaged var info: String
          }
          
          struct MyView: View {
              @ObservedObject var coreDataObject: MyObject
              
              var body: some View {
                  Form {
                      TextField("Name", text: $coreDataObject.name)
                      TextField("Info", text: $coreDataObject.info)
                  }
              }
          }
            • Re: SwiftUI How to preset a TextField
              jpcoates Level 1 Level 1 (0 points)

              That gives error:

               

              Cannot convert value of type 'Binding<String?>' to expected argument type 'Binding<String>'

                • Re: SwiftUI How to preset a TextField
                  Jim Dovey Level 3 Level 3 (240 points)

                  Ah, that little problem. Yeah, I've been bitten by that one. Happily, I worked out a nice little suite of Binding initializers to work around the issue in different ways.

                   

                  I posted an article about it with a full description of the mechanics, but there are a few different problems, each with their own solution. Frequently these build on the built-in Binding initializer that unwraps an Optional value to return a binding to a non-nil value, or a nil binding:

                   

                  /// Creates an instance by projecting the base optional value to its
                  /// unwrapped value, or returns `nil` if the base value is `nil`.
                  public init?(_ base: Binding<Value?>)

                   

                  For when your value might be nil, and you want to assign a non-nil 'empty' value automatically (e.g. for a non-optional property on a newly-created CoreData object), here's a Binding initializer that initializes that for you:

                   

                  extension Binding {
                      init(_ source: Binding<Value?>, _ defaultValue: Value) {
                          // Ensure a non-nil value in `source`.
                          if source.wrappedValue == nil {
                              source.wrappedValue = defaultValue
                          }
                          // Unsafe unwrap because *we* know it's non-nil now.
                          self.init(source)!
                      }
                  }

                   

                  If you have a CoreData property that is optional, you might want to map nil to and from a given 'none' value, for instance an empty string. This will do that for you:

                   

                  init(_ source: Binding<Value?>, replacingNilWith nilValue: Value) {
                      self.init(
                          get: { source.wrappedValue ?? nilValue },
                          set: { newValue in
                              if newValue == nilValue {
                                  source.wrappedValue = nil
                              }
                              else {
                                  source.wrappedValue = newValue
                              }
                      })
                  }
                  

                   

                  Using these two is fairly straightforward. Assuming we have an @ObservedObject referring to a CoreData object with non-optional "title: String?" and optional "notes: String?" attributes, we can now do this:

                   

                  Form {
                      // Items should have titles, so use a 'default value' binding
                      TextField("Title", text: Binding($item.title, "New Item"))
                      
                      // Notes are entirely optional, so use a 'replace nil' binding
                      TextField("Notes", text: Binding($item.notes, replacingNilWith: ""))
                  }
                  
              • Re: SwiftUI How to preset a TextField
                KMT Level 9 Level 9 (15,245 points)

                If by preset you mean prefill, see this SUI form tutorial, just substitute the hardcoded placeholder text w/your applicable (pulled) CD object strings:

                 

                h ttps://www.appcoda.com/swiftui-form-ui/