Blog

Swift Tip: Non-Empty Optionals

In our Swift Talk backend, we need to display some information that comes from a third-party API. For example, we receive the following data:

struct BillingInfo {
    var name: String
    var vatNumber: String?
}

Here’s a simplified computed property that displays the data:

extension BillingInfo {
    var lines: [String] {
        return [
            "Name: \(name)",
            vatNumber.map { number in
                "Your VAT Number is: \(number)"
            } ?? "No VAT Number."
        ]
    }
}

Note that in the cases where we don’t have a VAT number, we display a different text. Unfortunately, sometimes the API we work with also returns an empty VAT number. As a first try, we dealt with it like this:

return [
    "Name: \(name)",
    vatNumber.map { number in
        number.isEmpty ? "No VAT Number." : "Your VAT Number is: \(number)"
    } ?? "No VAT Number."
]

Of course, the duplication isn’t very nice, so we tried another approach:

return [
    "Name: \(name)",
    vatNumber?.isEmpty == false ? "Your VAT Number is: \(vatNumber!)" : "No VAT Number."
]

Unfortunately, now we have a force-unwrap, which we usually try to avoid. Another variant would be to use an if let, and pull out the formatted VAT string into a variable:

let vatString: String
if let number = vatNumber, !number.isEmpty {
    vatString = "Your VAT Number is: \(number)"
} else {
    vatString = "No VAT Number."
}

return [
    "Name: \(name)",
    vatString
]

The if let variant is safe, and has no duplication, but it involves quite a bit more code. The nice thing about the other two approaches was that the transformations were very local: they happened at the point of usage.

Even though it’s a very minor piece of code, we didn’t really like any of the solutions above. Instead, we wrote a helper that solves this in a more general way:

extension Optional where Wrapped: Collection {
    var nonEmpty: Wrapped? {
        return self?.isEmpty == true ? nil : self
    }
}

The nonEmpty property returns nil when the collection within the optional is empty. This allows us to collapse both checks (for nil and for isEmpty) into one:

return [
    "Name: \(name)",
    vatNumber.nonEmpty.map { "Your VAT Number is: \($0)" } ?? "No VAT Number."
]

We’re almost certain that we aren’t the first to come up with this solution, but if you haven’t seen it yet, we figured it might still be useful.

Our Server-Side Swift Collection covers a range of related topics. We’ll be talking more about our backend rewrite soon.

To support our work, please subscribe, or give a gift.


Stay up-to-date with our newsletter or follow us on Twitter.

Back to the Blog

recent posts