nikko 4913
nikko 4913

Reputation: 3

Save data to .plist file in Swift 3

I have successfully read data from my plist file into a table view. Now, I just want to know how i can add another "item" with strings "name" and "location" attached.

What I'm looking for, is a way, to send a string to be saved as either "name" or "location" for a new "item". For example, if i click a button, the entered information gets stored in the plist file under a new "item".

Can anybody help me in the right direction? What would you do, if it was you?

I'm using Swift 3 and Xcode 8.2.1

This is my .plist file: enter image description here

This is the code i use for getting "name" and "location" of an "item", so that i can insert it into a table view:

struct SavedTracks {

    let name: String
    let location: String
}

extension SavedTracks {
    enum ErrorType: Error {
        case noPlistFile
        case cannotReadFile
    }

    /// Load all the elements from the plist file
    static func loadFromPlist() throws -> [SavedTracks] {
        // First we need to find the plist
        guard let file = Bundle.main.path(forResource: "SkiTracks", ofType: "plist") else {
            throw ErrorType.noPlistFile
        }

        // Then we read it as an array of dict
        guard let array = NSArray(contentsOfFile: file) as? [[String: AnyObject]] else {
            throw ErrorType.cannotReadFile
        }

        // Initialize the array
        var elements: [SavedTracks] = []

        // For each dictionary
        for dict in array {
            // We implement the element
            let element = SavedTracks.from(dict: dict)
            // And add it to the array
            elements.append(element)
        }

        // Return all elements
        return elements
    }

    /// Create an element corresponding to the given dict
    static func from(dict: [String: AnyObject]) -> SavedTracks {
        let name = dict["name"] as! String
        let location = dict["location"] as! String


        return SavedTracks(name: name,
                           location: location)
    }
}

Upvotes: 0

Views: 8342

Answers (3)

Narendra Jagne
Narendra Jagne

Reputation: 446

simple way to save repeat dynamic dictionary using PropertyListSerialization

let plistDict: [String: Any] = ["Key": "Value"]

//Write Data Into Plist
func writePlist(dictContent: [String: Any]) {

    let url = URL(fileURLWithPath: PlistManager.path)
    do {
        let data = try Data(contentsOf: url)
        var array = try PropertyListSerialization.propertyList(from: data, format: nil) as! [[String:Any]]
        array.append(dictContent)
        let writeData = try PropertyListSerialization.data(fromPropertyList: array, format: .xml, options:0)
        try writeData.write(to: url)
    } catch {
        print(error)
    }
}

Upvotes: 4

CryingHippo
CryingHippo

Reputation: 5086

Here is helper structure for reading & writing plists :

Usage :

//Reading :
let rootArray = PlistFile(named: "PlistFilename")?.array
let rootDictionary = PlistFile(named: "PlistFilename")?.dictionary

//Writing :

if let plistFile = PlistFile(named : "UserData") {
  plistFile.array = yourArray
}

//or :

try? PlistFile(named : "UserData")?.write(yourArray)

Code :

struct PlistFile {

    enum PlistError: Error {
        case failedToWrite
        case fileDoesNotExist
    }

    let name:String

    var sourcePath:String? {
        return Bundle.main.path(forResource: name, ofType: "plist")
    }

    var destPath:String? {
        if let _ = sourcePath {
            let dir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
            return (dir as NSString).appendingPathComponent("\(name).plist")
        } else {
            return nil
        }
    }

    var dictionary : [String:Any]? {
        get{
            return getDictionary()
        }
        set{
            if let newDict = newValue {
                try? write(dictionary: newDict)
            }
        }
    }

    var array : [Any]? {
        get{
            return getArray()
        }
        set{
            if let newArray = newValue {
                try? write(array: newArray)
            }
        }
    }

    private let fileManager = FileManager.default

