Swift: converting between Arrays of 'Protocol' and Arrays of implementing Class

1.7k Views Asked by At

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!
2

There are 2 best solutions below

0
On BEST ANSWER

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}
1
On

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]