Is there a possibility to only perform an assignment (e.g. to a non-optional property) if the right hand side is not nil? I am looking for a one-line form for:
A single expression with the same effect as your code is
funcThatReturnsOptional().map { object.nonOptionalProperty = $0 }
but your code is definitely better readable.
Here the map() method of Optional is used and the closure is
executed only if the function does not return nil.
There are various ways that aren't too unwieldy
Using ?? :
object.nonOptionalProperty = funcThatReturnsOptional() ?? object.nonOptionalProperty
Using a function :
func setNotNil<T>(inout variable:T, _ value:T?)
{ if value != nil { variable = value! } }
setNotNil(&object.nonOptionalProperty, funcThatReturnsOptional())
Using an operator :
infix operator <-?? { associativity right precedence 90 assignment }
func <-??<T>(inout variable:T, value:T?)
{ if value != nil { variable = value! } }
object.nonOptionalProperty <-?? funcThatReturnsOptional()
Using an extension :
extension Equatable
{ mutating func setNotNil(value:Self?) { if value != nil { self = value! } } }
object.nonOptionalProperty.setNotNil(funcThatReturnsOptional())
The answer from @Sajjon could be improved by using an if let to avoid reflection.
precedencegroup OptionalAssignment { associativity: right }
infix operator ?= : OptionalAssignment
public func ?= <T>(variable: inout T, value: T?) {
if let value = value {
variable = value
}
}
Neither the solution provided by @ch1llb4y nor by @tyler-sheaffer works since generics loses information about optional values. But this can be fixed using the unwrap method below.
precedencegroup OptionalAssignment {
associativity: right
}
infix operator ?= : OptionalAssignment
public func ?= <T>(variable: inout T, value: T?) {
if let unwrapped = unwrap(any: value) ?? nil {
variable = unwrapped
}
}
public func unwrap<T: Any>(any: T) -> T? {
let mirror = Mirror(reflecting: any)
guard mirror.displayStyle == .optional else { return any }
guard let child = mirror.children.first else { return nil }
return unwrap(any: child.value) as? T
}
I have not yet come up with a solution for not having to write ?? nil in unwrap(any: value) ?? nil though.
Okay, actually there is a way to achieve exactly this by introducing a new operator ?=:
infix operator ?= { associativity right precedence 90 }
func ?= <T: Any> (inout left: T, right: T?) {
if let right = right {
left = right
}
}
By using ?= defined as above you can actually assign an optional to a non-optional if the optional has a value inside.