Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!
like image 217
Hatchmaster J Avatar asked Jun 08 '15 09:06

Hatchmaster J


1 Answers

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}
like image 153
vacawama Avatar answered Nov 05 '22 04:11

vacawama