how to write data in the local json file iOS Swift? - swift

if let path = Bundle.main.path(forResource: "domaines", ofType: "json") {
if JSONSerialization.isValidJSONObject(dict){
do{
let rawData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
try rawData.write(to: URL(fileURLWithPath: path))
}catch{
}
}else{
}
}else{
print("file not present")
}
This is the code used by me but I'm not able to save the data to the local JSON file.

well, I created a class by which you can create a JSON file then add data to bypassing an array to it and you can update it by replacing data with the new data. check out my code and comment if you don’t understand anything.
it's easy to understand,
it's easy to implement.
// offlineJsonFileManager.swift
// BuzCard
//
// Created by ap00724 on 06/02/20.
// Copyright © 2020 ap00724. All rights reserved.
//
import Foundation
import UIKit
class offlineJsonFileManager: NSObject {
static let sharedManager = offlineJsonFileManager()
func saveToJsonFile(fileName:String,dict:[[String:Any]]) {
guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileUrl = documentDirectoryUrl.appendingPathComponent("\(fileName).json")
let personArray = dict
// 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)
}
}
func retrieveFromJsonFile(fileName:String,completion:(Bool,[[String:Any]])->()) {
// Get the url of Persons.json in document directory
guard let documentsDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
completion(false,[["error":"file does not exist."]]);return }
let fileUrl = documentsDirectoryUrl.appendingPathComponent("\(fileName).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:Any]] else { return }
completion(true,personArray)
} catch {
print(error)
completion(false,[["error":"\(error)"]])
}
}
}

private func saveDomaines(json: [[String: Any]]) {
if let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last {
let fileURL = documentsDirectory.appendingPathComponent("domaines.json")
print("File exists")
writeFile(fileURL: fileURL, json: json)
} else {
print("Shouldn't reach here")
}
}
private func writeFile(fileURL: URL, json: [[String: Any]]) {
do {
if let jsonData = try JSONSerialization.data(withJSONObject: json, options: .init(rawValue: 0)) as? Data {
try jsonData.write(to: fileURL)
}
} catch {
print(error.localizedDescription)
}
}

Related

CoreData [Int64] transformable, transform to usable array outside of Swift

I have a CoreData entity with various values. Sometimes I want to export the values to Json to share with another app. My Transformed [Int64] turns into data.
How can I transform it back? For instance in javascript?
I've solved this by writing an export to json directly from the app. Then the array is intact.
let dir = FileManager.default.urls(for: .documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first!
let fileurl = dir.appendingPathComponent("export.json")
var array: [String] = ["[\n"]
for value in values
if let value = (value.toJSON()) {
array.append(value + ",\n")
}
}
array.append("]")
let data = array.joined().data(using: .utf8, allowLossyConversion: false)!
if FileManager.default.fileExists(atPath: fileurl.path) {
if let fileHandle = try? FileHandle(forUpdating: fileurl) {
fileHandle.seekToEndOfFile()
fileHandle.write(data)
fileHandle.closeFile()
}
} else {
try! data.write(to: fileurl, options: Data.WritingOptions.atomic)
}
Using .toJSON() which I got here CoreData object to JSON in Swift 3 from #Mike_NotGuilty.
extension NSManagedObject {
func toJSON() -> String? {
let keys = Array(self.entity.attributesByName.keys)
let dict = self.dictionaryWithValues(forKeys: keys)
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
let reqJSONStr = String(data: jsonData, encoding: .utf8)
return reqJSONStr
}
catch{}
return nil
}
}
Edit: Forgot, above function has an error because the last value also gets a , at the end, leading to a corrupt JSON. I use something like this:
for i in values.indices {
if i != values.count - 1 {
if let value = (values[i].toJSON()) {
array.append(value + ",\n")
}
} else {
if let value = (values[i].toJSON()) {
array.append(value + "\n")
}
}
}

How to save images in ascending order in swift?

I am building an application in which i am using FileManager to save some images using device camera. So for Now I am saving file name as Doc-Time.
I am using below code,
func saveImageToDocumentDirectory(image: UIImage ) {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "hh:mm:ss"
let fileName = "Doc-" + dateFormatter.string(from: Date())
let fileURL = documentsDirectory.appendingPathComponent(fileName
)
if let data = image.jpegData(compressionQuality: 1.0),!FileManager.default.fileExists(atPath: fileURL.path){
do {
try data.write(to: fileURL)
print("file saved")
} catch {
print("error saving file:", error)
}
}
}
But Here i want to as, Doc-1,Doc-2, Doc-3....
How can i do that?
You can achieve this by simply storing the next index of the image. Like first the index should be 1 when you used named the image as Doc-1 then the index has 2 in it and so on....
One way to store this index in UserDefaults like:
var nextImageIndex: Int {
UserDefaults.standard.integer(forKey: "NextImageIndex") + 1 //+1 if you want to start with 1
}
func incrementImageIndex() {
UserDefaults.standard.setValue(nextImageIndex, forKey: "NextImageIndex")
}
Put the above code somewhere in UIViewController to see it works.
Here is your updated method...
func saveImageToDocumentDirectory(image: UIImage ) {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
let fileName = "Doc-\(nextImageIndex)"
let fileURL = documentsDirectory.appendingPathComponent(fileName)
let fileAlreadyExists = FileManager.default.fileExists(atPath: fileURL.path)
if let data = image.jpegData(compressionQuality: 1.0), !fileAlreadyExists {
do {
try data.write(to: fileURL)
incrementImageIndex()
print("file saved")
} catch {
print("error saving file:", error)
}
}
}
Create a variable to store the document count and increment it every time your save to the document directory, then use that value in the string.
let documentKey = "documentIndex"
#objc var documentIndex: Int {
get { UserDefaults.value(forKey: documentKey) as? Int ?? 0 }
set { UserDefaults.setValue(newValue, forKey: documentKey) }
}
func saveImageToDocumentDirectory(image: UIImage ) {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
documentIndex += 1
let fileName = "Doc-\(documentIndex)"
let fileURL = documentsDirectory.appendingPathComponent(fileName
)
if let data = image.jpegData(compressionQuality: 1.0),!FileManager.default.fileExists(atPath: fileURL.path){
do {
try data.write(to: fileURL)
print("file saved")
} catch {
print("error saving file:", error)
}
}
}

