Save Data to .plist File in Swift

I am trying to save data to a plist file in swift, but the data isn't showing up as it was saved when the plist is read. This is the code I was using.

var documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
var path : NSString = documentsDirectory.stringByAppendingPathComponent("data.plist")
var data : NSMutableDictionary = NSMutableDictionary(contentsOfFile: path)
data.setObject(self.object, forKey: "key")
data.writeToFile(path, atomically: true)

Edit: I've heard that the best way to do this is write to the documents directory, so my question would be how should I write to a file in that directory?


Solution 1:

Apparently the file is not in a writable location, so I created it in the documents directory.

var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
var path = paths.stringByAppendingPathComponent("data.plist")
var fileManager = NSFileManager.defaultManager()
if (!(fileManager.fileExistsAtPath(path)))
{
    var bundle : NSString = NSBundle.mainBundle().pathForResource("data", ofType: "plist")
    fileManager.copyItemAtPath(bundle, toPath: path, error:nil)
}
data.setObject(object, forKey: "object")
data.writeToFile(path, atomically: true)

Then, it has to be read from the documents directory.

var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
var path = paths.stringByAppendingPathComponent("data.plist")
let save = NSDictionary(contentsOfFile: path)

Solution 2:

Swift 3:

func loadData() {
    let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
    let documentDirectory = paths[0] as! String
    let path = documentDirectory.appending("myData.plist")
    let fileManager = FileManager.default
    if(!fileManager.fileExists(atPath: path)){
        if let bundlePath = Bundle.main.path(forResource: "myData", ofType: "plist"){
            let result = NSMutableDictionary(contentsOfFile: bundlePath)
            print("Bundle file myData.plist is -> \(result?.description)")
            do{
                try fileManager.copyItem(atPath: bundlePath, toPath: path)
            }catch{
                print("copy failure.")
            }
        }else{
            print("file myData.plist not found.")
        }
    }else{
        print("file myData.plist already exits at path.")
    }

    let resultDictionary = NSMutableDictionary(contentsOfFile: path)
    print("load myData.plist is ->\(resultDictionary?.description)")

    let myDict = NSDictionary(contentsOfFile: path)
    if let dict = myDict{
        myItemValue = dict.object(forKey: myItemKey) as! String?
        txtValue.text = myItemValue
    }else{
        print("load failure.")
    }
}

Read and Write plist file in swift

Solution 3:

Check in Xcode 10 swift 4.1

//TODO: for wtite in .plist file
        let docsBaseURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let customPlistURL = docsBaseURL.appendingPathComponent("custom.plist")
        print(customPlistURL.absoluteString)
        let dic:[String:Any] = ["key":"val"]
        // Swift Dictionary To Data.
        do  {
        let data = try PropertyListSerialization.data(fromPropertyList: dic, format: PropertyListSerialization.PropertyListFormat.binary, options: 0)
            do {
                try data.write(to: customPlistURL, options: .atomic)
                print("Successfully write")
            }catch (let err){
                print(err.localizedDescription)
            }
        }catch (let err){
            print(err.localizedDescription)
        }

Solution 4:

Use writeToFile:options:error: and see what the error says:

var error: NSError?
var bytes = NSKeyedArchiver.archivedDataWithRootObject(data)
if !bytes.writeToFile(path, options: nil, error: &error) {
    if let actualError = error {
        println(actualError)
    }
}

Solution 5:

struct Plist {

enum PlistError: ErrorType {
    case FileNotWritten
    case FileDoesNotExist
}

let name:String

var sourcePath:String? {
    guard let path = NSBundle.mainBundle().pathForResource(name, ofType: "plist") else { return .None }
    return path
}

var destPath:String? {
    guard sourcePath != .None else { return .None }
    let dir = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
    return (dir as NSString).stringByAppendingPathComponent("\(name).plist")
}

init?(name:String) {

    self.name = name

    let fileManager = NSFileManager.defaultManager()

    guard let source = sourcePath else { return nil }
    guard let destination = destPath else { return nil }
    guard fileManager.fileExistsAtPath(source) else { return nil }

    if !fileManager.fileExistsAtPath(destination) {

        do {
            try fileManager.copyItemAtPath(source, toPath: destination)
        } catch let error as NSError {
            print("Unable to copy file. ERROR: \(error.localizedDescription)")
            return nil
        }
    }
}


func getValuesInPlistFile() -> NSDictionary?{
    let fileManager = NSFileManager.defaultManager()
    if fileManager.fileExistsAtPath(destPath!) {
        guard let dict = NSDictionary(contentsOfFile: destPath!) else { return .None }
        return dict
    } else {
        return .None
    }
}

func getMutablePlistFile() -> NSMutableDictionary?{
    let fileManager = NSFileManager.defaultManager()
    if fileManager.fileExistsAtPath(destPath!) {
        guard let dict = NSMutableDictionary(contentsOfFile: destPath!) else { return .None }
        return dict
    } else {
        return .None
    }
}

func addValuesToPlistFile(dictionary:NSDictionary) throws {
    let fileManager = NSFileManager.defaultManager()
    if fileManager.fileExistsAtPath(destPath!) {
        if !dictionary.writeToFile(destPath!, atomically: false) {
            print("File not written successfully")
            throw PlistError.FileNotWritten
        }
    } else {
        throw PlistError.FileDoesNotExist
    }
}
}

Now, implement below in your view controller.

        if let plist = Plist(name: "plist file name") {
        let dict = plist.getMutablePlistFile()!
        dict["key"] = value

        do {
            try plist.addValuesToPlistFile(dict)
        } catch {
            print(error)
        }
            print(plist.getValuesInPlistFile())
        } else {
           print("Unable to get Plist")
        }