In an app I created to collect data from Apple pencil input, I tried to export the data into a CSV file. But so far, I only managed to create a single column which records the time length. I want to add another column to record the force from the Apple pencil.
This is what I have tried to do:
var patientsData:[Dictionary<String, AnyObject>] = Array()
var dct = Dictionary<String, AnyObject>()
// MARK: CSV writing
func createCSVX(from recArray:[Dictionary<String, AnyObject>]) {
var csvString = "\("Time")\n"
dct.updateValue(TestDraw.time as AnyObject, forKey: "T")
csvString = csvString.appending("\(String(describing: dct["T"]))\n")
patientsData.append(dct)
let fileManager = FileManager.default
do {
let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileURL = path.appendingPathComponent("TrailTime.csv")
try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("error creating file")
}
}
I know I can write another function to create another CSV file with a single column to record the force, but I would like to record them in a single spreadsheet​.
Also, does anyone know how to remove the "Optional" in the CSV file created?
This is what I have tried based on one of the answers.
func createCSVX(from recArray:[Dictionary<String, AnyObject>]) {
var csvString = "\("Time"),\("Force")\n"
dct.updateValue(TestDraw.time as AnyObject, forKey: "T")
dct.updateValue(TestDraw.force as AnyObject, forKey: "F")
patientsData.append(dct)
csvString = csvString.appending("\(String(describing: dct["T"])), \(String(describing: dct["F"]))\n")
let fileManager = FileManager.default
do {
let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil , create: false )
let fileURL = path.appendingPathComponent("TrailTime.csv")
try csvString.write(to: fileURL, atomically: true , encoding: .utf8)
} catch {
print("error creating file")
}
print(TestDraw.force)
}
Tutorial copied from https://iostutorialjunction.com/2018/01/create-csv-file-in-swift-programmatically.html:
Step 1:
Create an array, named as "employeeArray" which will store all our records for the employees as key value objects. Also we will add dummy data to the newly created array
class ViewController: UIViewController {
var employeeArray:[Dictionary<String, AnyObject>] = Array()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
for i in 1...10 {
var dct = Dictionary<String, AnyObject>()
dct.updateValue(i as AnyObject, forKey: "EmpID")
dct.updateValue("NameForEmplyee id = \(i)" as AnyObject, forKey: "EmpName")
employeeArray.append(dct)
}
}
}
Step 2: Now we have data with us, and its time to create CSV(comma separated values) file using swift programmatically. For this we will loop through our records in "employeeArray" and append them in a string. Then we will write this string to our document directory of the app. All the stuff goes in different function named as "createCSV", below is the code for the same
func createCSV(from recArray:[Dictionary<String, AnyObject>]) {
var csvString = "\("Employee ID"),\("Employee Name")\n\n"
for dct in recArray {
csvString = csvString.appending("\(String(describing: dct["EmpID"]!)) ,\(String(describing: dct["EmpName"]!))\n")
}
let fileManager = FileManager.default
do {
let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileURL = path.appendingPathComponent("CSVRec.csv")
try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("error creating file")
}
}
Step 3: Finally we will call our function from "viewDidLoad". Below is the complete code
class ViewController: UIViewController {
var employeeArray:[Dictionary<String, AnyObject>] = Array()
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...10 {
var dct = Dictionary<String, AnyObject>()
dct.updateValue(i as AnyObject, forKey: "EmpID")
dct.updateValue("NameForEmplyee id = \(i)" as AnyObject, forKey: "EmpName")
employeeArray.append(dct)
}
createCSV(from: employeeArray)
}
func createCSV(from recArray:[Dictionary<String, AnyObject>]) {
var csvString = "\("Employee ID"),\("Employee Name")\n\n"
for dct in recArray {
csvString = csvString.appending("\(String(describing: dct["EmpID"]!)) ,\(String(describing: dct["EmpName"]!))\n")
}
let fileManager = FileManager.default
do {
let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileURL = path.appendingPathComponent("CSVRec.csv")
try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("error creating file")
}
}
}
Excellent answer above, made a slight modification to address specific instances in the data. You can modify individual components as needed and remove commas, trim UUIDs, etc. Note this solution uses transactions stored in a list of Core Data objects. I also print the location of the data file so you can check it in the simulator.
func createCSVFile() {
var csvString = "id,name,description,category,date,type,receipt,amount\n"
for trans in transactions {
let transID = trans.id!.debugDescription.split(separator: "-")[0].replacingOccurrences(of: ")", with: "")
let transName = trans.name!
let transDesc = trans.desc!.replacingOccurrences(of: ",", with: "-")
let transCat = trans.category!
let transDate = trans.date!
let transType = trans.type!
var transReceipt = "None"
if trans.receipt == nil {
transReceipt = "Present"
}
let transAmount = trans.amount
let dataString = "\(transID),\(transName),\(transDesc),\(transCat),\(transDate),\(transType),\(transReceipt),\(transAmount)\n"
print("DATA: \(dataString)")
csvString = csvString.appending(dataString)
}
let fileManager = FileManager.default
do {
let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
print("PATH: \(path)")
let fileURL = path.appendingPathComponent("CSVData.csv")
try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("error creating file")
}
}
Related
I am using the SKCache library to cache data in my app and I have two warnings that say that NSKeyedArchiver and NSKeyedUnarchiver related code is deprecated and should be updated. I gave it my best attempt, but I wanted to check here to get the correct answer as this is an important function of my app and I do not want to mess it up.
private func save(object: SKObject) throws {
let fileManager = FileManager.default
do {
let cacheDirectory = try fileManager.url(for: .cachesDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileDirectory = cacheDirectory.appendingPathComponent("spacekit")
var fileDir = fileDirectory.absoluteString
let range = fileDir.startIndex..<fileDir.index(fileDir.startIndex, offsetBy: 7)
fileDir.removeSubrange(range)
try createFolderIfNeeded(atPath: fileDir, absolutePath: fileDirectory)
let fileFormatedName = object.key.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? object.key
let convertedFileName = convertToBase64(withString: fileFormatedName).suffix(45).map({ String($0) }).joined()
let fileName = fileDirectory.appendingPathComponent(convertedFileName)
if !fileManager.fileExists(atPath: fileName.absoluteString) || object.isUpdated {
let data = NSKeyedArchiver.archivedData(withRootObject: object)
try? data.write(to: fileName)
}
} catch {
throw Operations.saveFail
}
}
Specifically on this line:
let data = NSKeyedArchiver.archivedData(withRootObject: object)
I was able to come up with this amended version, but I'm not sure if it is correct:
let data = try NSKeyedArchiver.archivedData(withRootObject: object, requiringSecureCoding: false)
Lastly,
private func load() throws {
let fileManager = FileManager.default
do {
let cacheDirectory = try fileManager.url(for: .cachesDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileDirectory = cacheDirectory.appendingPathComponent("spacekit")
var fileDir = fileDirectory.absoluteString
let range = fileDir.startIndex..<fileDir.index(fileDir.startIndex, offsetBy: 7)
fileDir.removeSubrange(range)
try createFolderIfNeeded(atPath: fileDir, absolutePath: fileDirectory)
let paths = try fileManager.contentsOfDirectory(atPath: fileDir)
for path in paths {
if let object = NSKeyedUnarchiver.unarchiveObject(withFile: fileDir + path) as? SKObject {
if !object.isExpired {
add(object: object)
} else {
try? fileManager.removeItem(atPath: fileDir + path)
}
}
}
} catch {
throw Operations.loadFail
}
}
Specifically on this line:
if let object = NSKeyedUnarchiver.unarchiveObject(withFile: fileDir + path) as? SKObject {
I wasn't able to figure anything out on my own related to this as the new method does not seem to have a parameter for a file directory. I appreciate the help in advance.
Updated:
private func load() throws {
let fileManager = FileManager.default
do {
let cacheDirectory = try fileManager.url(for: .cachesDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileDirectory = cacheDirectory.appendingPathComponent("spacekit")
var fileDir = fileDirectory.absoluteString
let range = fileDir.startIndex..<fileDir.index(fileDir.startIndex, offsetBy: 7)
fileDir.removeSubrange(range)
try createFolderIfNeeded(atPath: fileDir, absolutePath: fileDirectory)
let paths = try fileManager.contentsOfDirectory(atPath: fileDir)
for path in paths {
let objData = try Data(contentsOf: URL(fileURLWithPath: fileDir + path))
if let object = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [SKObject.self], from: objData) as? SKObject {
if !object.isExpired {
add(object: object)
} else {
try? fileManager.removeItem(atPath: fileDir + path)
}
}
}
} catch {
throw Operations.loadFail
}
}
As for the first function, you've updated it correctly.
As for the second one,
func unarchivedObject(ofClasses classes: [AnyClass], from data: Data) throws -> Any? now requires Data param.
And you have a path to the file where that Data is stored.
So you can update the code like this:
let objData = try Data(contentsOf: URL(fileURLWithPath: fileDir + path))
if let object = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [MyClass.self], from: objData) {
// check if object.isExpired, etc
}
I'd like to notice that if it's a legacy code and you have to support it, you could add some kind of unit test for this archiving/unarchiving, make it work, and then quickly update the deprecated code while being sure that the test works.
Whenever the user exports data (it is a JSON format) it creates a folder named "Documents." And does not directly export the file and it does this for both my JSON export and CSV.
I have tried changing the fileManager settings but nothing seems to work that I have tried and that includes changing the default, for, and in. The latter two being when I call fileManger.url
Here is my main export for my JSON
// MARK: - Export to Share
func toExportJSON() {
// Call to clear Chached exported files
clearAllFile()
var exportArrayJson = [exportJsonData]()
var exportArray = [Item]()
exportArray = fetchedRC.fetchedObjects!
for i in exportArray {
let newI = exportJsonData(name: i.name!, pricePer: i.pricePer, totalPrice: i.totalPrice!, isComplete: i.isComplete, Qty: i.quantity, Cat: i.catagory!, Priority: i.priority, DNH: i.didHave)
exportArrayJson.append(newI)
}
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
encoder.keyEncodingStrategy = .convertToSnakeCase
var jsonDataTop = Data()
do {
let jsonData = try encoder.encode(exportArrayJson)
jsonDataTop = jsonData
// Old Code to print for testing
///if let jsonString = String(data: jsonData, encoding: .utf8) {
///print(jsonString)
///print(jsonData)
///}
let fileManager = FileManager.default
do {
let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileURL = path.appendingPathComponent("\(detailedList.lname!).json")
try jsonDataTop.write(to: fileURL)//.write(to: fileURL)//write(to: fileURL, encoding: .utf8)
let vc = UIActivityViewController(activityItems: [path], applicationActivities: [])
present(vc, animated: true, completion: nil)
} catch {
print("error creating file")
}
} catch {
print(error.localizedDescription)
}
}
I expect this to export the JSON by its self and not in a folder. I want only the file so it is easier for the user to share.
No matter where you save the file, you should delete it when the UIActivityViewController is finished. You can set a completion handler to remove the file.
vc.completionWithItemsHandler = { (_, _, _, _) in
try? fileManager.removeItem(at: fileURL)
}
Another option would be to save the file in the temporaryDirectory instead of documents.
let fileURL = path
.temporaryDirectory
.appendingPathComponent("\(detailedList.lname!).json")
But you should still remove the file after.
I have an app that can export user input information in a csv file. I set the file name to be PatientInfo in the code. However when I receive new data and click save again. The old one will be overwritten. How to automatically create a new csv file with a name perhaps PatientInfo_01? Or even better, let the user type in the file name after clicking save button.
This is the original code I have:
var patientsData:[Dictionary<String, AnyObject>] = Array()
var dct = Dictionary<String, AnyObject>()
func createCSVX(from recArray:[Dictionary<String, AnyObject>]) {
var csvString = "\("Time"),\("Force")\n"
dct.updateValue(-TestDraw.time as AnyObject, forKey: "T")
for i in 0...TestDraw.force.count-1 {
dct.updateValue(TestDraw.force[i] as AnyObject, forKey: "F")
patientsData.append(dct)
csvString = csvString.appending("\(String(describing: dct["T"]!)), \(String(describing: dct["F"]!))\n")
}
let fileManager = FileManager.default
do {
let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileURL = path.appendingPathComponent("PatientInfo.csv")
try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("error creating file")
}
}
Try this:
var counter = 0
let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileName = String(format:"PatientInfo%d.csv",counter)
let fileURL = path.appendingPathComponent(fileName)
try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
And in the next iteration, increase the counter value by 1.
So, i have an empty plist, i am trying to create these values in the plist
using this code :
let dictionary:[String:String] = ["key1" : "value1", "key2":"value2", "key3":"value3"]
let documentDirectoryURL = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentDirectoryURL.appendingPathComponent("dictionary.plist")
if NSKeyedArchiver.archiveRootObject(dictionary, toFile: fileURL.path) {
print(true)
}
if let loadedDic = NSKeyedUnarchiver.unarchiveObject(withFile: fileURL.path) as? [String:String] {
print(loadedDic) // "["key1": "value1", "key2": "value2", "key3": "value3"]\n"
}
everything is fine here, but the question is, when i click the plist in my xcode project, its empty, these values are only printed not inserted to the plist
NSKeyedUnarchiver is the wrong way to save property lists.
There is a dedicated struct PropertyListSerialization to load and save property lists.
First declare a computed property plistURL
var plistURL : URL {
let documentDirectoryURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
return documentDirectoryURL.appendingPathComponent("dictionary.plist")
}
and two methods for loading and saving
func savePropertyList(_ plist: Any) throws
{
let plistData = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)
try plistData.write(to: plistURL)
}
func loadPropertyList() throws -> [String:String]
{
let data = try Data(contentsOf: plistURL)
guard let plist = try PropertyListSerialization.propertyList(from: data, format: nil) as? [String:String] else {
return [:]
}
return plist
}
Create the dictionary and save it
do {
let dictionary = ["key1" : "value1", "key2":"value2", "key3":"value3"]
try savePropertyList(dictionary)
} catch {
print(error)
}
To update a value read it, update the value and save it back
do {
var dictionary = try loadPropertyList()
dictionary.updateValue("value4", forKey: "key4")
try savePropertyList(dictionary)
} catch {
print(error)
}
Have you tried using PropertyListEncoder instead of NSKeyedArchiver?
do {
try PropertyListEncoder().encode(dictionary).write(to: fileURL)
} catch {
print(error)
}
Decode:
do {
let data = try Data(contentsOf: fileURL)
try PropertyListDecoder().decode([String: String].self, from: data)
} catch {
// Handle error
}
Here is my answer
//MARK: User.plist Save & retrive data
func saveUserDataWithParams(userData: AnyObject) -> Void {
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let path : NSString = documentsDirectory.appendingPathComponent("User.plist") as NSString
//userData.write(path as String, atomically: true)
userData.write(toFile: path as String, atomically: true)
}
func getUserPlistData() -> NSMutableDictionary {
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let path : NSString = documentsDirectory.appendingPathComponent("User.plist") as NSString
let fileManager = FileManager.default
if (!(fileManager.fileExists(atPath: path as String)))
{
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let path : NSString = documentsDirectory.appendingPathComponent("User.plist") as NSString
let data : NSMutableDictionary = NSMutableDictionary()
data.write(toFile: path as String, atomically: true)
}
let data : NSMutableDictionary = NSMutableDictionary(contentsOfFile: path as String)!
return data
}
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?
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);
}
}
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"]