SwiftUI seems to assume that no one uses optionals anymore.

I'm wrestling with a view that handles a class whose members are mostly optional. This does not appear to be a scenario that SwiftUI envisions. What is the expected approach?

Take this class, for example, which represents a user:

class User : Equatable, Codable, ObservableObject
{
	var ID: String?
	var pw: String?
	var username:  String?
	var firstName: String?
	var lastName: String?
	var EMail: String?
	var phoneNbr: String?
	var avatarURL: String?
	var mediaServiceID: String?
	var validated: Bool = false
...
}

I can't directly show a form to fill this thing out in SwiftUI, because you can't bind text fields to optionals. Most of the published workarounds to that involve using let to create a non-optional variable if the member isn't nil; but this is unworkable because that won't populate a nil member if someone enters text in the text field.

Apple docs talk about custom binding, which would probably work to populate an optional member. That means doing this in the SwiftUI view to set the object's username, for example:

	@State private var		tempUser = User()

	private var	username: Binding<String>
	{
		Binding
		{
			if let theName = tempUser.username
			{
				return theName
			}
			else
			{
				return ""
			}
		}
		set: { newName in
			tempUser.username = newName
		}
	}

But that means setting up one of these verbose methods for every single optional member of every class I want to show in a UI. At that point I might as well just make a shadow structure that's all non-optionals that I can bind directly to. Or just make all the members non-optional and just face the fact that optionals are done with in the age of SwiftUI.

Or is there some succinct approach I'm missing here? Thanks for any insight.

Replies

I've found that the get and set closures for Binding seem to help with optional values. Below, the Binding for the username property uses the nil-coalescing operator (??) to provide an empty string as a default value when tempUser.username is nil. The set block handles setting the tempUser.username property, ensuring it becomes nil if the new value is an empty string.

private var username: Binding<String> {
    Binding(
        get: {
            self.tempUser.username ?? ""
        },
        set: { newName in
            self.tempUser.username = newName.isEmpty ? nil : newName
        }
    )
}
  • Thanks, but that's essentially the same "solution" I posted. It still requires that I implement this nonsense for every member of every class I want to expose in a UI. Totally impractical.

Add a Comment

Given that most of your class attributes are optional, maybe it might make more sense to use a dictionary? Dictionaries don't mind if most of your "attributes" don't exist, and for TextField you can use a default value of "" if the dictionary entry doesn't exist. In the meantime, if you're set on using optional attributes, there's this: StackOverflow