loading...
Cover image for 4 tips to level up your Swift

4 tips to level up your Swift

nemecek_f profile image Filip Němeček ・2 min read

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.

Posted on Jan 19 by:

nemecek_f profile

Filip Němeček

@nemecek_f

Primarily iOS developer, I also like Django and Python. And dabble with JavaScript occasionally. Love reading and coffee.

Discussion

markdown guide
 

So, does that mean, if the code has a statement like this somewhere:
log(fetchString())

then this statement will kind of "not get executed" at all, when not in debug mode?

 

Yes, only the call to the log method will but the body wont and since message is a closure then the fetchString() would be executed only in the method body and not in the method call