write plist file swift - ios

i have a plist file, this file populate a form with settings; when I finish writing each textfield or switch, value is saved correctly, refresh my table correctly and my info its correct. But when i change the screen and return, my plist file is empty, i check several options:
...
let path = NSBundle.mainBundle().pathForResource("perfilBitrubian", ofType: "plist")
descripcionCelda.writeToFile(path!, atomically: false)
or
...
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
let documentsDirectory = paths.objectAtIndex(0) as! NSString
let path = documentsDirectory.stringByAppendingPathComponent("perfilBitrubian.plist")
descripcionCelda.writeToFile(path!, atomically: false)
My list has hidden options; when i hide and show my table refresh data correctly, but if i change the process and return the form; my plist file don't have info.
i see this examples and I do not know what to do.
http://rebeloper.com/read-write-plist-file-swift/
and this:
http://www.appcoda.com/expandable-table-view/
Thanks!

Several things:
First, your first code block won't work. The app bundle is read-only. Trying to write a file to the bundle will always fail.
Next, property lists can only contain a very small set of objects, all of which must be NSObjects. Do a search on "property list object" in Xcode for more info.
The function writeToFile that you are calling (on a dictionary or array?) returns a Bool. Check the result to see if it's failing.

These are my methods, you have to pass the plist name (without extension), to write also pass the key and a dictionary or an array.
The plist is automatically copied to documents directory if not already done before:
public static func plistRead(plistName: String) -> [String: AnyObject]? {
let path = documentsPath().stringByAppendingPathComponent(plistName + ".plist")
let fileManager = NSFileManager.defaultManager()
if !(fileManager.fileExistsAtPath(path)) {
if let bundlePath = NSBundle.mainBundle().pathForResource(plistName, ofType: "plist") {
do {
try fileManager.copyItemAtPath(bundlePath, toPath: path)
} catch let error as NSError {
print("Can't move plist from bundle to documents directory: " + error.localizedDescription)
}
} else {
print("No plist found!")
}
}
return NSDictionary(contentsOfFile: path) as? [String: AnyObject]
}
public static func plistWrite(plistName: String, key: String, data: AnyObject?) -> Bool {
let path = documentsPath().stringByAppendingPathComponent(plistName + ".plist")
let fileManager = NSFileManager.defaultManager()
if !(fileManager.fileExistsAtPath(path)) {
if let bundlePath = NSBundle.mainBundle().pathForResource(plistName, ofType: "plist") {
do {
try fileManager.copyItemAtPath(bundlePath, toPath: path)
} catch let error as NSError {
print("Can't move plist from bundle to documents directory: " + error.localizedDescription)
return false
}
} else {
print("No plist found!")
return false
}
}
if let savedStock = NSMutableDictionary(contentsOfFile: path) {
if let data = data { savedStock.setObject(data, forKey: key) }
else { savedStock.removeObjectForKey(key) }
if savedStock.writeToFile(path, atomically: true) { return true }
else {
print("Can't save file at path: \(path)")
return false
}
}
else {
print("Can't create dictionary!")
return false
}
}

Update swift 3
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 switf

class MainTableViewController: UITableViewController {
//MARK:- Properties
//reading data
var tableData = [String]()
//writing data
var dicData: NSMutableDictionary = ["name" : "data"]
//MARK:- View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
/* converting path to document directory since plist in bundle can't be modified */
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let path : NSString = documentsDirectory.appendingPathComponent("TableDataPropertyList.plist") as NSString
/* getting the path and paste the path in finder to see the plist in document directory */
print(" \(path) ")
dicData.write(toFile: path as String, atomically: false)
//path of plist in bundle to read
let mainPath = Bundle.main.path(forResource: "TableDataPropertyList", ofType: "plist")
let dict = NSDictionary(contentsOfFile: (mainPath)! as String)
tableData = dict!.object(forKey: "AppleDevice") as! [String]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath as IndexPath)
cell.textLabel!.text = tableData[indexPath.row]
return cell
}
}

