Handling empty optional strings in Swift
String optionals in Swift are a little peculiar. The String?
type sort of has two “invalid” values: nil
and ""
. This can make it cumbersome to handle and ripe for bugs and unexpected behavior.
Take for example a UITextField
’s text
property, which is a String?
. We could write the following code to appropriately alert the user when the text field is left empty, while unwrapping the text string for later use:
if let title = textField.text {
if title.isEmpty {
// Alert: textField is empty!
}
// title String is now unwrapped, non-empty, and ready for use
}
Any combination of guard let
or optional chaining, like textField.text?.isEmpty ?? true
could’ve been used here as well. Either way, the implementation gets messy, and you’ll be dealing with multiple and/or nested conditionals.
Swift extensions to the rescue!
Extensions allow you to add new functionality to an existing class, structure, enumeration, or protocol type. With a where
clause, you can restrict the extension to specific or conforming types.
So how can we apply this to handling the empty string value for a String?
? Let’s define a new computed property nilIfEmpty
in an extension restricted to optional Strings, which transforms an empty String to nil
or returns the original String?
.
extension Optional where Wrapped == String {
var nilIfEmpty: String? {
guard let strongSelf = self else {
return nil
}
return strongSelf.isEmpty ? nil : strongSelf
}
}
Now, this allows us to clean up our above code for detecting when a text field is empty to alert the user, while unwrapping the text string:
guard let title = textField.text.nilIfEmpty else {
// Alert: textField is empty!
return
}
// title String is now unwrapped, non-empty, and ready for use
Pretty, ain’t it?
This extension could also come in handy when used with higher-order functions like map
or flatMap
:
let stuff = ["nate", "", nil, "loves", nil, "swift", ""]let a = stuff.map { $0.nilIfEmpty }
print(a) // [Optional("nate"), nil, nil, Optional("loves"), nil, Optional("swift"), nil]let b = stuff.flatMap { $0.nilIfEmpty }
print(b) // ["nate", "loves", "swift"]
Before you go…
Have you found any other approaches for dealing with empty optional strings? I’d love to hear them. Leave a comment below, or you can reach me on Twitter @nathanwchan.