PopKernel
PopKernel

Reputation: 4270

Pass Array containing either class or struct

In Swift I declared a function that differs from Array.count only in that if array == nil the function returns 0. This is related to my UITableViewDataSource, but that's not important here. The problem is, if I declare the function as:

class func countOfItemsInArray(array: [AnyObject]?) -> Int

and then try to pass it an array of structs, it declares that the structs in the array do not conform to AnyObject. I understand why that is (I think), but is there a way to make this work with classes and structs, or should I just give in to copy and paste?

Upvotes: 1

Views: 150

Answers (2)

Airspeed Velocity
Airspeed Velocity

Reputation: 40955

Generics are probably better suited to this problem than relying on covariance of [AnyObject]. A version of countElements that worked on an optional array and returned 0 in case of nil could go like this:

func countElements<T>(array: [T]?) -> Int {
    return array?.count ?? 0
}

When you call countElements with any kind of array, the placeholder T is replaced with the type of the element contained in the array.

Note, this version overloads the existing countElements with a version that takes an optional. If you call it with a non-optional or any other kind of collection, the Swift version would be called, if you pass in an optional array, this one will be called. It’s debatable whether this is a good practice (I think it’s fine) or a bad one (some may disapprove :).

A version that works on any collection type would be:

func countElements<C: CollectionType>(col: C?) -> C.Index.Distance {
    return col.map { countElements($0) } ?? 0
}

Upvotes: 3

Marcus Rossel
Marcus Rossel

Reputation: 3258

If you use Any instead of AnyObject you can pass any type, so also structs:

class func countOfItemsInArray(array: [Any]?) -> Int

This is kind of weird.

I used this function:

func countOfItemsInArray(array: [Any]?) -> Int {
    return array != nil ? array!.count : 0
}

Declared two of your Assignment structs and put them in an array:

let structOne = Assignment(name: "1", dueDate: NSDate(), subject: "1")
let structTwo = Assignment(name: "2", dueDate: NSDate(), subject: "2")
let myArray: [Assignment] = [structOne, structTwo]

But here's the interesting part.
When calling println(countOfItemsInArray(myArray)) it gives the error:

<stdin>:27:33: error: 'Assignment' is not identical to 'Any'
println(countOfItemsInArray(myArray))
^
<stdin>:17:26: note: in initialization of parameter 'array'
func countOfItemsInArray(array: [Any]?) -> Int {
^

So I tested if myArray is of type [Any]:

println(myArray is [Any])

to which swift says:

<stdin>:25:17: error: 'Any' is not a subtype of 'Assignment'
println(myArray is [Any])
^

But when I change the type annotation of myArray to [Any] it works:

let myArray: [Any] = [structOne, structTwo]

And when simply handing the literal to the function it works, too:

countOfItemsInArray([structOne, structTwo])

The whole code example can be seen here.

Upvotes: 0

Related Questions