Wednesday, June 18, 2014

Cannot downcast from 'AnyObject' to non-@objc protocol type

The next item I wished to do was to pass in an object to my picker and let it be a choice.  I defined a simple object type of Die, which represents a type of die.

I could not find a way to have interpolation work with the object, for "\(value)", so I decided to have a protocol that can be checked for.

I defined the following protocol:
protocol ProvidesTextValue
{
    var textValue:String { get}

}

With the following usage:
 func pickerView(pickerView: UIPickerView!, titleForRow row: Int, forComponent component: Int) -> String
    {
        let array=getArray(component);

        let value : AnyObject=array[row];
        if value is ProvidesTextValue
        {
            let textProvider=value as ProvidesTextValue
            return "\(textProvider.textValue)"
        }
            return "\(value)";
    }

Which resulted in the error:
DieRollDataSource.swift:61:18: Cannot downcast from 'AnyObject' to non-@objc protocol type 'ProvidesTextValue'

The Swift Programming guide provides the following guidance:

NOTE
You can check for protocol conformance only if your protocol is marked with the @objc attribute, as seen for the HasArea protocol above. This attribute indicates that the protocol should be exposed to Objective-C code and is described in Using Swift with Cocoa and Objective-C. Even if you are not interoperating with Objective-C, you need to mark your protocols with the @objc attribute if you want to be able to check for protocol conformance.
Note also that @objc protocols can be adopted only by classes, and not by structures or enumerations. If you mark your protocol as @objc in order to check for conformance, you will be able to apply that protocol only to class types.

So I had to modify the protocol definition to be:
@objc protocol ProvidesTextValue
{
    var textValue:String { get}
}


This resulted in a runtime error with the very 'useful' error message:
** NSForwarding: warning: object 0x110fa0000 of class '_TtC15SimpleDieRoller3Die' does not implement methodSignatureForSelector: -- trouble ahead

I then modified the class definition with @objc as well, which solved the problem:
@objc class Die : ProvidesTextValue
{
    var dieType:Int
    
    init(die:Int)
    {
        dieType=die;
    }

    var textValue:String { return "\(dieType)" }


}

2 comments: