Save Data to .plist File in Swift
Asked Answered
C

7

23

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?

Cordellcorder answered 2/8, 2014 at 23:24 Comment(2)
Use writeToFile:options:error: and see what the error says.Ostmark
Could you provide the complete code for this?Cordellcorder
C
34

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)
Cordellcorder answered 6/8, 2014 at 23:58 Comment(5)
what is data? (as is data.setObject(...))Florettaflorette
Data is a mutable dictionaryCordellcorder
stringByAppendingPathComponent is unavailable ?Termor
Use appeding in swift 5.0Gamekeeper
in SWift5 use let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent(plistName)Slabber
C
8

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

Cirrocumulus answered 5/12, 2016 at 13:41 Comment(0)
P
8

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)
        }
Plexus answered 26/11, 2018 at 12:48 Comment(0)
O
2

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)
    }
}
Ostmark answered 2/8, 2014 at 23:41 Comment(9)
This gives the error "Extra argument 'error' in call" when I try to buildCordellcorder
What version of Xcode are you using? This compiles and works fine in the latest beta.Ostmark
@Cordellcorder 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.Ostmark
I got the error "unrecognized selector sent to instance" at runtimeCordellcorder
Terminating app due to uncaught exception 'NSInvalidArgumentException'Cordellcorder
@Cordellcorder on what line?Ostmark
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 youOstmark
Great, thanks. Found it, I was using NSInteger instead of NSNumberCordellcorder
FWIW, this pattern is wrong: you should check the return value of writeToFile(_:options:error:) and print out error only if it's YES.Kauffmann
M
1
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")
        }
Microscope answered 18/1, 2017 at 7:4 Comment(0)
P
-1

From your Information Property List

Key

  Privacy - Photo Library Additions Usage Description

Type

  String

Value

  "Your App Name" would like to access the photo gallery to manage your profile picture
Ptomaine answered 22/6, 2020 at 12:37 Comment(0)
R
-4

updated swift code of Rebeloper:

let BedroomFloorKey = "BedroomFloor"
let BedroomWallKey = "BedroomWall"
var bedroomFloorID: AnyObject = 101
var bedroomWallID: AnyObject = 101

 func saveGameData()
    {
        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
        let documentsDirectory = paths.objectAtIndex(0) as! NSString
        let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist")
        let dict: NSMutableDictionary = ["XInitializerItem": "DoNotEverChangeMe"]

        //saving values
        dict.setObject(bedroomFloorID, forKey: BedroomFloorKey)
        dict.setObject(bedroomWallID, forKey: BedroomWallKey)
        //...
        //writing to GameData.plist
        dict.writeToFile(path, atomically: false)
        let resultDictionary = NSMutableDictionary(contentsOfFile: path)
        print("Saved GameData.plist file is --> \(resultDictionary?.description)")

        self.loadGameData()

    }//eom


    func loadGameData() {
        // getting path to GameData.plist

        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
        let documentsDirectory = paths[0] as! NSString
        let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist")

//        let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist")
        let fileManager = NSFileManager.defaultManager()

        //check if file exists
        if(!fileManager.fileExistsAtPath(path))
        {
            // If it doesn't, copy it from the default file in the Bundle

            if let bundlePath = NSBundle.mainBundle().pathForResource("GameData", ofType: "plist")
            {
                let resultDictionary = NSMutableDictionary(contentsOfFile: bundlePath)
                print("Bundle GameData.plist file is --> \(resultDictionary?.description)")

                do
                {
                    try fileManager.copyItemAtPath(bundlePath, toPath: path)
                    print("copy")
                }
                catch _
                {
                    print("error failed loading data")
                }
            }
            else
            {
                print("GameData.plist not found. Please, make sure it is part of the bundle.")
            }
        }
        else
        {
            print("GameData.plist already exits at path.")
            // use this to delete file from documents directory
            //fileManager.removeItemAtPath(path, error: nil)
        }

        let resultDictionary = NSMutableDictionary(contentsOfFile: path)
        print("Loaded GameData.plist file is --> \(resultDictionary?.description)")
        let myDict = NSDictionary(contentsOfFile: path)

        if let dict = myDict {
            //loading values
            bedroomFloorID = dict.objectForKey(BedroomFloorKey)!
            bedroomWallID = dict.objectForKey(BedroomWallKey)!
            //...
        }
        else
        {
            print("WARNING: Couldn't create dictionary from GameData.plist! Default values will be used!")
        }

    }//eom
Raceme answered 21/1, 2016 at 18:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.