0

I am trying to parse JSON data to a dictionary, for parsing I am using the separate method, and later would like to use the results (dictionary) for other operations in another method, not just to print it out as it is given in many examples online, e. g. here.

However, I cannot return the value since I was asked to insert return statement inside guard, but after the insertion getting "Non-void function should return a value".

The code looks the following way:

 func  extractJSONDictionaryFrom(JSONData:NSData) ->NSMutableDictionary
    {
        var dict = NSMutableDictionary()
        do {
        guard let JSON = try NSJSONSerialization.JSONObjectWithData(JSONData, options:NSJSONReadingOptions(rawValue: 0)) as? NSDictionary else {
            print("Not a Dictionary")
            return
        }
            dict = NSMutableDictionary(dictionary: JSON)
        }
        catch let JSONError as NSError {
            print("\(JSONError)")
        }
        print("The JSON is \(dict)")
        return dict
    }

The approach using throw is as well hardly useful since I need to handle throws in other methods when calling "extractJSONDictionaryFrom"

Community
  • 1
  • 1
Darius Miliauskas
  • 3,391
  • 4
  • 35
  • 53
  • Why is `throw` not good? If the method is marked that it `throws` errors, then if you do `JSONObjectWithData` without `do`-`catch` block, the `NSJSONSerialization` error will be thrown. But you then have the option of throwing your own errors, (whether custom `ErrorType` types like shown in that link, or your own `NSError` object if you want). – Rob Jan 19 '16 at 08:55
  • But then I need to handle throws in another methods when i call that method, don't I? I would like to leave that handling just for one method, avoid doing that later. – Darius Miliauskas Jan 19 '16 at 09:04
  • That's fine, but then you have to check for `nil` if you don't throw errors, anyway, and you won't know why it failed. But if the caller doesn't need to know why it failed, then go ahead and use optional. Use whichever approach you want. – Rob Jan 19 '16 at 09:09

2 Answers2

2

Make the result optional (NSMutableDictionary?) and use return nil.

You might also want to return NSDictionary? if the caller should not change the returned dictionary.

EDIT

The code given does not work as NSJSONSerialization.JSONObjectWithData throws an error given invalid JSON and this is caught by the catch. The function returns an empty dictionary and not nil. I would try the following:

func  extractJSONDictionaryFrom(JSONData:NSData) ->NSMutableDictionary?
{
    var dict = NSMutableDictionary()
    do {
        guard let JSON = try NSJSONSerialization.JSONObjectWithData(JSONData, options:NSJSONReadingOptions(rawValue: 0)) as? NSDictionary else {
            print("Not a Dictionary")
            return nil
        }
        dict = NSMutableDictionary(dictionary: JSON)
    }
    catch let JSONError as NSError {
        print("\(JSONError)")
        return nil
    }
    print("The JSON is \(dict)")
    return dict
}

I also believe that propagating the exception would be better.

Alternatively, you could return an empty dictionary for all errors and not use an optional result, but this "hides" errors which is probably a bad idea.

formal
  • 21
  • 3
1

One option, is to let your method throw errors (whether the NSJSONSerialization error because JSON parsing failed entirely or your custom error if JSON parsing worked, but it wasn't a dictionary for some reason):

func extractJSONDictionaryFrom(JSONData: NSData) throws -> NSMutableDictionary {
    guard let JSON = try NSJSONSerialization.JSONObjectWithData(JSONData, options:[]) as? NSDictionary else {
        throw NSError(domain: NSBundle.mainBundle().bundleIdentifier!, code: -1, userInfo: [NSLocalizedDescriptionKey : "Not a dictionary"])
    }
    return NSMutableDictionary(dictionary: JSON)
}

Or, another approach is to not have it throw errors, but just return nil if the conversion failed:

func extractJSONDictionaryFrom(JSONData: NSData) -> NSMutableDictionary? {
    do {
        guard let JSON = try NSJSONSerialization.JSONObjectWithData(JSONData, options:NSJSONReadingOptions(rawValue: 0)) as? NSDictionary else {
            print("Not a Dictionary")
            return nil
        }
        return NSMutableDictionary(dictionary: JSON)
    } catch let JSONError as NSError {
        print("\(JSONError)")
        return nil
    }
}

I'd lean towards the former, but the latter works, too.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • That (your second approach) raises another problem: Nil is incompatible with return type 'NSMutableDictionary' – Darius Miliauskas Jan 19 '16 at 08:52
  • 1
    @DariusMiliauskas - Note, formal and I suggested changing it to `NSMutableDictionary?` (an optional) if you don't want to throw errors. That lets you return `nil`. Either make the return type optional or throw errors. Those are the two logical ways to let the caller know that the JSON parsing failed for some reason. – Rob Jan 19 '16 at 08:56