swift 3.1 how to get array or dictionary from CSV - csv

How could I use data in this kind of CSV file? Or how could I print for example row 2 value for "inside" column and assign it to a property / entity?
I have this kind of file I got from excel file converted to Numbers, I'd like to grab data for each column and use them.
The original CSV file opened in numbers:
The console output I got:
Using this methods:
func readDataFromCSV(fileName:String, fileType: String)-> String!{
guard let filepath = Bundle.main.path(forResource: fileName, ofType: fileType)
else {
return nil
}
do {
var contents = try String(contentsOfFile: filepath, encoding: .utf8)
contents = cleanRows(file: contents)
return contents
} catch {
print("File Read Error for file \(filepath)")
return nil
}
}
func cleanRows(file:String)->String{
var cleanFile = file
cleanFile = cleanFile.replacingOccurrences(of: "\r", with: "\n")
cleanFile = cleanFile.replacingOccurrences(of: "\n\n", with: "\n")
// cleanFile = cleanFile.replacingOccurrences(of: ";;", with: "")
// cleanFile = cleanFile.replacingOccurrences(of: ";\n", with: "")
return cleanFile
}
SOLUTION thanks to Jens Meder
using
func csv(data: String) -> [[String]] {
var result: [[String]] = []
let rows = data.components(separatedBy: "\n")
for row in rows {
let columns = row.components(separatedBy: ";")
result.append(columns)
}
return result
}
in viewDidLoad
var data = readDataFromCSV(fileName: kCSVFileName, fileType: kCSVFileExtension)
data = cleanRows(file: data!)
let csvRows = csv(data: data!)
print(csvRows[1][1]) // UXM n. 166/167

What you want to do is splitting up the string in rows and then into columns (basically a two dimensional array of Strings). Swift already provides the components method for that on String structs.
func csv(data: String) -> [[String]] {
var result: [[String]] = []
let rows = data.components(separatedBy: "\n")
for row in rows {
let columns = row.components(separatedBy: ";")
result.append(columns)
}
return result
}
Then you can access any value via:
var data = readDataFromCSV(fileName: kCSVFileName, fileType: kCSVFileExtension)
data = cleanRows(file: data)
let csvRows = csv(data: data)
print(csvRows[1][1]) //UXM n. 166/167.

Swift 4
Sometime CSV file is more complicated such as special characters (e.g. comma), the values are surrounded by double quotes as examples below:
Hello, "Complicated String, with a comma inside", 123
In this case, I use:
let dataString: String! = String.init(data: data!, encoding: .utf8)
var items: [(String, String, String)] = []
let lines: [String] = dataString.components(separatedBy: NSCharacterSet.newlines) as [String]
for line in lines {
var values: [String] = []
if line != "" {
if line.range(of: "\"") != nil {
var textToScan:String = line
var value:NSString?
var textScanner:Scanner = Scanner(string: textToScan)
while textScanner.string != "" {
if (textScanner.string as NSString).substring(to: 1) == "\"" {
textScanner.scanLocation += 1
textScanner.scanUpTo("\"", into: &value)
textScanner.scanLocation += 1
} else {
textScanner.scanUpTo(",", into: &value)
}
values.append(value! as String)
if textScanner.scanLocation < textScanner.string.count {
textToScan = (textScanner.string as NSString).substring(from: textScanner.scanLocation + 1)
} else {
textToScan = ""
}
textScanner = Scanner(string: textToScan)
}
// For a line without double quotes, we can simply separate the string
// by using the delimiter (e.g. comma)
} else {
values = line.components(separatedBy: ",")
}
// Put the values into the tuple and add it to the items array
let item = (values[0], values[1], values[2])
items.append(item)
print(item.1)
print(item.2)
print(item.3)
}
}
It is just written in Swift 4, the original is from https://www.appcoda.com/core-data-preload-sqlite-database/

Starting from iOS15 there is a new Official framework called TabularData, try to use it.
import TabularData
let url = Bundle.main.url(forResource: "csvFileName", withExtension: "csv")!
let result = try? DataFrame(contentsOfCSVFile: url)
print(result)
Then you can convert them into the data model you need
More Detail About TabularData(WWDC)

