简体   繁体   中英

Swift 3 unable to append array of objects, which conform to a protocol, to a collection of that protocol

Below I have pasted code which you should be able to paste into a Swift 3 playground and see the error.

I have a protocol defined and create an empty array of that type. I then have a class which conforms to the protocol which I try to append to the array but I get the below error.

protocol MyProtocol {
    var text: String { get }
}

class MyClass: MyProtocol {
    var text = "Hello"
}
var collection = [MyProtocol]()
var myClassCollection = [MyClass(), MyClass()]
collection.append(myClassCollection)

argument type '[MyClass]' does not conform to expected type 'MyProtocol'

Note that collection += myClassCollection returns the following error:

error: cannot convert value of type '[MyProtocol]' to expected argument type 'inout _'

This was working in earlier versions of Swift.

The only solution I have found so far is to iterate and add each element to the new array like so:

for item in myClassCollection {
    collection.append(item)
}

Any help appreciated, thanks!

EDIT

The solution as show below is:

collection.append(contentsOf: myClassCollection as [MyProtocol])

The real issue is a misleading compiler error when you are missing "as [MyProtocol]"

The compiler error reads:

error: extraneous argument label 'contentsOf:' in call
collection.append(contentsOf: myClassCollection)

This error causes users to remove contentsOf: from the code which then causes the error I first mentioned.

append(_ newElement: Element) appends a single element. What you want is append(contentsOf newElements: C) .

But you have to convert the [MyClass] array to [MyProtocol] explicitly:

collection.append(contentsOf: myClassCollection as [MyProtocol])
// or:
collection += myClassCollection as [MyProtocol]

As explained in Type conversion when using protocol in Swift , this wraps each array element into a box which holds "something that conforms to MyProtocol ", it is not just a reinterpretation of the array.

The compiler does this automatically for a single value (that is why

for item in myClassCollection {
    collection.append(item)
}

compiles) but not for an array. In earlier Swift versions, you could not even cast an entire array with as [MyProtocol] , you had to cast each individual element.

Your trying to append an array when collection is only expecting individual items. For example, changing collection to this compiles:

var collection = [[MyProtocol]]()

here is a way you can go about adding two arrays together:

func merge<T: MyProtocol>(items: inout [T], with otherItems: inout [T]) -> [T] {
return items + otherItems

}

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

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

let combinedArray = merge(items: &myClassCollection, with: &myClassCollection2)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM