Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 2: is there a short syntax to conditionally set a non-optional variable from an optional variable?

Sometimes I find myself writing swift 2 code like this:

class Test {
    var optionalInt: Int?
    var nonOptionalInt: Int = 0
    func test() {
        if let i = optionalInt {
            nonOptionalInt = i
        }
        // do more stuff
    }
}

where the problematic part is this:

if let i = optionalInt {
    nonOptionalInt = i
}

In words: if optionalInt has a value, assign it to the variable nonOptionalInt, else do nothing.

Is there an equivalent way in swift 2 to express this in a elegant single line without adding an intermediate variable with if let i?

Edit

after contemplating the first answers...

Obviously there is an equivalent way

if optionalInt != nil {
    nonOptionalInt = optionalInt!
}

Ternary operators ? : are not equivalent as they may trigger a didSet which the original code does not (good if this is a intended side effect)

The most elegant answer so far appears to be

nonOptionalInt = optionalInt ?? nonOptionalInt

it may also trigger a didSet like the ternary operator and therefore is not equivalent (also good if this is intended).

I think my wish to Apple would be something like

nonOptionalInt = optionalInt ?? 

or

nonOptionalInt ??= optionalInt
like image 231
Gerd Castan Avatar asked Aug 15 '15 21:08

Gerd Castan


3 Answers

What you are looking for is the nil coalescing operator.

You would use it like so in this case:

nonOptionalInt = optionalInt ?? nonOptionalInt

EDIT

re: your last edit to your question:

"I think my wish to Apple would be something like nonOptionalInt ??= optionalInt"

...why wish when you can build it? :)

I haven't thought about it enough to decide whether I would recommend something like this for the general case, but if it helps in your situation you can create a ??= operator that does what you ask like this:

infix operator ??= {}

func ??= <T>(inout lhs: T, rhs: T?) -> Void {
  guard let value = rhs else { return }
  lhs = value
}

then, to see it in action:

let optionalNil: String? = nil,
  optionalValue: String? = "optionalValue",
  implicitlyUnwrappedNil: String! = nil,
  implicitlyUnwrappedValue: String! = "implicitlyUnwrappedValue"
var a = "a", b = "b", c = "c", d = "d"

a ??= optionalNil               // "a"
b ??= optionalValue             // "optionalValue"
c ??= implicitlyUnwrappedNil    // "c"
d ??= implicitlyUnwrappedValue  // "implicitlyUnwrappedValue"

(note that you would add this definition at top-level scope, outside of the definition of any class, struct, or other type)

like image 84
fqdn Avatar answered Oct 15 '22 21:10

fqdn


Swift 4.0 Version build from the 2.0er answer

/// Optional Assignment, assigns if value exists
infix operator ?=: AssignmentPrecedence

func ?=<T>(lhs: inout T, rhs: T?)  {

    guard let value = rhs else { return }
    lhs = value
}

// Playground Test

var dummy = "old"
var opt :String?

dummy ?= opt // leaves old

opt = "new"
dummy ?= opt // value becomes new 
like image 23
JGS Avatar answered Oct 15 '22 23:10

JGS


Is there an equivalent way in swift 2 to express this in a elegant single line without adding an intermediate variable with if let i?

You can omit the intermediate value if you want to:

if optionalInt != nil {
    nonOptionalInt = optionalInt!
}

And you can obviously put that all on a single line if you want:

if optionalInt != nil { nonOptionalInt = optionalInt! }

But you can also use the ternary operator if you prefer:

nonOptionalInt = (optionalInt != nil) ? optionalInt! : 0

where the 0 is just a default value to use in the case where optionalInt == nil. If you don't want to change nonOptionalInt if optionalInt == nil then just use nonOptionalInt:

nonOptionalInt = (optionalInt != nil) ? optionalInt! : nonOptionalInt

Note that the ! in optionalInt! implicitly unwraps the optional value -- it's the way you tell the compiler that you know that optionalInt isn't nil so it's okay to assign to a non-optional variable.

like image 20
Caleb Avatar answered Oct 15 '22 22:10

Caleb