user3069232
user3069232

Reputation: 8995

JSON encode/decoding with swift 4

Swift 4.0 iOS 11.2.x

I created a Codable stuct called products here and used this method to save it to file.

 func saveImage() {
    let documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    let file2ShareURL = documentsDirectoryURL.appendingPathComponent("config.n2kHunt")

    var json: Any?
    let encodedData = try? JSONEncoder().encode(products)
    if let data = encodedData {
        json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
        if let json = json {
            do {
                try String(describing: json).write(to: file2ShareURL, atomically: true, encoding: .utf8)
            } catch {
                print("unable to write")
            }
        }
    }
}

It works and assuming I enter two records I get this on file. [which isn't json, but whatever].

(
    {
    identity = "blah-1";
    major = 245;
    minor = 654;
    url = "https://web1";
    uuid = f54321;
},
    {
    identity = "blah-2";
    major = 543;
    minor = 654;
    url = "https://web2";
    uuid = f6789;
}

)

I airdrop the file to a second iOS device, where I try and decode it with this procedure in appDelegate.

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {

    do {
        let string2D = try? String(contentsOf: url)
        let jsonData = try? JSONSerialization.data(withJSONObject: string2D)
        do {
            products = try JSONDecoder().decode([BeaconDB].self, from: jsonData!)
        } catch let jsonErr {
            print("unable to read \(jsonErr)")
        }

    }  catch {
        print("application error")
    }
   }

And I get a mother crash, with this error message...

2018-03-06 21:20:29.248774+0100 blah[2014:740956] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* +[NSJSONSerialization dataWithJSONObject:options:error:]: Invalid top-level type in JSON write'

What am I doing wrong; how can I write json to a file such that I can read it back!! Ironically I made this code work with a plist a few days back, I hate json.

Upvotes: 0

Views: 2026

Answers (1)

vadian
vadian

Reputation: 285290

Why do you encode the struct to JSON, then deserialize the JSON to Swift Array and save the collection type string representation(!) to disk? The other way round cannot work and that causes the error.

It's much easier:

func saveImage() {
    let documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
    let file2ShareURL = documentsDirectoryURL.appendingPathComponent("config.n2kHunt")

    do {
        let encodedData = try JSONEncoder().encode(products)
        try encodedData.write(to: file2ShareURL)
    } catch {
        print("unable to write", error)
    }
}

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {

    do {
        let jsonData = try Data(contentsOf: url)
        products = try JSONDecoder().decode([BeaconDB].self, from: jsonData)
    } catch {
        print("unable to read", error)
    }
}

Notes:

  • Never print a meaningless literal string in a catch clause, print at least the actual error.
  • Never write try? in a do - catch block. Write try without question mark to do catch the errors.

Upvotes: 3

Related Questions