Jimmery
Jimmery

Reputation: 10139

Casting Any to AnyObject in Swift

I have an object contained within a Dictionary like so:

let dict:<String, Any> = ["obj": self]

Then later on I am trying to use that object as the target of a button press:

button.addTarget(dict["obj"], action: "buttonPress:", forControlEvents: .TouchUpInside)

And I get the error:

Type '(String, Any)' does not conform to protocol 'AnyObject'

I am guessing here my issue is trying to cast Any as AnyObject. Is this even possible?

Upvotes: 4

Views: 6455

Answers (3)

Kiran Jasvanee
Kiran Jasvanee

Reputation: 6564

Any to AnyObject is possible. You can unwrap Any to AnyObject by using .unsafelyUnwrapped of Any regardless it's classes, structs, enums, ints, strings etc.

Here _change[.oldKey] returns Any, unwrap and cast it to AnyObject as done in below code.

 override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

        guard let oldKey = (_change[.oldKey].unsafelyUnwrapped as AnyObject).cgPointValue else {
                        return
                    }
        guard let newKey = (_change[.newKey].unsafelyUnwrapped as AnyObject).cgPointValue else {
                        return
                    }
        print(oldKey)
        print(newKey)
    }  
extension NSKeyValueChangeKey {
    public static let newKey: NSKeyValueChangeKey
    public static let oldKey: NSKeyValueChangeKey
}

I've used KVO ObserveValue forKeyPath to receive change in [NSKeyValueChangeKey : Any] format.
I successfully retrieved CGPoint values converted from Any to AnyObject then to CGPoint.

Upvotes: 3

Antonio
Antonio

Reputation: 72780

No downcasting from Any to AnyObject is not possible, and yes, the problem you have is about Any not convertible to AnyObject.

Any can represent any type (classes, structs, enums, ints, strings etc.), whereas AnyObject can represent reference types only (i.e. classes).

To solve the problem, I think you should change your dictionary to store values of AnyObject type:

let dict:<String, AnyObject> = ["obj": self]

Note that even if the dictionary contains AnyObject values, it can also store:

  • numbers
  • strings
  • arrays
  • dictionaries

because these types are automatically bridged to the corresponding objc types (NSNumber, NSArray, NSDictionary, etc.)

If you really need to have max flexibility, then I suggest using NSDictionary - but in that case value types must be explicitly boxed in reference types (i.e. int in NSNumber, etc.).

Also @rintaro's answer in another good option.

Upvotes: 3

rintaro
rintaro

Reputation: 51911

You cannot cast Any to AnyObject, but you can cast it to any concrete class:

    button.addTarget(dict["obj"] as UIViewController, action: "buttonPress:", forControlEvents: .TouchUpInside)

Of course this may causes runtime error if dict["obj"] is not UIViewController, so make it safer:

    if let target = dict["obj"] as? UIViewController {
        button.addTarget(target, action: "buttonPress:", forControlEvents: .TouchUpInside)
    }
    else {
        fatalError("dict[\"obj\"] is not UIViewController!")
    }

And of course, it's better to use your own subclass, instead of UIViewController.

Upvotes: 2

Related Questions