Reputation: 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
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
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
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