15

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

}
NNikN
  • 3,720
  • 6
  • 44
  • 86
  • I do not understand what you are asking in the update of your question? Either clarify how it relates to the first part or ask a new question for the new part. At the moment your title refers to an `Optional Binding` issue, your first part is about `do try catch` and now it is about `down casting` and `nil coalescing operator` – R Menke Oct 28 '15 at 06:01
  • I am bit confused with nil check , type check and then downcasting, how all these things work together? Simple code but lots of understanding is required for it. – NNikN Oct 28 '15 at 06:21
  • 1
    [is and as](http://www.codingexplorer.com/type-casting-swift/), [optional binding](http://www.codingexplorer.com/multiple-optional-bindings-swift-1-2/), [do try catch](https://www.hackingwithswift.com/new-syntax-swift-2-error-handling-try-catch). Google is your friend for general language questions. Especially when it concerns the most written about stuff – R Menke Oct 28 '15 at 06:26
  • nil check is a simple if else depending on nil/value. Optional Binding gives you a non optional result from an optional input. Type check is a simple if else depending on type. down casting gives you an optional result of a specific type. – R Menke Oct 28 '15 at 06:29
  • have you had a chance to look at those links? – R Menke Oct 28 '15 at 22:01
  • multiple optional binding is the best explained here, something I would not find easily with books. – NNikN Oct 29 '15 at 01:15
  • I added some extra code illustrating multiple optional bindings. – R Menke Oct 29 '15 at 01:44

2 Answers2

13

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()
R Menke
  • 8,183
  • 4
  • 35
  • 63
2

You are looking for as?, which attempts to convert the thing on the left to the type on the right, and returns nil if the conversion is not possible:

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

if let actualDetails = details as? NSDictionary {
    print("Parse Data")
}

You rarely need to use isKindOfClass in Swift. If you find yourself using it, ask why, and consider whether as or as? will work instead.

Paul Cantrell
  • 9,175
  • 2
  • 40
  • 48
  • What if details is not null and happens to be a NSArray? – NNikN Oct 28 '15 at 03:45
  • Your original code was explicitly checking for NSDictionary, so I assumed that you specifically needed a dictionary for your parsing to work. If you can work with a dictionary _or_ an array, then you don’t need the type check at all; just remove `.AllowFragments`, and it will only allow dictionaries and arrays. Please describe in more detail what you are trying to accomplish. – Paul Cantrell Oct 28 '15 at 03:49
  • [Reply to deleted comment from R Menke]: From the original code, it looked like the asker was trying to work with a dictionary, in which case they need a variable of type `NSDictionary`, in which case `as?` in a conditional unwrapping is the correct approach. If their parser code should proceed _only_ if the result is an `NSDictionary` but does not actually need any dictionary-specific calls (which is unlikely but possible), then `is` would indeed be appropriate. – Paul Cantrell Oct 28 '15 at 04:05
  • @PaulCantrell yeah I was working on this in a playground with the limited code available and I was too fast. I was only responding to his comment on my answer where he said he needed to check a type. Wasn't anticipating the needed downcast. `is` only works as check, not to cast it to a type. – R Menke Oct 28 '15 at 04:10