Related

Saved data on JSON file but can not retrieve previous data, only new entries

I am saving datas on on the json file on the first VC , load the data as well and display it when switching tab. When I kill the app or re run the app again, add new datas to the JSON file, only those new datas are on the JSON file, previous datas are gone(deleted without deleting them manually) and can not be load. How do I save the file so that next time I run the program it will just append to the previous data ?
class ViewController: UIViewController {
var game : Game?
var weekLeague : [[Game]]? = []
override func viewDidLoad() {
super.viewDidLoad()
creation()
}
#IBAction func endWLButton(_ sender: UIButton) {
if games != nil {
weekLeague?.append(games!)
}
save()
}
func save(){
guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileUrl = documentDirectoryUrl.appendingPathComponent("ArrayOfArray.json")
print(fileUrl)
let json = try? JSONEncoder().encode(weekLeague)
do {
try json?.write(to: fileUrl)
print(json!)
print(weekLeague)
print("JSON data was written to teh file successfully!")
}catch{
print(error)
}
}
func ShouldSendGame(game: Game) {
self.game = game
games?.append(game)
}
func creation(){
let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!
let jsonFilePath = documentsDirectoryPath.appendingPathComponent("ArrayOfArray.json")
let fileManager = FileManager.default
var isDirectory: ObjCBool = false
// creating a .json file in the Documents folder
if !fileManager.fileExists(atPath: jsonFilePath!.path, isDirectory: &isDirectory) {
let created = fileManager.createFile(atPath: jsonFilePath!.path, contents: nil, attributes: nil)
if created {
print("File created ")
} else {
print("Couldn't create file for some reason")
}
} else {
print("File already exists")
}
}
}
class AllLeagueController : UITableViewController {
var arrayOfArrayGamesCopy : [[Game]] = []
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.global().async {
self.loadData()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
func loadData() {
guard let documentsDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileUrl = documentsDirectoryUrl.appendingPathComponent("ArrayOfArray.json")
do{
let data = try Data(contentsOf:fileUrl)
let decoder = JSONDecoder()
let jsonData = try decoder.decode([[Game]].self, from: data)
arrayOfArrayGamesCopy = jsonData
print("Succes")
print(jsonData.count)
} catch{
print(error)
}
}
}
You need to load data here before save ... Also you need to have separate class for saving and loading data .. dot do that in controller .. its against Single Responsibility Principle ...Your load function should return array of [Game] and save data should return success or failure
#IBAction func endWLButton(_ sender: UIButton) {
//load and set data in games from file ...then append current data and save to file
if games != nil {
weekLeague?.append(games!)
}
save()
}

Open DB with FMDB in Swift

i would like to open an existing Database with FMDB. The DB should be a sqlite Database with the ending .db.
My Code is:
static let shared: DBManager = DBManager()
let databaseFileName = "/mydb.db"
var pathToDatabase: String!
var pathNSURL: NSURL!
var database: FMDatabase!
override init() {
super.init()
let documentsDirectory = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString) as String
let zwerg = documentsDirectory + databaseFileName
let pathNSURL = NSURL(fileURLWithPath: zwerg)
let pathString = documentsDirectory + databaseFileName
pathToDatabase = pathNSURL.path
print(pathToDatabase)
}
func firstQuestion() -> Bool {
if openDatabase(){
let query = "SELECT * FROM movie"
do {
let result = try database.executeQuery(query, values: nil)
print(result)
}
catch {
print(error.localizedDescription)
}
database.close()
return true
}
return false
}
func openDatabase() -> Bool {
if database == nil {
print(FileManager.default.fileExists(atPath: pathToDatabase))
if FileManager.default.fileExists(atPath: pathToDatabase) {
print("Database set new path -> File exists")
database = FMDatabase(path: pathToDatabase)
}
}
if database != nil {
print("Database != nil")
if database.open() {
print("Database is open in != nil")
return true
}
}
return false
}
Im calling the Method firstQuestion() from a VC:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print(DBManager.shared.firstQuestion())
}
My Problem is that the fileExists-Method always returns false. The Filename is correct. This is my first Project with a database in swift, so perhaps i made a stupid mistake... Any suggestions?
Thanks for your help!
let fileManager = FileManager.default
let docURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let databaseURL = docURL.appendingPathComponent("databaseNaeme.db")
and initialize database as:
database = FMDatabase(path: databaseURL.absoluteString)

