How to save an array as a json file in Swift?

#1. Save a Swift Array as a json file

The following Swift 3 / iOS 10 code shows how to transform an Array instance into json data and save it into a json file located in an iPhone's document directory using FileManager and JSONSerialization:

func saveToJsonFile() {
    // Get the url of Persons.json in document directory
    guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let fileUrl = documentDirectoryUrl.appendingPathComponent("Persons.json")

    let personArray =  [["person": ["name": "Dani", "age": "24"]], ["person": ["name": "ray", "age": "70"]]]

    // Transform array into data and save it into file
    do {
        let data = try JSONSerialization.data(withJSONObject: personArray, options: [])
        try data.write(to: fileUrl, options: [])
    } catch {
        print(error)
    }
}

/*
 Content of Persons.json file after operation:
 [{"person":{"name":"Dani","age":"24"}},{"person":{"name":"ray","age":"70"}}]
*/

As an alternative, you can implement the following code that use streams:

func saveToJsonFile() {
    // Get the url of Persons.json in document directory
    guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let fileUrl = documentDirectoryUrl.appendingPathComponent("Persons.json")

    let personArray =  [["person": ["name": "Dani", "age": "24"]], ["person": ["name": "ray", "age": "70"]]]

    // Create a write-only stream
    guard let stream = OutputStream(toFileAtPath: fileUrl.path, append: false) else { return }
    stream.open()
    defer {
        stream.close()
    }

    // Transform array into data and save it into file
    var error: NSError?
    JSONSerialization.writeJSONObject(personArray, to: stream, options: [], error: &error)

    // Handle error
    if let error = error {
        print(error)
    }
}

/*
 Content of Persons.json file after operation:
 [{"person":{"name":"Dani","age":"24"}},{"person":{"name":"ray","age":"70"}}]
*/

#2. Get a Swift Array from a json file

The following Swift 3 / iOS 10 code shows how to get data from a json file located in an iPhone's document directory and transform it into an Array instance using FileManager and JSONSerialization:

/*
 Content of Persons.json file:
 [{"person":{"name":"Dani","age":"24"}},{"person":{"name":"ray","age":"70"}}]
*/

func retrieveFromJsonFile() {
    // Get the url of Persons.json in document directory
    guard let documentsDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let fileUrl = documentsDirectoryUrl.appendingPathComponent("Persons.json")

    // Read data from .json file and transform data into an array
    do {
        let data = try Data(contentsOf: fileUrl, options: [])
        guard let personArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: [String: String]]] else { return }
        print(personArray) // prints [["person": ["name": "Dani", "age": "24"]], ["person": ["name": "ray", "age": "70"]]]
    } catch {
        print(error)
    }
}

As an alternative, you can implement the following code that use streams:

/*
 Content of Persons.json file:
 [{"person":{"name":"Dani","age":"24"}},{"person":{"name":"ray","age":"70"}}]
*/

func retrieveFromJsonFile() {
    // Get the url of Persons.json in document directory
    guard let documentsDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let fileUrl = documentsDirectoryUrl.appendingPathComponent("Persons.json")

    // Create a read-only stream
    guard let stream = InputStream(url: fileUrl) else { return }
    stream.open()
    defer {
        stream.close()
    }

    // Read data from .json file and transform data into an array
    do {
        guard let personArray = try JSONSerialization.jsonObject(with: stream, options: []) as? [[String: [String: String]]] else { return }
        print(personArray) // prints [["person": ["name": "Dani", "age": "24"]], ["person": ["name": "ray", "age": "70"]]]
    } catch {
        print(error)
    }
}

The Playground located in the Github's Save-and-read-JSON-from-Playground repo shows how to save a Swift Array into a json file and how to read a json file and get a Swift Array from it.


If you're like me who doesn't like to use a whole new third-party framework just for a trivial thing like this, here's my solution in vanilla Swift. From creating a .json file in the Documents folder to writing JSON in to it.

let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first!
let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!

let jsonFilePath = documentsDirectoryPath.URLByAppendingPathComponent("test.json")
let fileManager = NSFileManager.defaultManager()
var isDirectory: ObjCBool = false

// creating a .json file in the Documents folder
if !fileManager.fileExistsAtPath(jsonFilePath.absoluteString, isDirectory: &isDirectory) {
    let created = fileManager.createFileAtPath(jsonFilePath.absoluteString, contents: nil, attributes: nil)
    if created {
        print("File created ")
    } else {
        print("Couldn't create file for some reason")
    }
} else {
    print("File already exists")
}

// creating an array of test data
var numbers = [String]()
for var i = 0; i < 100; i++ {
    numbers.append("Test\(i)")
}

// creating JSON out of the above array
var jsonData: NSData!
do {
    jsonData = try NSJSONSerialization.dataWithJSONObject(numbers, options: NSJSONWritingOptions())
    let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding)
    print(jsonString)
} catch let error as NSError {
    print("Array to JSON conversion failed: \(error.localizedDescription)")
}

// Write that JSON to the file created earlier
let jsonFilePath = documentsDirectoryPath.URLByAppendingPathComponent("test.json")
do {
    let file = try NSFileHandle(forWritingToURL: jsonFilePath)
    file.writeData(jsonData)
    print("JSON data was written to teh file successfully!")
} catch let error as NSError {
    print("Couldn't write to file: \(error.localizedDescription)")
}

I recommend that you use SwiftyJSON framework. Study its documentation and in addition learn how to write strings to files (hint: NSFileHandle)

Something like the code below, but you really need to study both SwiftyJSON and NSFileHandle to learn how to both serialize JSON data to a file and parse JSON data from a file

let levels = ["unlocked", "locked", "locked"]
let json = JSON(levels)
let str = json.description
let data = str.dataUsingEncoding(NSUTF8StringEncoding)!
if let file = NSFileHandle(forWritingAtPath:path) {
    file.writeData(data)
} 

In Swift 4 this is already built-in with JSONEncoder.

let pathDirectory = getDocumentsDirectory()
try? FileManager().createDirectory(at: pathDirectory, withIntermediateDirectories: true)
let filePath = pathDirectory.appendingPathComponent("levels.json")

let levels = ["unlocked", "locked", "locked"]
let json = try? JSONEncoder().encode(levels)

do {
     try json!.write(to: filePath)
} catch {
    print("Failed to write JSON data: \(error.localizedDescription)")
}

func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    return paths[0]
}

The object you're trying to encode must conform to the Encodable protocol.

Read Apple's official guide on how to extend existing objects to be encodable.

Tags:

Json

Xcode

Swift