Swift 5.0
.scanLocaion and .scanUpTo() were deprecated in iOS13. Here's a working version of Chhaileng's answer.
func openCSV(fileName:String, fileType: String)-> String!{
guard let filepath = Bundle.main.path(forResource: fileName, ofType: fileType)
else {
return nil
}
do {
let contents = try String(contentsOfFile: filepath, encoding: .utf8)
return contents
} catch {
print("File Read Error for file \(filepath)")
return nil
}
}
func parseCSV(){
let dataString: String! = openCSV(fileName: "MeislinDemo", fileType: "csv")
var items: [(String, String, String)] = []
let lines: [String] = dataString.components(separatedBy: NSCharacterSet.newlines) as [String]
for line in lines {
var values: [String] = []
if line != "" {
if line.range(of: "\"") != nil {
var textToScan:String = line
var value:String?
var textScanner:Scanner = Scanner(string: textToScan)
while !textScanner.isAtEnd {
if (textScanner.string as NSString).substring(to: 1) == "\"" {
textScanner.currentIndex = textScanner.string.index(after: textScanner.currentIndex)
value = textScanner.scanUpToString("\"")
textScanner.currentIndex = textScanner.string.index(after: textScanner.currentIndex)
} else {
value = textScanner.scanUpToString(",")
}
values.append(value! as String)
if !textScanner.isAtEnd{
let indexPlusOne = textScanner.string.index(after: textScanner.currentIndex)
textToScan = String(textScanner.string[indexPlusOne...])
} else {
textToScan = ""
}
textScanner = Scanner(string: textToScan)
}
// For a line without double quotes, we can simply separate the string
// by using the delimiter (e.g. comma)
} else {
values = line.components(separatedBy: ",")
}
// Put the values into the tuple and add it to the items array
let item = (values[0], values[1], values[2])
items.append(item)
print(item.0)
print(item.1)
print(item.2)
}
}
}


This is for CSV file for swift 4.2
var dataArray = [[String]]()
if let path = Bundle.main.path(forResource: "file", ofType: "csv") {
dataArray = []
let url = URL(fileURLWithPath: path)
do {
let data = try Data(contentsOf: url)
let dataEncoded = String(data: data, encoding: .utf8)
if let dataArr = dataEncoded?.components(separatedBy: "\r\n").map({ $0.components(separatedBy: ";") }) {
for line in dataArr {
dataArray.append(line)
}
}
} catch let jsonErr {
print("\n Error reading CSV file: \n ", jsonErr)
}
}

Related

How do I correctly write this data to a JSON file without overwriting the file?

