Save Data to .plist File in Swift

52,319

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

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 3

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 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")
        }
Share:
52,319

Related videos on Youtube

trumpeter201
Author by

trumpeter201

Updated on June 23, 2020

Comments

  • trumpeter201
    trumpeter201 almost 4 years

    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?

    • drewag
      drewag almost 10 years
      Use writeToFile:options:error: and see what the error says.
    • trumpeter201
      trumpeter201 almost 10 years
      Could you provide the complete code for this?
  • trumpeter201
    trumpeter201 almost 10 years
    This gives the error "Extra argument 'error' in call" when I try to build
  • drewag
    drewag almost 10 years
    What version of Xcode are you using? This compiles and works fine in the latest beta.
  • drewag
    drewag almost 10 years
    @trumpeter201 Oh I didn't notice that data was not an instance of NSData. NSMutableDictionary does not have a writeToFile that returns an error. Instead, you can convert the dictionary to NSData first. I updated my answer.
  • trumpeter201
    trumpeter201 almost 10 years
    I got the error "unrecognized selector sent to instance" at runtime
  • trumpeter201
    trumpeter201 almost 10 years
    Terminating app due to uncaught exception 'NSInvalidArgumentException'
  • drewag
    drewag almost 10 years
    @trumpeter201 on what line?
  • drewag
    drewag almost 10 years
    So you have a type stored in your dictionary that does not implement the NSCoding protocol and therefore it can't be written to a file for you
  • trumpeter201
    trumpeter201 almost 10 years
    Great, thanks. Found it, I was using NSInteger instead of NSNumber
  • csiu
    csiu almost 10 years
    FWIW, this pattern is wrong: you should check the return value of writeToFile(_:options:error:) and print out error only if it's YES.
  • quemeful
    quemeful over 9 years
    what is data? (as is data.setObject(...))
  • trumpeter201
    trumpeter201 over 9 years
    Data is a mutable dictionary
  • swiftBoy
    swiftBoy about 8 years
  • Narendra Jagne
    Narendra Jagne about 4 years
    Use appeding in swift 5.0
  • Anees
    Anees about 4 years
    in SWift5 use let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent(plistName)