39

I have a JSON file called points.json, and a read function like:

private func readJson() {
    let file = Bundle.main.path(forResource: "points", ofType: "json")
    let data = try? Data(contentsOf: URL(fileURLWithPath: file!))
    let jsonData = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String:Any]
    print(jsonData)
}

It does not work, any help?

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Xie
  • 627
  • 1
  • 8
  • 18
  • 1
    What does not work? Add a `do - catch` block to get error information. By the way, `Bundle` has got `URL` related API to retrieve resources. – vadian Nov 05 '16 at 13:35

2 Answers2

107

Your problem here is that you force unwrap the values and in case of an error you can't know where it comes from.

Instead, you should handle errors and safely unwrap your optionals.

And as @vadian rightly notes in his comment, you should use Bundle.main.url.

private func readJson() {
    do {
        if let file = Bundle.main.url(forResource: "points", withExtension: "json") {
            let data = try Data(contentsOf: file)
            let json = try JSONSerialization.jsonObject(with: data, options: [])
            if let object = json as? [String: Any] {
                // json is a dictionary
                print(object)
            } else if let object = json as? [Any] {
                // json is an array
                print(object)
            } else {
                print("JSON is invalid")
            }
        } else {
            print("no file")
        }
    } catch {
        print(error.localizedDescription)
    }
}

When coding in Swift, usually, ! is a code smell. Of course there's exceptions (IBOutlets and others) but try to not use force unwrapping with ! yourself and always unwrap safely instead.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • Thanks! It printed "The data couldn’t be read because it isn’t in the correct format.". – Xie Nov 05 '16 at 13:43
  • 1
    So I think it is the problem with the json file – Xie Nov 05 '16 at 13:43
  • 2
    Yes, this is the `catch` that caught the error coming from JSONSerialization. Your JSON file is likely to be invalid. See: always handle errors. :) – Eric Aya Nov 05 '16 at 13:44
  • 5
    `usually, ! is a code smell ... try to not use force unwrapping with ! yourself and always unwrap safely instead.` +1 – MANN Apr 12 '17 at 21:45
  • I do not quite agree with your statement on "When coding in Swift, usually, ! is a code smell. Of course there's exceptions (IBOutlets and others) but try to not use force unwrapping with ! yourself and always unwrap safely instead.", swift force unwrapping is used when you guarantee there are value store under variable. It is your design that make code smell not the language itself. – Parama Dharmika Oct 11 '17 at 15:38
  • 1
    @ParamaDharmika A code smell means that the code has an issue, not the language. You have misinterpreted my words. I agree with your conclusion, we are actually thinking the same... I'm talking about the code (what is written), not about the language used to write it. – Eric Aya Oct 11 '17 at 17:06
5

The Swift 5 / iOS 12.3 code below shows a possible rewrite of your method that avoids force unwrap on optional values and handles gently potential errors:

import Foundation

func readJson() {
    // Get url for file
    guard let fileUrl = Bundle.main.url(forResource: "Data", withExtension: "json") else {
        print("File could not be located at the given url")
        return
    }

    do {
        // Get data from file
        let data = try Data(contentsOf: fileUrl)

        // Decode data to a Dictionary<String, Any> object
        guard let dictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
            print("Could not cast JSON content as a Dictionary<String, Any>")
            return
        }

        // Print result
        print(dictionary)
    } catch {
        // Print error if something went wrong
        print("Error: \(error)")
    }
}
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218