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)
Conclusion
Is the
ifPresent function a big game changer for Swift?
Definitely not. Is it necessary?
No. Can it be useful?
Yes.