Reputation: 3709
I'm getting a compiler error at the as?. Type 'Type' does not conform to protocol 'AnyObject'. Why would as? be requiring AnyObject?
func listForKey<Type>(key: String) -> [Type] {
guard let fullList = (itemList as NSArray).valueForKey( key ) as? NSArray else {
return [Type]()
}
// Filter out any values not matching the expected type such as if nil was used (value wasn't supplied)!
let typeOnlyList = fullList.filter( {$0 as? Type != nil} )
guard let foundList = typeOnlyList as? [Type] else { // <== at as?, Compiler Error 'Type' does not conform to protocol AnyObject
return [Type]()
}
return foundList
}
If I change the declaration to the following, it will compile:
func listForKey<Type:AnyObject>(key: String) -> [Type] {
However, it then won't work with String objects as Strings are of type any. Any thoughts on what's going on?
I thought I had a potential solution after reading Anton's comment by casting to Any. That resolved the compile time error, but now I get a runtime error (fatal error: array cannot be bridged from Objective-C):
extension NSArray {
public func toSwiftArray<Type>() -> [Type] {
// Filter out any values not matching the expected type such as nil
let typeOnlyList : [AnyObject] = self.filter( {$0 is Type} )
let typeOnlyAnyList : [Any] = typeOnlyList as [Any] // <== Runtime error EXC_BAD_INSTRUCTION
guard let foundList : [Type] = typeOnlyAnyList as? [Type] else {
return [Type]()
}
return foundList
}
}
Why would casting from [AnyObject] to [Any] cause a runtime error? I figured [Any] was the super set of [AnyObject].
Upvotes: 2
Views: 419
Reputation: 130092
If you want to work with NSArray
in Swift, you will have to work with classes only because NSArray
cannot hold anything else.
extension NSArray {
public func toSwiftArray<Type: AnyObject>() -> [Type] {
let typeOnlyList : NSArray = self.filter( {$0 is Type} )
return typeOnlyList as! Array<Type>
}
}
let nsArray: NSArray = [10, 20, "my-text"]
print("NSArray: \(nsArray)")
let swiftArray: [NSString] = nsArray.toSwiftArray()
print("Swift array: \(swiftArray)")
In short: use NSString
instead of String
. You can also convert [NSString]
to [String]
as the second step.
Upvotes: 0
Reputation: 3709
I guess this doesn't really answer the original question. However, it is a workaround that gets the job done. I still don't understand why the solution above doesn't work (especially the fatal error when casting from [AnyObject] to [Any]), but I took a different approach which works great:
extension NSArray {
public func toSwiftArray<Type>() -> [Type] {
var swiftArray = [Type]()
for value in self {
if let valueOfType = value as? Type {
swiftArray.append( valueOfType )
}
}
return swiftArray
}
public func toSwiftArray<Type>() -> ([Type], [Any]) {
var swiftTypeArray = [Type]()
var unknownTypeArray = [Any]()
for value in self {
if let valueOfType = value as? Type {
swiftTypeArray.append( valueOfType )
} else {
unknownTypeArray.append( value )
}
}
return (swiftTypeArray, unknownTypeArray)
}
}
Not sure why I couldn't use .filter to do this, but this is a very straightforward solution to the problem, and it also allows for a version that returns a list of the values that couldn't be converted. This is very handy routine for converting to NSArray with full type safety.
Upvotes: 0
Reputation: 10136
Here:
guard let fullList = ... as? NSArray
... you declare your fullList
variable to be NSArray
.
Therefore typeOnlyList
is also NSArray
(it's a result of filtering of fullList
).
NSArray
's elements are AnyObject
's (bridged from Objective-C).
Therefore, when you try to cast typeOnlyList as? [Type]
Swift expects Type
to conform to AnyObject
protocol.
Upvotes: 1