1

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?

2 Answers 2

3

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
}
Sign up to request clarification or add additional context in comments.

3 Comments

Your solution with generics is perfect. However I am among those who would avoid overriding a Swift standard function. More specifically I admit I don't know if Cocoa (and other OS libraries/frameworks) can see your version of countElements. If the can maybe the override approach is a little insecure.
There’s no risk from Cocoa (even if it was a possibility which I suspect it is not) calling this version, since the the Swift version comes ahead of it in the overload resolution priority (even though Swift will implicitly upconvert non-optionals to optionals, calls that require that appear to come lower in the pecking order). I would not advocate overloading an existing function in a way that would cause ambiguity for existing callers (though I guess you could do it unwittingly).
Ok in your declaration you did not use the "public" access control modifier. So your version of countElements is visibile only inside the project where it is declared. Then it should not interfere with other library.
0

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.

1 Comment

I'm getting an error, "'myStructType' is not identical to 'Any'"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.