Swift - Save Dictionary into a plist

I am trying to save/update a dictionary inside a plist file. If a try to save a single value into a plist I have no problem calling the dictionary as a NSMutableDictionary however, if I want to add another dictionary inside it I cannot achieve it.
For example I want to add a dictionary inside the plist with dictionary "dict2" below called "level2" having values as "value1", "value2", "value3" I cannot Achieve this.
Is there a way I can do this?
struct MyProgress {
//1
enum PlistError: ErrorType {
case FileNotWritten
case FileDoesNotExist
}
//2
let name:String
//3
var sourcePath:String? {
guard let path = NSBundle.mainBundle().pathForResource(name, ofType: "plist") else { return .None }
return path
}
//4
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) {
//1
self.name = name
//2
let fileManager = NSFileManager.defaultManager()
//3
guard let source = sourcePath else { return nil }
guard let destination = destPath else { return nil }
guard fileManager.fileExistsAtPath(source) else { return nil }
//4
if !fileManager.fileExistsAtPath(destination) {
//5
do {
try fileManager.copyItemAtPath(source, toPath: destination)
} catch let error as NSError {
print("Unable to copy file. ERROR: \(error.localizedDescription)")
return nil
}
}
}
//1
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
}
}
//2
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
}
}
//3
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
}
}
}
class ProgressFunctions {
static func saveProgressData (plistName: String, dictName:String, dataName:String, dataValue: AnyObject) {
if let myProgressPlist = MyProgress(name: plistName) {
let dict = myProgressPlist.getMutablePlistFile()!
//let savingDict = dict[dictName]![dataName]!
//dict[dictName] = dataValue
print(dict)
do {
try myProgressPlist.addValuesToPlistFile(dict)
} catch {
print(error)
}
}
}
}

plist error in swift 2

I've got this code to save data , it should be working properly but when I run the app when it wants to save the data the it shows me an error this the code is this :(just the part related to plist)
class savedata: NSObject {
var finalselectedlist = [String]!()
override init() {
super.init()
let plistpath = docpath().stringByAppendingPathComponent("items.plist")
let filemanager = NSFileManager.defaultManager()
if filemanager.fileExistsAtPath(plistpath){
let dataarray = NSArray(contentsOfFile: plistpath)
finalselectedlist = dataarray as! [String] // Error is for this line
}else{
finalselectedlist = []
}
}
func docpath() -> NSString {
let allpaths = NSSearchPathForDirectoriesInDomains( .DocumentDirectory , .UserDomainMask, true )
let docpath = allpaths[0] as NSString
return docpath
}
func saveItemstoPlist() {
let plistpath = docpath().stringByAppendingPathComponent("items.plist")
[finalselectedlist as NSArray].writeToFile(plistpath, atomically: true)
}
}
The error is this :
Thread 1: EXC_BAD_INSTRUCTION (code =EXC_I386_INVOP, subcode=0x0)
What's the meaning of this error and how can I solve it ??

NSCoding gamedata not saved swift

