The idiom for dealing with optionals in Swift seems excessively verbose, if all you want to do is provide a default value in the case where it's nil:
if let value = optionalValue {
// do something with 'value'
} else {
// do the same thing with your default value
}
which involves needlessly duplicating code, or
var unwrappedValue
if let value = optionalValue {
unwrappedValue = value
} else {
unwrappedValue = defaultValue
}
which requires unwrappedValue
not be a constant.
Scala's Option monad (which is basically the same idea as Swift's Optional) has the method getOrElse
for this purpose:
val myValue = optionalValue.getOrElse(defaultValue)
Am I missing something? Does Swift have a compact way of doing that already? Or, failing that, is it possible to define getOrElse
in an extension for Optional?
Update
Apple has now added a coalescing operator:
var unwrappedValue = optionalValue ?? defaultValue
The ternary operator is your friend in this case
var unwrappedValue = optionalValue ? optionalValue! : defaultValue
You could also provide your own extension for the Optional enum:
extension Optional {
func or(defaultValue: T) -> T {
switch(self) {
case .None:
return defaultValue
case .Some(let value):
return value
}
}
}
Then you can just do:
optionalValue.or(defaultValue)
However, I recommend sticking to the ternary operator as other developers will understand that much more quickly without having to investigate the or
method
Note: I started a module to add common helpers like this or
on Optional
to swift.
As of Aug 2014 Swift has coalescing operator (??) that allows that. For example, for an optional String myOptional you could write:
result = myOptional ?? "n/a"
if you wrote:
let result = optionalValue ?? 50
and optionalValue != nil
then result
will be optional
too and you will need unwrap it in future
But you can write operator
infix operator ??? { associativity left precedence 140 }
func ???<T>(optLeft:T?, right:T!) -> T!
{
if let left = optLeft
{
return left
}
else { return right}
}
Now you can:
let result = optionalValue ??? 50
And when optionalValue != nil
then result
will be unwraped
The following seems to work
extension Optional {
func getOrElse<T>(defaultValue: T) -> T {
if let value = self? {
return value as T
} else {
return defaultValue
}
}
}
however the need to cast value as T
is an ugly hack. Ideally, there should be a way to assert that T
is the same as the type contained in the Optional. As it stands, type inferencing sets T
based on the parameter given to getOrElse, and then fails at runtime if this does not match the Optional and the Optional is non-nil:
let x: Int?
let y = x.getOrElse(1.414) // y inferred as Double, assigned 1.414
let a: Int? = 5
let b: Double = a.getOrElse(3.14) // Runtime failure casting 5 to Double
T
for the method. That actually overrides the existing T
from Optional. You can just do: func getOrElse(defaultValue: T) -> T
then T refers to the real value type of the optional and you don't have to type check it
T
in that? (beyond simply assuming based on convention)
Optional
inside Xcode, you will see that it is defined as enum Optional<T>
If you are trying to do this with a String you can do this ..
string1 = string2.isEmpty ? "Default Value":string2
Success story sharing
unwrappedValue
is still the optional type for this case in Swift 1.2:var unwrappedValue = optionalValue ? optionalValue! : defaultValue
(xcode 6 beta 4). Has this changed???
default type inference behavior is that it returns an optional, which is not what you want typically. You can declare the variable as non-optional and this works nicely.