I am trying to write to a plist in xcode. What I've written works, except the plist file doesn't change. I've tried a few different implementations of this, and reaching the same result.
Here is the code:
func saveGameData() {
let BedroomFloorKey = "BedroomFloor"
let BedroomWallKey = "BedroomWall"
var bedroomFloorID: AnyObject = 101 as AnyObject
var bedroomWallID: AnyObject = 101 as AnyObject
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
let documentsDirectory = paths.object(at: 0) as! NSString
let path = documentsDirectory.appendingPathComponent("room.plist")
print("PATH", path)
let dict: NSMutableDictionary = ["XInitializerItem": "DoNotEverChangeMe"]
//saving values
dict.setObject(bedroomFloorID, forKey: BedroomFloorKey as NSCopying)
dict.setObject(bedroomWallID, forKey: BedroomWallKey as NSCopying)
//...
//writing to GameData.plist
dict.write(toFile: path, atomically: false)
let resultDictionary = NSMutableDictionary(contentsOfFile: path)
print("Saved GameData.plist file is --> \(resultDictionary?.description ?? "")")
}
My plist is in the main directory of my xcode project, same folder as were the ViewController is.
Thanks
You cannot write into the application bundle, for obvious reasons the bundle is read-only.
Your code writes the plist into the Documents directory in the container of the application. If you have a default Property List file in the application bundle copy it on the first launch of the app into the Documents directory.
However the code looks like a ugly literal translation of Objective-C code. This is a native Swift version
func saveGameData() throws {
let bedroomFloorKey = "BedroomFloor"
let bedroomWallKey = "BedroomWall"
let bedroomFloorID = 101
let bedroomWallID = 101
let documentsDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let url = documentsDirectory.appendingPathComponent("room.plist")
print("PATH", url)
var dict : [String:Any] = ["XInitializerItem": "DoNotEverChangeMe"]
//saving values
dict[bedroomFloorKey] = bedroomFloorID
dict[bedroomWallKey] = bedroomWallID
//...
//writing to GameData.plist
let data = try PropertyListSerialization.data(fromPropertyList: dict, format: .xml, options: 0)
try data.write(to: url)
print("Saved GameData.plist file is --> \(dict)")
}
There is no need to reread the data. If no error is thrown the plist has been written successfully.
You create a plist file inside documents
let path = documentsDirectory.appendingPathComponent("room.plist")
which isn't same as the file located in your main bundle ( same level as ViewController.swift ) which won't accept any write as bundle is signed with the app and can't accept any change
Related
I am using Swift 4, Xcode 9, and development target iOS 11.0.
I am trying to append a custom folder (MyFolder) to the path variable.
let outputFilePath = (NSTemporaryDirectory() as NSString).appending("MyFolder").appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!)
But builder is giving error message:
appendingPathComponent' is unavailable: Use appendingPathComponent on URL instead.
I know, I am doing some silly mistake. Can you kindly help me in this?
Use this line
URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("MyFolder").appendingPathComponent(outputFileName).appendingPathExtension("mov")
instead of
(NSTemporaryDirectory() as NSString).appending("MyFolder").appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!)
This will return you a url and use url.Path to get its path in string .
Hope this helps you.
Check below code for reference in document Directory
class func getDocumentsDirectory() -> URL {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let dataPath = documentsDirectory.appendingPathComponent("FolderName")
do {
try FileManager.default.createDirectory(atPath: dataPath.path, withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
print("Error creating directory: \(error.localizedDescription)")
}
return dataPath
}
For Appending Files in Folder You can use this
//name for file to be added
let uuid = UUID().uuidString
// storing a Audio File in Directory
let audioFilename = getDocumentsDirectory().appendingPathComponent("\(uuid).m4a")
To get Names of Files Available in the respected Folder created
//This function returns a Array with file names Available
class func getListOfRecordingsAvailable() -> [String] {
var fileNameArray = [String]()
let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let myFilesPath = documentDirectoryPath.appending("/FolderName")
let files = FileManager.default.enumerator(atPath: myFilesPath)
while let file = files?.nextObject() {
//myfilesPath - Path
//file - fileName
fileNameArray.append(file as! String)
}
print(fileNameArray)
return fileNameArray
}
This plist work correctly on simulator...read and write plist data without problems!Work correctly also deleting data. It don't crash!!
If instead launch the app on device don't read nothing data!
I am using Swift 3 version 8.3.2.
Any idea?
thanks in advance!
read plist
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) as NSArray
let documentsDirectory:AnyObject = paths[0] as AnyObject
let path = documentsDirectory.appending("orari_marcate.plist")
let fileManager = FileManager.default
let fileExists:Bool = fileManager.fileExists(atPath: path)
let lista_marcate : NSMutableDictionary = NSMutableDictionary()
var marcate2 = NSMutableArray()
if fileExists == false {
marcate2 = NSMutableArray()
} else {
marcate2 = NSMutableArray(contentsOfFile: path)!
}
write plist
lista_marcate.setValue(risultato_Ora.text, forKey: "fine_Ora")
lista_marcate.setValue(risultato_Min.text, forKey: "fine_Min")
marcate2.add(lista_marcate)
marcate2.write(toFile: path, atomically: true)
I had written a code when i was working with swift 1.2 which its function is loading and saving the data from plist. it was working fine with swift 1.2 but now that im working on swift 2. the code still loads the value but it doesnt save the value.
i run the application on device and not the simulator.
you can see the codes below:
func loadGameData() {
let path = NSBundle.mainBundle().pathForResource("GameData", ofType: "plist")!
let myDict = NSDictionary(contentsOfFile: path)
if let dict = myDict {
highscore = dict.objectForKey("highscore")!
} else {
print("WARNING: Couldn't create dictionary from GameData.plist! Default values will be used!")
}
}
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"]
dict.setObject(highscore.integerValue, forKey: "highscore")
//writing to GameData.plist
dict.writeToFile(path, atomically: true)
let resultDictionary = NSMutableDictionary(contentsOfFile: path)
print("Saved GameData.plist file is --> \(resultDictionary?.description)")
}
the console message after saving the code is:
Saved GameData.plist file is --> Optional("{\n XInitializerItem = DoNotEverChangeMe;\n highscore = 25;\n}")
does anyone know a different code which works with swift 2 ? this one worked fine with the previous versions.
Tnx for the help
Problem is you are reading from Main bundle while writing into Documents directly.
Reading Path: NSBundle.mainBundle().pathForResource("GameData", ofType: "plist")!
Writing Path: NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
To fix this, change your reader code like:
func loadGameData() {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
let documentsDirectory = paths.objectAtIndex(0) as! NSString
let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist")
let myDict = NSDictionary(contentsOfFile: path)
if let dict = myDict {
highscore = dict.objectForKey("highscore")!
} else {
print("WARNING: Couldn't create dictionary from GameData.plist! Default values will be used!")
}
}
Ok, Managed to put this code together, created a plist file with Xcode which I copied {using iTunes} to the app folder, it doesn't work...
import Foundation
class getBeaconConfiguration {
var myBeaconsDict: NSDictionary?
func getBeacons() {
let documentsPath : AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0]
let sourcePath:NSString = documentsPath.stringByAppendingString("/beacons.plist")
let dict = NSDictionary(contentsOfFile: sourcePath as String)
print("dict",dict)
}
}
The path is good, and the plist is there, but the NSDictionary returned is a null pointer it seems. How do you do this in swift 2.0.
Get Create a URL to the file and then create a dictionary from it.
let filename = "beacons.plist"
guard
let fileURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first?.URLByAppendingPathComponent(filename)
else { fatalError("Unable to get file") }
let dict = NSDictionary(contentsOfURL: fileURL)
A little different in Swift 2.0, String no longer has stringByAppendingPathComponent, and you should be using this method when working with file paths.
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
let sourcePath = documentsPath.stringByAppendingPathComponent("beacons.plist")
let dictionary = NSDictionary(contentsOfFile: sourcePath as String)
print("dict: \(dictionary)")
I need help to read and write data to a remote plist file in my iOS application with Swift.
I can read and save data in local but not with a remote server.
Here, my code to read in local.
Variables
var VintiInizialiID: AnyObject!
var PersiInizialiID: AnyObject!
var CampionatoID: AnyObject!
var coefficientetorneoID: AnyObject!
loadPlistData()
func loadPlistData() {
var VintiInizialiKey = "VintiIniziali"
var PersiInizialiKey = "PersiIniziali"
var TutorialKey = "Tutorial"
var coefficientetorneoKey = "CoefficienteTorneo"
var CampionatoKey = "Campionato"
// getting path to database.plist
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
let documentsDirectory = paths[0] as! String
let path = documentsDirectory.stringByAppendingPathComponent("database.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("database", ofType: "plist") {
let resultDictionary = NSMutableDictionary(contentsOfFile: bundlePath)
println("Bundle database.plist file is --> \(resultDictionary?.description)")
fileManager.copyItemAtPath(bundlePath, toPath: path, error: nil)
println("copy")
} else {
println("database.plist not found. Please, make sure it is part of the bundle.")
}
} else {
println("database.plist already exits at path.")
// use this to delete file from documents directory
//fileManager.removeItemAtPath(path, error: nil)
}
let resultDictionary = NSMutableDictionary(contentsOfFile: path)
println("Loaded database.plist file is --> \(resultDictionary?.description)")
var myDict = NSDictionary(contentsOfFile: path)
if let dict = myDict {
//loading values
VintiInizialiID = dict.objectForKey(VintiInizialiKey)!
PersiInizialiID = dict.objectForKey(PersiInizialiKey)!
CampionatoID = dict.objectForKey(CampionatoKey)!
coefficientetorneoID = dict.objectForKey(coefficientetorneoKey)!
//...
} else {
println("WARNING: Couldn't create dictionary from GameData.plist! Default values will be used!")
}
}
And Finally SavePlistData()
func Saveplistdata() {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
let documentsDirectory = paths.objectAtIndex(0)as! NSString
let path = documentsDirectory.stringByAppendingPathComponent("database.plist")
var dict: NSMutableDictionary = ["XInitializerItem": "DoNotEverChangeMe"]
//saving values
dict.setObject(VintiInizialiID, forKey: "VintiIniziali")
dict.setObject(PersiInizialiID, forKey: "PersiIniziali")
dict.setObject(CampionatoID, forKey: "Campionato")
dict.setObject(coefficientetorneoID, forKey: "CoefficienteTorneo")
//...
//writing to database.plist
dict.writeToFile(path, atomically: false)
let resultDictionary = NSMutableDictionary(contentsOfFile: path)
// println("Saved database.plist file is --> \(resultDictionary?.description)")
}
No, there isn't a native way to read and write to an external .plist using just Swift without downloading the file, making changes and re-uploading it. Alternatively, you'd need to set up your own API on a server in order to carry out the read / write actions for you.
As #Scott H stated in the comments, theres a better way to do this:
If you want to go this route, download the file locally, change it
locally, and then upload to the server. However, there are many
alternatives available to you for remote configuration like CloudKit,
Parse, or similar.
Learn more about 3rd party options:
CloudKit
Parse
NSMutableDictionary(contentsOfFile: bundlePath)
Use contentsOfURL instead.