Add ifPresent to Swift Optionals

20 Nov, 2015
Xebia Background Header Wave
In my previous post I wrote a lot about how you can use the map and flatMap functions of Swift Optionals. In this one, I'll add a custom function to Optionals through an extension, the ifPresent function. [code language="obj-c"] extension Optional { public func ifPresent(@noescape f: (Wrapped) throws -> Void) rethrows { switch self { case .Some(let value): try f(value) case .None: () } } } [/code] What this does is simply execute the closure f if the Optional is not nil. A small example: [code language="obj-c"] var str: String? = "Hello, playground" str.ifPresent { print($0) } [/code] This works pretty much the same as the ifPresent method of Java optionals.

Why do we need it?

Well, we don't really need it. Swift has the built-in language feature of if let and guard that deals pretty well with these kind of situations (which Java cannot do). We could simply write the above example as follows: [code language="obj-c"] var str: String? = "Hello, playground" if let str = str { print(str) } [/code] For this example it doesn't matter much wether you would use ifPresent or if let. And because everyone is familiar with if let you'd probably want to stick with that.

When to use it

Sometimes, when you want to call a function that has exactly one parameter with the same type as your Optional, then you might benefit a bit more from this syntax. Let's have a loot at that: [code language="obj-c"] var someOptionalView: UIView? = ... var parentView: UIView = ... someOptionalView.ifPresent(parentView.addSubview) [/code] Since addSubview has one parameter of type UIView, we can immediately pass in that function reference to the ifPresent function. Otherwise we would have to write the following code instead: [code language="obj-c"] var someOptionalView: UIView? = ... var parentView: UIView = ... if let someOptionalView = someOptionalView { parentView.addSubview(someOptionalView) } [/code]

When you can't use it

Unfortunately, it's not always possible to use this in the way we'd like to. If we look back at the very first example with the print function we would ideally write it without closure: [code language="obj-c"] var str: String? = "Hello, playground" str.ifPresent(print) [/code] Even though print can be called with just a String, it's function signature takes variable arguments and default parameters. Whenever that's the case it's not possible to use it as a function reference. This becomes increasingly frustrating when you add default parameters to existing methods after which your code that refers to it doesn't compile anymore. It would also be useful if it was possible to set class variables through a method reference. Instead of: [code language="obj-c"] if let value = someOptionalValue { self.value = value } [/code] We would write something like this: [code language="obj-c"] someOptionalValue.ifPresent(self.value) [/code] But that doesn't compile, so we need to write it like this: [code language="obj-c"] someOptionalValue.ifPresent { self.value = $0 } [/code] Which isn't really much better that the if let variant. (I had a look at a post about If-Let Assignment Operator but unfortunately that crashed my Swift compiler while building, which is probably due to a compiler bug)


Is the ifPresent function a big game changer for Swift? Definitely not. Is it necessary? No. Can it be useful? Yes.

Explore related posts