Swift: converting between Arrays of ‘Protocol’ and Arrays of implementing Class

i0S Swift Issue

Question or problem in the Swift programming language:

Consider a protocol and a class that implements it:

protocol MyProtocol {
    var x: Int { get }
}

class MyClass : MyProtocol {
    var x: Int = 5
}

I have an array of type [MyClass] and I wish to assign it to a variable of type [MyProtocol]. However, this causes a error when attempted in Playground (XCode 6.3, Swift 1.2):

var classArr: [MyClass] = [MyClass(), MyClass()]

var protocolArrA: [MyProtocol] = classArr // Causes error: EXC_BAD_INSTRUCTION
var protocolArrB: [MyProtocol] = classArr as [MyProtocol] // Causes error: EXC_BAD_INSTRUCTION
var protocolArrC: [MyProtocol] = [MyProtocol](classArr) // Causes error: Cannot find an initializer for type [MyProtocol[ that accepts an argument list of type [MyClass]

What is the correct way to make this assignment in Swift?

P.S. Interestingly, when using a base class instead of a protocol, the expressions for protocolArrA and protocolArrB work without error.

It is also interesting to note that assigning a newly created instance of [MyClass] also works well:

var protocolArrD: [MyProtocol] = [MyClass]()   // A-OK!

How to solve the problem:

Solution 1:

You can use map to cast each element to the desired type:

protocol MyProtocol {
    var x: Int { get }
}

class MyClass : MyProtocol {
    var x: Int = 5
}

var classArr: [MyClass] = [MyClass(), MyClass()]

var protocolArrA:[MyProtocol] = classArr.map{$0 as MyProtocol}
var andBack:[MyClass] = protocolArrA.map{$0 as! MyClass}

Note, Swift is able to infer all of the array types above, so this can be written more succinctly as:

var classArr = [MyClass(), MyClass()]

var protocolArrA = classArr.map{$0 as MyProtocol}
var andBack = protocolArrA.map{$0 as! MyClass}

Solution 2:

Try this

@objc protocol MyProtocol {
    var x: Int { get }
}

class MyClass : MyProtocol {
    @objc var x: Int = 5
}

Both works if using @objc

 var classArr: [MyClass] = [MyClass(), MyClass()]     
 var protocolArrA: [MyProtocol] = classArr
 var protocolArrB: [MyProtocol] = classArr as [MyProtocol] 

Hope this helps!