How to save/retrieve dictionary from file using SwiftyJSON
Asked Answered
C

2

10

I'm trying to make the conversion from Objc to swift and have had better days.

I have a class with a dictionary:

 collaborationDictionary:[String:Set<String>]

I am trying to write/read this dictionary to/from a file and just can't quite seem to make it work. I have to save the dictionary using the following JSON structure and I have to use SwiftyJSON.

 { "Collaborations" : {
          "5604" : [
              "whiteboard.png",
              "VID_20161123_135117.3gp",
              "Photo_0.jpeg"]
          "5603" : [
              "VID_20161123_135117.3gp"],
          "5537" : [
              "Screenshot_20151212-132454.png",
              "VID_20161202_083205.3gp",
              "VID_20161123_135117.3gp",
              "Photo_0.jpeg",
              "Screenshot_20151212-132428.png",
              "Screenshot_20151212-132520.png",
              "IMG_20161017_132105.jpg",
              "whiteboard.png"]}
 }

I don't have any real problem with finding/retrieving the file or writing the file. I just can't quite figure out how to manually load SwiftyJSON. I need to have a JSON object called "Collaborations" at the top. It needs to contain a dictionary of collaboration IDs (5604, 5603...). Each collaboration contains an array of string (filenames). I'm including the code I'm using to read/write the file but I need help with the SwiftyJSON library.

This is the member data member I'm using to store the above data:

These are the functions I need to finish:

     private var collaborationDictionary:[String:Set<String>] = [:]


  func getUploadedFileSet() {
    collaborationDictionary = [:]
    let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
    let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
    let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)

    if FileManager.default.fileExists(atPath: (jsonFileURL?.absoluteString)!) {
        do {
            let data = try Data(contentsOf: jsonFileURL!, options: .alwaysMapped)
            let json = JSON(data: data)

            // ************************************************
            // NEED HELP START
            // NOW WHAT????  What is the SwiftyJSON code

            ?????????????????????????                

            // NEED HELP END
            // ************************************************

        } catch let error {
            print(error.localizedDescription)
        }
    }
 }

  func saveUploadedFilesSet() {        
    let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
    let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
    let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)

    do {
        let dirExists = FileManager.default.fileExists(atPath: (appURL?.absoluteString)!)
        if !dirExists {
            try FileManager.default.createDirectory(atPath: (appURL?.absoluteString)!, withIntermediateDirectories: false, attributes: nil)
        }

        // ************************************************
        // NEED HELP START
            // NOW WHAT????  What is the SwiftyJSON code

            ?????????????????????????                

        // NEED HELP END
        // ************************************************

        // Write to file code - haven't written it yet but that should be easy          

    } catch let error as NSError {
        print(error.localizedDescription);
    }
 }

Any direction would be greatly appreciated. Thanks!

EDIT

I was able to figure out how to load the supplied JSON structure from file. Here is the code:

 func getUploadedFileSet() {
    let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
    let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
    let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)

    if FileManager.default.fileExists(atPath: (jsonFileURL?.absoluteString)!) {
        do {
            let data = try Data(contentsOf: jsonFileURL!, options: .alwaysMapped)
            let json = JSON(data: data)
            if json != nil {
                for (key, subJson) in json[kCollaborations] {
                    let stringArray:[String] = subJson.arrayValue.map { $0.string! }
                    let stringSet = Set(stringArray)
                    collaborationDictionary.updateValue(stringSet, forKey: key)
                }
            } else {
                print("Could not get json from file, make sure that file contains valid json.")
            }
        } catch let error {
            print(error.localizedDescription)
        }
    }

I still haven't figured out how to save the collaborationDictionary object to file. My biggest problem is figuring out how to put in the "Collaborations" key. Any ideas?

