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.