Search code examples
iosswift3anyobject

Dictionary of type [NSObject: AnyObject] has no member "value(forKeyPath: ...)"


I'm converting an app to swift3 and encountering the following problem.

@objc required init(response: HTTPURLResponse, representation: [NSObject : AnyObject])
{
    if (representation.value(forKeyPath: "title") is String)    {
        self.title = **representation.value**(forKeyPath: "title") as! String
    }

I get the following error:

Value of type [NSObject:AnyObject] has no member value.

In the old version of the code I was just using AnyObject as type for representation, but if I do so I get the error AnyObject is not a subtype of NSObject there:

if (representation.value(forKeyPath: "foo") is String) {
    let elementObj = Element(response: response, representation:**representation.value(forKeyPath: "foo")**!)
}

Solution

  • You're mixing Objective-C and Swift styles. Better to actually decide.

    Bridging back to NSDictionary is not automatic.

    Consider:

    let y: [NSObject: AnyObject] = ["foo" as NSString: 3 as AnyObject] // this is awkward, mixing Swift Dictionary with explicit types yet using an Obj-C type inside
    let z: NSDictionary = ["foo": 3]
    (y as NSDictionary).value(forKeyPath: "foo") // 3
    // y["foo"] // error, y's keys are explicitly typed as NSObject; reverse bridging String -> NSObject/NSString is not automatic
    y["foo" as NSString] // 3
    y["foo" as NSString] is Int // true
    z["foo"] // Bridging here is automatic though because NSDictionary is untyped leaving compiler freedom to adapt your values
    z["foo"] is Int // true
    // y.value // error, not defined
    
    // Easiest of all:
    let ynot = ["foo": 3]
    ynot["foo"] // Introductory swift, no casting needed
    ynot["foo"] is Int // Error, type is known at compile time
    

    Reference:

    https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html

    Note the explicit use of 'as' required to get a String back to NSString. Bridging is not hidden because they want you to use value types (String) over reference types (NSString). So this is deliberately more cumbersome.

    One of the primary advantages of value types over reference types is that they make it easier to reason about your code. For more information about value types, see Classes and Structures in The Swift Programming Language (Swift 3), and WWDC 2015 session 414 Building Better Apps with Value Types in Swift.