At the moment, my data does not get retrieved in another new session and it uses the default values. Do I have to have an existing plist file for this to work? I tried using an existing file with no luck.
I followed the guide here http://battleofbrothers.com/sirryan/saving-game-data-in-spritekit
class GameData : NSObject, NSCoding {
/// Data to save
var variableC : Int = 3
/// Create of shared instance
class var sharedInstance: GameData {
struct Static {
static var instance: GameData?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
var gamedata = GameData()
if let savedData = GameData.loadGame() {
gamedata = savedData
}
Static.instance = gamedata
}
return Static.instance!
}
override init() {
super.init()
}
required init(coder: NSCoder) {
super.init()
}
func encodeWithCoder(coder: NSCoder) {
coder.encodeObject(GameData.sharedInstance, forKey: "GameData")
}
class func loadGame() -> GameData? {
// load existing high scores or set up an empty array
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentsDirectory = paths[0] as! String
let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist")
let fileManager = NSFileManager.defaultManager()
// check if file exists
if !fileManager.fileExistsAtPath(path) {
// create an empty file if it doesn't exist
println("File doesn't exist")
if let bundle = NSBundle.mainBundle().pathForResource("DefaultFile", ofType: "plist") {
fileManager.copyItemAtPath(bundle, toPath: path, error:nil)
}
}
if let rawData = NSData(contentsOfFile: path) {
// do we get serialized data back from the attempted path?
// if so, unarchive it into an AnyObject, and then convert to an array of HighScores, if possible
if let data = NSKeyedUnarchiver.unarchiveObjectWithData(rawData) as? GameData {
println("We loaded the data!")
return data
}
}
println("we returned Nil")
return nil
}
func save() {
// find the save directory our app has permission to use, and save the serialized version of self.scores - the HighScores array.
let saveData = NSKeyedArchiver.archivedDataWithRootObject(GameData.sharedInstance);
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray;
let documentsDirectory = paths.objectAtIndex(0)as! NSString;
let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist");
saveData.writeToFile(path, atomically: true);
}
}
I load the gamedata in the init function with this
var gameData: GameData = GameData.sharedInstance
Updating the data
gameData.variableC = gameData.variableC + 1
gameData.save()
println(gameData.variableC)
You save the data but don't use it in your init. In initWithCoder:
required init(coder: NSCoder) {
super.init()
GameData.shaderInstance = coder.decodeObjectForKey("GameData")
}
Like Jozsef said, I forgot to decode the data. However I need to decode each of the individual variables and then copy it over to the GameData for it to work.
Here's
class GameData : NSObject, NSCoding {
/// Data to save
var variableC : Int! = 3
/// Create of shared instance
class var sharedInstance: GameData {
struct Static {
static var instance: GameData?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
var gamedata = GameData()
if let savedData = GameData.loadGame() {
gamedata.variableC = savedData.variableC
}
Static.instance = gamedata
}
return Static.instance!
}
override init() {
super.init()
}
required init(coder: NSCoder) {
super.init()
self.variableC = coder.decodeObjectForKey("variableC") as? Int
}
func encodeWithCoder(coder: NSCoder) {
coder.encodeObject(GameData.sharedInstance.variableC, forKey: "variableC")
}
class func loadGame() -> GameData? {
// load existing high scores or set up an empty array
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentsDirectory = paths[0] as! String
let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist")
let fileManager = NSFileManager.defaultManager()
// check if file exists
if !fileManager.fileExistsAtPath(path) {
// create an empty file if it doesn't exist
println("File doesn't exist")
if let bundle = NSBundle.mainBundle().pathForResource("DefaultFile", ofType: "plist") {
fileManager.copyItemAtPath(bundle, toPath: path, error:nil)
}
}
if let rawData = NSData(contentsOfFile: path) {
// do we get serialized data back from the attempted path?
// if so, unarchive it into an AnyObject, and then convert to an array of HighScores, if possible
if let data = NSKeyedUnarchiver.unarchiveObjectWithData(rawData) as? GameData {
println("We loaded the data!")
return data
}
}
return nil
}
func save() {
// find the save directory our app has permission to use, and save the serialized version of self.scores - the HighScores array.
let saveData = NSKeyedArchiver.archivedDataWithRootObject(GameData.sharedInstance);
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray;
let documentsDirectory = paths.objectAtIndex(0)as! NSString;
let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist");
saveData.writeToFile(path, atomically: true);
}
}

Resources