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.
Top comments (2)
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