Clipfed answered 14/12, 2016 at 14:24 Comment(7)
What is your specific question?Uncanonical
Specifically, what is the code to read AND write the json object. I'll highlight the sections where the code either needs to be verified/fixed or created.Clipfed
long story short, you need to load file from disk? read it as string? and serialize to json using SwiftyJSON?Highhat
I understand that. What I don't know is how to do is use "SwiftyJSON" specifically. I'm updating the above code so you can write the ACTUAL swiftyJSON to read and write the JSON string.Clipfed
Not an answer, but I highly recommend not to use a 3rd party library when you are learning a new language.Winfordwinfred
No choice there. Using the native Swift JSON libraries is like learning a new language. So, it was decided (not be me) that we should use SwiftyJSON because of the better fault tolerance... etc... I think the answer is fairly easy for someone familiar with SwiftyJSON. At least that is my hope.Clipfed
At least consider that fileExists(atPath: (appURL?.absoluteString)! will never return true.Eolithic
C
5

I finally got this to work. The biggest problem was that I couldn't convert collaborationDictionary to JSON. I finally had to convert it to a dictionary of arrays vs dictionary of sets. Here are the 2 methods:

     // **************************************************************************
func getUploadedFileSet() {
    let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
    let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
    let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)

    if FileManager.default.fileExists(atPath: (jsonFileURL?.absoluteString)!) {
        do {
            let data = try Data(contentsOf: jsonFileURL!, options: .alwaysMapped)
            let json = JSON(data: data)
            if json != nil {
                for (key, subJson) in json[kCollaborations] {
                    let stringArray:[String] = subJson.arrayValue.map { $0.string! }
                    let stringSet = Set(stringArray)
                    collaborationDictionary.updateValue(stringSet, forKey: key)
                }
            } else {
                print("Could not get json from file, make sure that file contains valid json.")
            }
        } catch let error {
            print(error.localizedDescription)
        }
    }
}

// **************************************************************************
func saveUploadedFilesSet() {
    let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
    let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
    let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)
    let adjustedJSONFileURL = URL(fileURLWithPath:(jsonFileURL?.absoluteString)!)

    do {
        let dirExists = FileManager.default.fileExists(atPath: (appURL?.absoluteString)!)
        if !dirExists {
            try FileManager.default.createDirectory(atPath: (appURL?.absoluteString)!, withIntermediateDirectories: false, attributes: nil)
        }

        // Convert set elements to arrays
        var convertedCollaborationDictionary: [String:[String]] = [:]
        for (sessionID, fileNameSet) in collaborationDictionary {
            let array = Array(fileNameSet)
            convertedCollaborationDictionary.updateValue(array, forKey: sessionID)
        }
        let json: JSON = JSON(convertedCollaborationDictionary)
        let fullJSON: JSON = [kCollaborations:json.object]
        let data = try fullJSON.rawData()
        try data.write(to: adjustedJSONFileURL, options: .atomic)

    } catch let error as NSError {
        print(error.localizedDescription);
    }
}
Clipfed answered 19/12, 2016 at 17:13 Comment(0)
T
2

If you dig into the source, SwiftyJSON wraps JSONSerialization, which can both be initialized and converted back to Data which is knows how to read and write itself from disk:

  func readJSON() -> JSON? {
      guard let url = Bundle.main.url(forResource: "data", withExtension: "json"),
            let data = try? Data(contentsOf: url) else {
          return nil
      }
      return JSON(data: data)
  }

  func write(json: JSON, to url: URL) throws {
      let data = try json.rawData()
      try data.write(to: url)
  }

Note that you can load your static data from anywhere including your Bundle, but you can only write to the sandbox (ie the Documents directory). You may wish to copy from your Bundle to the documents directory on first run if you are planning on reading/writing to the same file.

Also your sample JSON is bad (lint it). You need a comma after "Photo_0.jpeg"]

Teniacide answered 18/12, 2016 at 15:37 Comment(4)
I understand how to convert the JSON to data objects and back. How do I load the collaborationDictionary object specified in the question? How do I convert the collaborationDictionary back to SwiftyJSON. And the JSON is correct. Array elements are followed by a comma UNLESS they are the last element in the array.Clipfed
Did you run the code that I posted? If you copy the json into a data.json text file and put that file in your project and call the readJSON() function it fails (print(readJSON) shows nil), but if you add the comma it works and you get the full JSON object in the console.Teniacide
The problem is that it doesn't answer the question: the question is how to convert the following member data: collaborationDictionary:[String:Set<String>] from SwiftyJSON (SJ) and convert it back to SJ. As you can see from my code, I'm already loading the file into a data object. I just can't figure out how to get SwiftyJSON data into var collaborationDictionary:[String:Set<String>]. Also, I didn't put the actual code of writing it to file but that is also quite simple. I just cant figure out how to put the collaborationDictionary into SwiftyJSON. By SwiftyJSON, I mean a JSON object.Clipfed
SwiftyJSON does not serialize and deserialize classes/structs. If that is what you want you should use ObjectMapper to convert to an from a [String : Any] which you can use to initialize your JSON. github.com/Hearst-DD/ObjectMapperTeniacide

© 2022 - 2024 — McMap. All rights reserved.