I am writing a JSON file to documents directory, I would like to keep it in one file and read it later. The struct looks like this:
struct SymptomD:Codable
{
var symptom:String
var severity:String
var comment:String
var timestamp:String
}
Then I write to documents like so:
var completeData = SymptomD(symptom: "", severity: "", comment: "", timestamp: "")
func writeTrackedSymptomValues(symptom: String, comment: String, time: String, timestamp: String) {
completeData.symptom = symptom
completeData.severity = self.severity
completeData.comment = comment
completeData.timestamp = timestamp
createJSON()
}
var logFile: URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
let fileName = "symptom_data.json"
return documentsDirectory.appendingPathComponent(fileName)
}
func createJSON() {
guard let logFile = logFile else {
return
}
let jsonData = try! JSONEncoder().encode(completeData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(completeData) //This does not work, I am not sure how to add data without overwriting the previous file.
fileHandle.closeFile()
}
} else {
do {
try JSONEncoder().encode(completeData)
.write(to: logFile)
} catch {
print(error)
}
}
}
With this I can only add the data once, I am not sure how I should go about adding another 'row' basically to the JSON file, so that I can read these and decode them with my struct for use in a tableView later. The JSON file made looks like this:
What is a way I can call the createJSON function again, without overwriting the whole file, and how should I go about organising this so that when I read the JSON I can decode it simply and access the info.
Update:
Using this I am able to add more lines to the JSON,
let jsonData = try! JSONEncoder().encode(completeData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(jsonData)
fileHandle.closeFile()
}
Giving me this:
{"timestamp":"1592341465","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592342433","comment":"","severity":"Moderate","symptom":"Anxiety"}{"timestamp":"1592342458","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592343853","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592329440","comment":"","severity":"Mild","symptom":"Fatigue"}{"timestamp":"1592344328","comment":"","severity":"Mild","symptom":"Mood Swings"}{"timestamp":"1592257920","comment":"test","severity":"Mild","symptom":"Anxiety"}
But when trying to parse this, it crashes with an error:
Code=3840 "Garbage at end."
What am I doing wrong?
The issue looks pretty clear to me. You are appending another dictionary to an existing dictionary but you should have created an array of dictionaries to be able to append a dictionary to it.
struct SymptomD: Codable {
var symptom, severity, comment, timestamp: String
init(symptom: String = "", severity: String = "", comment: String = "", timestamp: String = "") {
self.symptom = symptom
self.severity = severity
self.comment = comment
self.timestamp = timestamp
}
}
If you would like to manually append the text to your json string you will need to seek to the position before the end of your file, add a comma before the next json object and a closed bracket after it:
extension SymptomD {
func write(to url: URL) throws {
if FileManager.default.fileExists(atPath: url.path) {
let fileHandle = try FileHandle(forWritingTo: url)
try fileHandle.seek(toOffset: fileHandle.seekToEndOfFile()-1)
let data = try JSONEncoder().encode(self)
fileHandle.write(Data(",".utf8) + data + Data("]".utf8))
fileHandle.closeFile()
} else {
try JSONEncoder().encode([self]).write(to: url)
}
}
}
Playground testing:
var logFile: URL? {
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("symptom_data.json")
}
var symptomD = SymptomD()
symptomD.symptom = "Anxiety"
symptomD.severity = "Mild"
symptomD.timestamp = .init(Date().timeIntervalSince1970)
do {
if let logFile = logFile {
try symptomD.write(to: logFile)
}
} catch {
print(error)
}
var symptomD2 = SymptomD()
symptomD2.symptom = "Depression"
symptomD2.severity = "Moderate"
symptomD2.timestamp = .init(Date().timeIntervalSince1970)
do {
if let logFile = logFile {
try symptomD2.write(to: logFile)
}
} catch {
print(error)
}
do {
if let logFile = logFile {
let symptoms = try JSONDecoder().decode([SymptomD].self, from: .init(contentsOf: logFile))
print(symptoms)
}
} catch {
print(error)
}
This will print:
[__lldb_expr_532.SymptomD(symptom: "Anxiety", severity: "Mild",
comment: "", timestamp: "1592356106.9662929"),
__lldb_expr_532.SymptomD(symptom: "Depression", severity: "Moderate", comment: "", timestamp: "1592356106.978864")]
edit/update:
If you need to update a single "row" of your JSON, you will need to make your struc conform to equatable, read your collection and find its index:
extension SymptomD: Equatable {
static func ==(lhs: SymptomD, rhs: SymptomD) {
(lhs.symptom, lhs.severity, lhs.comment ,lhs.timestamp) ==
(rhs.symptom, rhs.severity, rhs.comment ,rhs.timestamp)
}
#discardableResult
mutating func updateAndWrite(symptom: String? = nil, severity: String? = nil, comment: String? = nil, timestamp: String? = nil, at url: URL) throws -> [SymptomD]? {
var symptoms = try JSONDecoder().decode([SymptomD].self, from: .init(contentsOf: url))
if let index = symptoms.firstIndex(of: self) {
self.symptom = symptom ?? self.symptom
self.severity = severity ?? self.severity
self.comment = comment ?? self.comment
self.timestamp = timestamp ?? self.timestamp
symptoms[index] = self
try JSONEncoder().encode(symptoms).write(to: url, options: .atomic)
return symptoms
}
return nil
}
}

Get user ID from JWT (JSON web token)

