3

How do you downcast an array of protocol instances into AnyObjects? I've tried some of the more reasonable ideas in the code example below.

protocol Nameable : class {
    var name: String { get }
}

class Person: Nameable {
    var name: String

    init(name: String)
    {
        self.name = name
    }
}

class Example {

    func setArray(array: [AnyObject]?, forKey: String)
    {
        print("hello world")
    }
}

var personOne = Person(name: "Evan")
var personTwo = Person(name: "Brian")

var array: [ Nameable ] = [ personOne, personTwo ]

var anotherArray = array.map({ $0 as AnyObject })    // OMG gross!
var yetAnotherArray = array as [ AnyObject ]         // Nope.
var evenYetAnotherArray = array as? [ AnyObject ]    // Nope.
var omgThisIsAnArray = Array<AnyObject>(array)       // Ha ha, srsly. Nope.

var myExample = Example()
myExample.setArray(anotherArray, forKey: "Named")

For what it's worth, setArray(_ anArray: [AnyObject]?, forKey aKey: String) method signature comes from Apple's NSUbiquitousKeyValueStore class, so I can't really redesign that to be type safe.

edelaney05
  • 6,822
  • 6
  • 41
  • 65
  • If an object conforms to `AnyObject` but its type is more distinct it's not necessary to cast the object to `AnyObject` to match a method signature. In your case – as Eric mentions in his answer – the issue is that `NSUbiquitousKeyValueStore` supports only property list compliant types. – vadian Apr 15 '16 at 05:04

2 Answers2

4

It's less than perfect, but it works if the protocol and class are both @objc (and the class subclasses NSObject):

@objc protocol Nameable: class {
    var name: String { get }
}

@objc class Person: NSObject, Nameable {
    var name: String
    init(name: String) {
        self.name = name
    }
}

...

var array: [Nameable] = [personOne, personTwo]
let array2 = array as [AnyObject] // ✓
MikeyWard
  • 1,123
  • 9
  • 19
  • 1
    Thank you! The key here is that `AnyObject` is annotated with `@objc`, whereas my protocol is _not_. Therefore it makes sense that it's not possible to cast. – edelaney05 Apr 15 '16 at 21:25
0

This doesn't really answer your question directly, but I don't think this will work like you expect.

From the docs for NSUbiquitousKeyValueStore setArray:

An array whose contents can be stored in a property list format. In other words, the objects in the array must be of the types NSNumber, NSString, NSDate, NSData, NSArray, or NSDictionary. The total size (in bytes) of the array and its contents must not exceed the per-key size limits.

You're trying to put data in that is not any of the above and I'm guessing you'll run into issues.

Something like this would work:

import UIKit // For String<->NSString bridging

protocol Nameable : class  {
    var name: String { get }
}

class Person: Nameable {
    var name: String

    init(name: String)
    {
        self.name = name
    }
}

class Example {

    func setArray(array: [AnyObject]?, forKey: String)
    {
        print("hello world")
    }
}

var personOne = Person(name: "Evan")
var personTwo = Person(name: "Brian")

var array: [ Nameable ] = [ personOne, personTwo ]

var nameArray = array.map({ $0.name }) // Now [String]

var myExample = Example()
myExample.setArray(nameArray, forKey: "Named")

Or you could serialize your Swift objects some other way to get them into one of the supported classes.

Eric Reid
  • 457
  • 3
  • 10