Initializer for conditional binding must have Optional type, not 'AnyObject - Approach

NNikN picture NNikN · Oct 28, 2015 · Viewed 19.3k times · Source

The following code throws a message which says "Initializer for conditional binding must have Optional type, not 'AnyObject'"

    func parseData2(){
        var data:NSData?


        if let data2 = data  {
            do {
                let details = try NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments)

                if let actualDetails = details where actualDetails.isKindOfClass(NSDictionary) {
                    print("Parse Data")
                }

            }catch {
                print("Error \(error)")
            }
        }

    }

To resolve the above error I used the following code.

    func parseData2(){
        var data:NSData?


        if let data2 = data  {
            do {
                let details:AnyObject = try NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments)

                if let actualDetails:AnyObject = details where actualDetails.isKindOfClass(NSDictionary) {
                    print("Parse Data")
                }

            }catch {
                print("Error \(error)")
            }
        }

    }

Is there any better approach then the above or my code might crash?

There is one more code which I want to add considering nil check,type check and then type cast check. The reason behind that Swift offers great flexibility but litle bit difficult to fix issues. Let's say I have a dictionary, cityDetails and I am trying to get data for self.cityZipCode and self.cityIdentifier, which are optional, defined as var cityZipCode:Int? and var cityIdentifier:Int?

if let cityBasic = cityDetails["basicDetails"] where 
cityBasic!.isKindOfClass(NSDictionary) {

self.cityZipCode = (cityBasic as! NSDictionary)["zip"].integerValue ?? 0

self.cityIdentifier =  (cityBasic as! NSDictionary)["cityId"].integerValue ?? 0

}

Answer

R Menke picture R Menke · Oct 28, 2015

No need to unwrap the result from try. It is not an optional. You do need to cast the result from try to an NSDictionary. Use as? to downcast it.

Best practice: full access to returned error for good error handling

func parseData2(){
    var data:NSData?

    if let data2 = data  {
        do {
            let details = try NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments)

            if let detailsDict = details as? NSDictionary {
                print("Parse Data")
            } else if let detailsArray = details as? NSArray {
                print("array")
            }

        } catch {
            print("Error \(error)")
        }
    }
}

Quick and dirty: error handling is not for me!

func parseData2(){
    var data:NSData?

    if let data2 = data  {

        let details = try? NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments)

        if let detailsDict = details as? NSDictionary {
            print("Parse Data")
        } else {
            print("details might be nil, or not an NSDictionary")
        }
    }
}

Bad Ass Mode: crashes are features

func parseData2(){
    var data:NSData?

    if let data2 = data  {

        let details = try! NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments) as! NSDictionary

    }
}

Some extra info on multiple unwraps : Drop the code below in a playground.

struct SomeStruct {
    var anOptional : Int?
    init() {
    }
}

func unwrapWithIfLet() {

    if let unWrappedStruct = myStruct, let unWrappedSomething = unWrappedStruct.anOptional {
        print("multiple optional bindings succeeded")
        // both unWrappedStruct and unWrappedSomething are available here
    } else {
        print("something is nil")
    }
}

func unwrapWithGuard() {

    guard let unWrappedStruct = myStruct, let unWrappedSomething = unWrappedStruct.anOptional else {
        print("something is nil")
        return
    }
    print("multiple optional bindings succeeded")
    // both unWrappedStruct and unWrappedSomething are available here
}


var myStruct : SomeStruct?

//unwrapWithGuard()
//unwrapWithIfLet()

myStruct = SomeStruct()
myStruct!.anOptional = 1

unwrapWithGuard()
unwrapWithIfLet()