I am trying to delete a pin on map, stored in the documents directory, but I get this error The file “SavedLocations” couldn’t be opened

I am trying to delete a pin on map, I have several of them on the map stored in the documents directory.I want to delete only a single pin at time, but once I call my delete method, I get this error- The file “SavedLocations” couldn’t be opened. How do I delete a single pin saved in the documents directory? Please help, Thank you.
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
//Load method
func loadData() {
print("loaded data")
let filename = getDocumentsDirectory().appendingPathComponent("SavedLocations")
do {
let data = try Data(contentsOf: filename)
locations = try JSONDecoder().decode([CodableMKPointAnnotation].self, from: data)
} catch {
print("Unable to load saved data")
}
}
//Save method
func saveData() {
print("saved data")
do {
let filename = getDocumentsDirectory().appendingPathComponent("SavedLocations")
// print(filename)
let data = try JSONEncoder().encode(self.locations)
try data.write(to: filename, options: [.atomicWrite, .completeFileProtection])
} catch {
print("Unable to save data")
}
}
//Delete method
func delete() {
let fileManager = FileManager.default
let filename = getDocumentsDirectory().appendingPathComponent("SavedLocations")
print("Pin deleted")
print(filename)
do {
let items = try fileManager.contentsOfDirectory(at: filename, includingPropertiesForKeys: .none, options: .skipsHiddenFiles)
print(items)
for item in items {
try fileManager.removeItem(at: item)
print(item)
}
} catch let error {
print("\(error.localizedDescription)")
}
}
Change this method to:
func delete() {
let fileManager = FileManager.default
let filename = getDocumentsDirectory().appendingPathComponent("SavedLocations")
print("Pin deleted")
print(filename)
do {
let items = try fileManager.contentsOfDirectory(at: filename, includingPropertiesForKeys: .none, options: .skipsHiddenFiles)
print(items)
if (newArray.count > 0){
try fileManager.removeItem(at: items[0])
}
} catch let error {
print("\(error.localizedDescription)")
}
}

FileManager.default.fileExists(atPath:) showing nil after saving string to filepath

When I create a filePath for an image then write it to the filePath, it doesn't load the image in the loadImageViewController
saveImageViewController:
let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let documentPath = documentsURL.path
let filePath = documentsURL.appendingPathComponent("profile_image.png")
do {
let files = try fileManager.contentsOfDirectory(atPath: documentPath)
for file in files {
if "\(documentPath)/\(file)" == filePath.path {
try fileManager.removeItem(atPath: filePath.path)
}
}
} catch {
print("Could not add image from document directory: \(error)")
}
do {
if let pngImageData = profilePicImageView.image!.pngData() {
try pngImageData.write(to: filePath, options: .atomic)
}
} catch {
print("couldn't write image")
}
entity.profilePicPath = filePath.path
loadImageViewController:
if FileManager.default.fileExists(atPath: entity.profilePicPath!) {
let contentsOfFilePath = UIImage(contentsOfFile: entity.profilePicPath!)
cell.entityImage.image = contentsOfFilePath
}
I am saving the file path as a string through core data.

Save file to custom folder

In AppDelegate I create hidden folder in .documents if it doesn't exist:
let fileManager = FileManager.default
let path = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let audioKitFilesFolder = path.appendingPathComponent(".AudioKitFilesFolder")
var isDir : ObjCBool = false
if fileManager.fileExists(atPath: audioKitFilesFolder.absoluteString, isDirectory:&isDir) {
if isDir.boolValue {
print("file exists and is a directory")
} else {
print("file exists and is not a directory")
}
} else {
do {
try fileManager.createDirectory(at: audioKitFilesFolder, withIntermediateDirectories: true, attributes: nil)
} catch {
print("Can't Create Folder \(error)")
}
}
In my Networking API I have func that save file from web to .documents. But I need save this file to the my hidden Folder. How I can get path for this folder for my copyItem method?
Newtwork API func:
func downloadFile(id: Int, url: URL, fileName: String) {
var request = URLRequest(url: url)
URLSession.shared.downloadTask(with: url, completionHandler: { location, response, error in
guard let location = location, error == nil else { return }
do {
let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent(fileName)
try fileManager.copyItem(at: location, to: fileURL)
try self.router.configureParameters(bodyParameters: ["uuid": UserDefaultsHelper.uuid], urlParameters: nil, request: &request)
} catch {
print(error)
}
}).resume()
URLSession.shared.dataTask(with: request) { data, response, error in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
}
What happens if you change
do {
let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent(fileName)
try fileManager.copyItem(at: location, to: fileURL)
try self.router.configureParameters(bodyParameters: ["uuid": UserDefaultsHelper.uuid], urlParameters: nil, request: &request)
} catch {
to
do {
let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let audioKitFilesFolder = documentsURL.appendingPathComponent(".AudioKitFilesFolder")
let fileURL = audioKitFilesFolder.appendingPathComponent(fileName)
try fileManager.copyItem(at: location, to: fileURL)
try self.router.configureParameters(bodyParameters: ["uuid": UserDefaultsHelper.uuid], urlParameters: nil, request: &request)
} catch {
and perhaps remove the . from . AudioKitFilesFolder in all places