I am using the plugin to authenticate WordPress using api-rest : JWT Authentication for WP REST API
From the request to the server I get the following answer:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvbWlob3N0Lm9yZ1wvcHJ1ZWJhcyIsImlhdCI6MTU1MzcyNDM4MSwibmJmIjoxNTUzNzI0MzgxLCJleHAiOjE1NTQzMjkxODEsImRhdGEiOnsidXNlciI6eyJpZCI6IjIifX19.rgi5Q2c8RCoHRp-lJiJN8xQaOavn9T_q8cmf8v1-57o",
"user_email": "abc#test.com",
"user_nicename": "test",
"user_display_name": "Test"
}
So far everything works fine, but I need to know the user ID.
I have read that the token is coded in base64 and within this is the ID. Trying to decode, I see if the ID that I need is there.
In swift with this function I decode the token, but I can not get the dictionary ID.
func decode(_ token: String) -> [String: AnyObject]? {
let string = token.components(separatedBy: ".")
let toDecode = string[1] as String
var stringtoDecode: String = toDecode.replacingOccurrences(of: "-", with: "+") // 62nd char of encoding
stringtoDecode = stringtoDecode.replacingOccurrences(of: "_", with: "/") // 63rd char of encoding
switch (stringtoDecode.utf16.count % 4) {
case 2: stringtoDecode = "\(stringtoDecode)=="
case 3: stringtoDecode = "\(stringtoDecode)="
default: // nothing to do stringtoDecode can stay the same
print("")
}
let dataToDecode = Data(base64Encoded: stringtoDecode, options: [])
let base64DecodedString = NSString(data: dataToDecode!, encoding: String.Encoding.utf8.rawValue)
var values: [String: AnyObject]?
if let string = base64DecodedString {
if let data = string.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: true) {
values = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String : AnyObject]
}
}
return values
}
The dictionary that returns this function is:
["iss": https://myhost.me/test, "exp": 1554235730, "nbf": 1553630930, "iat": 1553630930, "data": {
user = {
id = 2;
};
}]
How do I get the ID from this dictionary?
Your code is pretty unswifty.
Basically don't use NS... classes in Swift if there is a native equivalent and a JSON dictionary is always value type ([String:Any]).
I recommend to add an Error enum, make the function can throw, decode the serialized token with Decodable and return the Token instance on success
struct Token : Decodable {
let data : UserData
struct UserData : Decodable {
let user : User
struct User : Decodable {
let id : String
}
}
}
You are encouraged to keep the parameter label in the method declaration
enum TokenError : Error {
case invalidJWTFormat, invalidBase64EncodedData
}
func decode(token: String) throws -> Token {
let components = token.components(separatedBy: ".")
guard components.count == 3 else { throw TokenError.invalidJWTFormat }
var decodedString = components[1]
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
while decodedString.utf16.count % 4 != 0 {
decodedString += "="
}
guard let decodedData = Data(base64Encoded: decodedString) else { throw TokenError.invalidBase64EncodedData }
return try JSONDecoder().decode(Token.self, from: decodedData)
}
and call it
do {
let userID = try decode(token: "eyJ0eXAi.....").data.user.id
} catch { print(error) }

swift 3 server response with strange dictionary format

