Four tips to level up your Swift

How to unwrap optionals in a loop. Utilize @autoclosures, cleaner range checking and easy randomness.

Published: May 10, 2020
I want a Press Kit

Swift has number of advanced features that can help you write more concise and prettier code.

You probably won’t use these daily but when you do, you will be happy that you know them.

case let to unwrap optionals in for loop

Sometimes you have collection that may contain nil values. Instead of guard or if let inside the loop you can do the following:

for case let item? in items

Note the ? after item. Now you will get only non-nil items and they will be non-optional.

Cleaner range checking

Want to find out whether number falls in particular range? Instead of the usual two ifs and && you can use Range type and its contains method.

Range(min...max).contains(num)

Even neater? Use the tilda operator like so:

min...max ~= num

Easy randomness

Don’t forget, that Swift has so many random methods. For example you can use Bool.random() to simulate 50/50 chance. There are same methods on Int, Double even CGFloat.

@autoclosure

This is another of these features you may use once every few months but when you do, you will be glad it is there. What it does? It is basically syntactic sugar to hide closure behind traditional parameter.

Broadly there are two very useful use cases.

  • Always getting the current value of value type
  • Only using function argument if really needed

Regarding the first use case. I found @autoclosure helpful when designing data model for table view sections that can have variable number of rows but may not be backed by traditional arrays. If you set model rowCount with @autoclosure in your section model, you can change it and it will be reflected automatically on table view reload.

If you were to use just Int, the inial value would get copied and subsequent changes to your section model would not be reflected in table view.

For the second use case, remember that even if your function takes simple argument like a String in runtime, this string can be produced by expensive function that may do some heavy work before returning this string.

Logging is good example, because you may wish to log additional information only in debug mode. With @autoclosure the expensive method mentioned above would be executed only if the method body needed it to log.

func log(_ message: @autoclosure () -> String) {
    #if DEBUG
    print(message())
    #endif
}

Built-in assert works this way. It only works in debug mode and thanks to its parameters being @autoclosure nothing expensive can ever get executed in release mode.

Uses: Xcode 12 & Swift 5.3

Filip Němeček profile photo

WRITTEN BY

Filip Němeček @nemecek_f@iosdev.space

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