    init?(named :String) {
        self.name = named

        guard let source = sourcePath, let destination = destPath, fileManager.fileExists(atPath: source)  else {
            return nil
        }

        if !fileManager.fileExists(atPath: destination) {
            do {
                try fileManager.copyItem(atPath: source, toPath: destination)
            } catch let error {
                print("Unable to copy file. ERROR: \(error.localizedDescription)")
                return nil
            }
        }
    }


    private func getDictionary() -> [String:Any]? {
        guard let destPath = self.destPath, fileManager.fileExists(atPath: destPath) else {
            return nil
        }
        return NSDictionary(contentsOfFile: destPath) as? [String:Any]
    }

    private func getArray() -> [Any]? {
        guard let destPath = self.destPath, fileManager.fileExists(atPath: destPath) else {
            return nil
        }
        return NSArray(contentsOfFile: destPath) as? [Any]
    }

    func write(dictionary : [String:Any]) throws{
        guard let destPath = self.destPath, fileManager.fileExists(atPath: destPath) else {
            throw PlistError.fileDoesNotExist
        }

        if !NSDictionary(dictionary: dictionary).write(toFile: destPath, atomically: false) {
            print("Failed to write the file")
            throw PlistError.failedToWrite
        }
    }

    func write(array : [Any] ) throws {
        guard let destPath = self.destPath, fileManager.fileExists(atPath: destPath) else {
            throw PlistError.fileDoesNotExist
        }

        if !NSArray(array: array).write(toFile: destPath, atomically: false) {
            print("Failed to write the file")
            throw PlistError.failedToWrite
        }
    }


}

Upvotes: 5

fzh
fzh

Reputation: 688

you can try this code:

class SavedTracks: NSObject,NSCoding {
var name: String
var location: String

required init(name:String="", location:String="") {
    self.name = name
    self.location = location
}

required init(coder decoder: NSCoder) {
    self.name = decoder.decodeObject(forKey: "Name") as? String ?? ""
    self.location = decoder.decodeObject(forKey: "location") as? String ?? ""
}

func encode(with coder: NSCoder) {
    coder.encode(name, forKey:"Name")
    coder.encode(location, forKey:"location")
}
}
class DataModel: NSObject {

var saveTrack = [SavedTracks]()

override init(){
    super.init()
    print("document file path:\(documentsDirectory())")
    print("Data file path:\(dataFilePath())")
}

//save data
func saveData() {
    let data = NSMutableData()
    let archiver = NSKeyedArchiver(forWritingWith: data)
    archiver.encode(saveTrack, forKey: "userList")
    archiver.finishEncoding()
    data.write(toFile: dataFilePath(), atomically: true)
}

//read data
func loadData() {
    let path = self.dataFilePath()
    let defaultManager = FileManager()
    if defaultManager.fileExists(atPath: path) {
        let url = URL(fileURLWithPath: path)
        let data = try! Data(contentsOf: url)
        let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
        saveTrack = unarchiver.decodeObject(forKey: "userList") as! Array
        unarchiver.finishDecoding()
    }
}

func documentsDirectory()->String {
    let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory,
                                                    .userDomainMask, true)
    let documentsDirectory = paths.first!
    return documentsDirectory
}

func dataFilePath ()->String{
    return self.documentsDirectory().appendingFormat("/userList.plist")
}
}
class ViewController: UIViewController {
var dataModel = DataModel()

override func viewDidLoad() {
    super.viewDidLoad()
    onCreateData()
}

//create data
func onCreateData(){
    dataModel.saveTrack.append(SavedTracks(name: "jack", location: "xxx"))
    dataModel.saveTrack.append(SavedTracks(name: "tom", location: "yyyy"))
    dataModel.saveTrack.append(SavedTracks(name: "rose", location: "zzz"))
}

@IBAction func saveData(_ sender: UIButton) {
    dataModel.saveData()
    print("succeed")
}

@IBAction func printData(_ sender: UIButton) {
    dataModel.loadData()
    print("succeed!", dataModel.saveTrack)
}
}

Upvotes: 2

Related Questions