Will be grateful for help
I get server response with strange dictionary format
Data: {
"featureHelpshift" = "true";
"featureAppleTVSynchronisation" = "false";
"featureDirectPlay" = "true";
"googleLoginSwitchOn" = "true";
"serverVersion" = "6.11.1";
"facebookLoginSwitchOn" = "true";
"allAvailableWorlds" = (
{
"name" = "\U0420\U043e\U0441\U0441\U0438\U044f 16 (\U043d\U043e\U0432\U044b\U0439)";
"mapURL" = "https://backend2.lordsandknights.com/maps/LKWorldServer-RU-16";
"worldStatus" = {
"id" = "3";
"description" = "online";
};
"country" = "RU";
"language" = "ru";
"id" = "190";
"url" = "https://backend2.lordsandknights.com/XYRALITY/WebObjects/LKWorldServer-RU-16.woa";
},...
first i try to serialize this response to JSON with swiftyJSON and json serializer
Than i get response as Data and parse it to String
func some () {
Alamofire.request(serverRequest, method: .post, parameters: parameters).responseData { response in
if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) {
//print("Data: \(utf8Text)") //- server response that shown above i print here
print("Change->")
let str = self.responseToJsonParsing(stringResponse: utf8Text)
print("Data: \(str)")
}
}
}
i try to lead my data string response to JSON string format, create function for parsing:
func responseToJsonParsing(stringResponse: String) -> String {
var parseResponse = stringResponse.replacingOccurrences(of: ";\n\t\t}", with: "\n\t\t}", options: .literal)
parseResponse = parseResponse.replacingOccurrences(of: ";\n\t\t\t}", with: "\n\t\t\t}", options: .literal)
parseResponse = parseResponse.replacingOccurrences(of: ";\n\t}", with: "\n\t}", options: .literal)
parseResponse = parseResponse.replacingOccurrences(of: ";\n}", with: "\n}", options: .literal)
parseResponse = parseResponse.replacingOccurrences(of: ";", with: ",")
parseResponse = parseResponse.replacingOccurrences(of: "=", with: ":")
parseResponse = parseResponse.replacingOccurrences(of: "(", with: "[")
parseResponse = parseResponse.replacingOccurrences(of: ")", with: "]")
return parseResponse
}
after parsing i still cant create JSON or Dictionary

How to turn my JSON result into an array or object

I have been searching every where, but cannot get to the right answer. I receive a Json result with the following structure
{
result = {
"2ab5a003-0120-4c01-80f2-a237dcf4ba14" = {
icon = garden;
id = "2ab5a003-0120-4c01-80f2-a237dcf4ba14";
index = 1;
name = "Tafel hoek";
parent = "855113f1-f488-4223-b675-2f01270f573e";
};
"2afd6093-ca6d-4e52-aaca-336ab76ea454" = {
icon = default;
id = "2afd6093-ca6d-4e52-aaca-336ab76ea454";
index = 11;
name = Badkamer;
parent = "9919ee1e-ffbc-480b-bc4b-77fb047e9e68";
};
};
status = 200;
}
Because I don't know the 1st key in the result I am lost to get the separate items. Is there any one who can help me ? Thanks in advance
my code is:
{ print("Error: ")
print(error!)
} else { // no error
if let urlContent = data { // 3
do { // 4
let json = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(json)
} catch {
print("JSON processing failed")
} // end 4 do
} // end 3 let urlContent
} // end 2 if error}
Since you have the dictionary for key result, you can enumerate it as usual:
if let result = json["result"] as? [String:[String:Any]] {
for (key, value) in result {
let name = value["name"] as! String
let index = value["index"] as! Int
print (key, name, index)
}
}
Can you post your code ,i think that is not a valid json format and tell me where you are struggling to parse.ignore that key and try as usual

Getting unexpected conversion from JSON to dictionary and back to JSON

I am trying to read a JSON file from myFile.json in Bundle, modify some elements and store it in the document directory as a JSON file. I thought that it would be a simple way to persist the data. Subsequently, I intend to read and write to document directory. The following code shows what I did and is heavily commented. It seems I am missing some essential step because the expected JSON conversions does not match the JSON spec. I am open to suggestions on how to I tested in the playground. The code is based on
Convert Dictionary to JSON in Swift
import UIKit
/* Trying to read a json file from myFile.json in Bundle, modify some elemnts and store it
in the document directory as a json file. Subsequently, intent to read and write to document directory
myFile.json consistes of
{
"record": {"id": "A1234", "customer": "Customer-1"}
}
*/
typealias MyRecord = [String: AnyObject]
var json:MyRecord!
let fileName = "myFile"
var dictionary = MyRecord()
func loadJsonFromBundle (forFilename fileName: String) -> MyRecord {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
if let data = NSData(contentsOf: url) {
do {
let dictionary = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as? [String:Any]
print("dictionary = \(dictionary!)")
/* displays
dictionary = ["record": {
customer = "Customer-1";
id = A1234;
}]
*/
return dictionary! as MyRecord
} catch {
print("Error!! Unable to parse \(fileName).json")
}
}
print("Error!! Unable to load \(fileName).json")
}
return [:]
}
func loadJsonFromDocument (forFilename fileName: String) -> MyRecord {
let docDirectory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
if let url = docDirectory?.appendingPathComponent(fileName).appendingPathExtension("json") {
if let data = NSData(contentsOf: url) {
do {
let dictionary = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as? [String:Any]
print("dictionary = \(dictionary!)")
return dictionary! as MyRecord
} catch {
print("Error!! Unable to parse \(fileName).json")
}
}
print("Error!! Unable to load \(fileName).json")
}
return [:]
}
func saveJsonToFile (_ fileName:String, outString: String) -> URL {
let docDirectory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
if let fileURL = docDirectory?.appendingPathComponent(fileName).appendingPathExtension("json") {
print("fileURL = \(fileURL)")
// Write to a file on disk
do {
try outString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
}
return fileURL
}
return URL(string: "")!
}
let sq = "\""
func q(_ x:String) -> String {
return "\(sq)\(x)\(sq)"
}
dictionary = loadJsonFromBundle (forFilename: fileName)
var a = dictionary["record"] as? [String:String]
a?["customer"] = "newName"
var dict = MyRecord()
dict["record"] = a as AnyObject?
print(dict)
/* prints:
["record": {
customer = newName;
id = A1234;
}]
*/
// https://stackoverflow.com/questions/29625133/convert-dictionary-to-json-in-swift/29628000#29628000
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
// here "decoded" is of type `Any`, decoded from JSON data
// you can now cast it with the right type
if let dictFromJSON = decoded as? [String:Any] {
// need to save dictFromJson to a file in document directory
// saveJsonToFile is expecting a String for the json argument
// I converted dictFromJson to a string so I can save it
var outString = String(describing: dictFromJSON)
print("outString = \(outString)")
/* Notice that the internal structure is not quoted and there are semi-colons
outString = ["record": {
customer = newName;
id = A1234;
}]
*/
outString = outString.replacingOccurrences(of: "[", with: "{")
outString = outString.replacingOccurrences(of: "]", with: "}")
let url = saveJsonToFile("newFile", outString: String(describing: outString) )
print(url)
/* Resulting File looks like this:
{"record": {
customer = newName;
id = A1234;
}}
Question: Why were the braces swapped with brackets. The internal elements
were not quoted.
*/
// Will try to read back the json string from document directory
dictionary = loadJsonFromDocument(forFilename: fileName)
// results in ERROR (Unable to load myFile.json
a = dictionary["record"] as? [String:String]
a?["customer"] = "newName"
dict = MyRecord()
dict["record"] = a as AnyObject?
print(dict)
}
} catch {
print(error.localizedDescription)
}
Issue pointed by vadian is correct that you are storing the Dictionary object, but instead of converting Data to String and then write that String you can directly write Data in DocumentDirectory.
So i have changed your saveJsonToFile function which accept Data as second argument instead of String.
func saveJsonToFile (_ fileName:String, jsonData: Data) -> URL {
let docDirectory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
if let fileURL = docDirectory?.appendingPathComponent(fileName).appendingPathExtension("json") {
print("fileURL = \(fileURL)")
// Write to a file on disk
do {
try jsonData.write(to: fileURL)
} catch {
print("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
}
return fileURL
}
return URL(string: "")!
}
Now simply call this function after you change your json result and convert that to data.
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
let url = saveJsonToFile("newFile", jsonData: jsonData )
print(url)
} catch {
print(error.localizedDescription)
}
The issue is that you save a dictionary object to file rather than encoded JSON.
Use only the JSONSerialization from object to data and do not pass .prettyprinted.
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options:[])
// here "jsonData" is the dictionary encoded in JSON data
let outString = String(data:jsonData, encoding:.utf8)
print("outString = \(outString)")
let url = saveJsonToFile("newFile", outString: outString )
print(url)
}
} catch {
print(error.localizedDescription)
}