MatterGoal
MatterGoal

Reputation: 16430

Optional chaining and Array in swift

Let's take these two simple classes to show my problem:

class Object{  
    var name:String?
    // keep it simple... and useless
}

class TestClass {
    var objects:AnyObject[]? 

    func initializeObjects (){
        objects?.insert(Object(), atIndex:0) // Error
        objects?.insert(Object(), atIndex:1) // Error
        objects?.insert(Object(), atIndex:2) // Error
    }
}

With this implementation I get 3 errors Could not find member 'insert' where I try to add object into the objects array.

Now, if I remove the optional from objects definition and the optional chain in initializeObjects it works with no problem (here the working code)

class Object{
    var name:String?
}

class TestClass {
    var objects:AnyObject[] = AnyObject[]() // REMOVE optional and initialize an empty array

    func initializeObjects (){
        objects.insert(Object(), atIndex:0) // Remove Opt chaining 
        objects.insert(Object(), atIndex:1) // Remove Opt chaining
        objects.insert(Object(), atIndex:2) // Remove Opt chaining
    }
}

I can't understand what is wrong in the first implementation. I thought it checks with objects? if objects is not nil and at this point it adds an element using insert:atIndex:. But I'm probably wrong -.-

Upvotes: 2

Views: 1282

Answers (1)

Sulthan
Sulthan

Reputation: 130122

Arrays in Swift are structs and structs are value types.
Optionals in Swift are actually enums (Optional<T> or ImplicitlyUnwrappedOptional<T>).

When you are unwrapping an optional (implicitly or explicitly) of a value type, what you get is actually a constant copy of the struct. And you can't call mutating methods on a constant struct.

Executing objects?.insert(Object(), atIndex:0) basically means this:

if let tmp = objects {
    tmp.insert(Object(), atIndex:0)
}

As a workaround, you need to assign the unwrapped value to a variable and then assign the variable back to your optional property. That's how value types work.

This is reproducible for any struct, not only Arrays:

struct S {
    var value: Int = 0
}

var varS: S = S()
varS.value = 10 //can be called

let constS: S = S()
constS.value = 10 //cannot be called - constant!

var optionalS: S? = S()
optionalS?.value = 10 //cannot be called, unwrapping makes a constant copy!

//workaround
if optionalS {
    var tmpS = optionalS!
    tmpS.value = 10
    optionalS = tmpS
}

Some relevant discussion here: https://devforums.apple.com/thread/233111?tstart=60

Upvotes: 5

Related Questions