Swift optionals can save the day

Although generics in Swift are somehow limited at this time, there is a nice variance-related feature available when using optionals: you can assign a non-optional to an optional variable, an array of non-optionals to an array of optionals (these two are classics), but similarly you can use a function with a non-optional argument where a closure with optional argument is expected:

// Var
let i: Int? = 1

// Array
let ints: [Int?] = [1, 2]

// Func
func funcWithOptionalInt(i: Int?) {
    print(i)
}
let funcWithInt: (Int) -> Void = funcWithOptionalInt
funcWithInt(1)

This prints out “Optional(1)”, of course. But why do I think this is a such a great feature? Because this thingy can be used in a common situation like this:

Let’s assume that we need to perform an action repeatedly, and the first time it should be performed immediately, i.e. before returning from the current call. Like begin and repeat:

import Foundation

// Support for running this in a playground:
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

func execute(_: Timer) {
    print("Done")
}
execute() // This won't compile: execute requires a Timer argument.
let _ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, 
                                                    block: execute)

As you can see, we just cannot run the execute function one time “manually”, i.e. before scheduling the timer on it. There are a few standard solutions for this, of course, such as to define another execute method without an argument, and call it from the one with the Timer argument; or to call a fire() method immediately after the Timer is initialized.

But I didn’t like any of them too much (actually I’d really hate using the second one.)

And – because optionals work as described above – it seems there is a nicer way to solve the issue: to define the execute function as accepting an optional Timer argument with a default value of nil. That’s all, now everything would work, including that initial execute call, as the block closure required by scheduledTimer accepts any function with an argument of type Timer or an optional Timer! (And that default value helps us when there is no Timer object available yet to be passed to the function at all):

func execute(_: Timer? = nil) {
    print("Done")
}
execute()
let _ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true,
                                                    block: execute)

 

Advertisements

About Sorin Dolha

My passion is software development, but I also like physics.
This entry was posted in Swift and tagged , , , . Bookmark the permalink.

Add a reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s