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 ??
Related
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)
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
}
}
This is part my code. But myDict is nil. The filename is right.I already checked for many times.
var myDict: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("CatData", ofType: "plist") {
myDict = NSDictionary(contentsOfFile: path)
}
if let dict = myDict {
print("print something")
}
your plist is array of dictionary so try
var myArray: NSArray?
if let path = NSBundle.mainBundle().pathForResource("Categories", ofType: "plist") {
myArray = NSArray(contentsOfFile: path)
}
if let array = myArray {
for item: AnyObject in array {
if let item = item as? NSDictionary {
if let categoryTitle = item["CategoryTitle"] as? NSString {
print("categoryTitle = ", categoryTitle)
}
if let imageNames = item["ImageNames"] as? NSArray {
print("imageNames = ", imageNames)
}
}
}
}
Providing your file name is correct, and your plist has been added to the bundle:
You need to make sure your file is the type you're expecting.
For example. The root of your plist is an Array, and you're looking to get a Dictionary out of it.
So lets rewrite your code:
var myArray: Array? {
if let path = NSBundle.mainBundle().pathForResource("CatData", ofType: "plist") {
myArray = NSArray(contentsOfFile: path) as! [[String:AnyObject]]
}
if let array = myArray {
print("print something")
}
}
In my mainviewcontroller's viewDidLoad() method I am firing few processes.
let api = JsonData() // create instance of JsonData class
api.loadJson(nil) // this method receives json and writes it in the file.
//Function to find file location
func getFileURL(fileName: String) -> NSURL {
let manager = NSFileManager.defaultManager()
let dirURL = manager.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false, error: nil)
return dirURL!.URLByAppendingPathComponent(fileName)
}
//file with this name was created in loadJson method
let filePath = getFileURL("JSONFromServer.txt").path!
//trying to read this file
let newDictionary = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as! [Dictionary <String,AnyObject> ]
I was expecting that file will be created in api.loadJson and straight after it I will be able to read it. But for some reason when I use debugger I see that there is no file yet. And only when program goes out from viewdidload method I can see that file was created.
I wonder if program flow is specific?
Here is my JsonData class:
import Foundation
class JsonData {
var bankDict = [Dictionary <String,AnyObject> ]()
var arrayOfBanks: [[String:AnyObject]] = []
func loadJson(completion: ((AnyObject) -> Void)!) {
var urlString = "http://almaz.com/getjson.php"
let session = NSURLSession.sharedSession()
let sourceUrl = NSURL(string: urlString)
var task = session.dataTaskWithURL(sourceUrl!){
(data, response, error) -> Void in
if error != nil {
println(error.localizedDescription)
} else {
var error: NSError?
var jsonData = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &error) as! NSArray
//println(jsonData)
//convert from JSON into array of dictionaries
var rates = [ExchangeRates]() //instance of class Rate
var bnkDct = ["bank": "", "currency": "","buyrate": "", "sellrate": ""] //template
var indx : Int = 0 //index for iteration
for rate in jsonData{
let rate = ExchangeRates(rate as! NSDictionary)
rates.append(rate)
bnkDct["bank"] = rates[indx].bank
bnkDct["buyrate"] = rates[indx].buyRate
bnkDct["sellrate"] = rates[indx].sellRate
bnkDct["currency"] = rates[indx].currency
self.bankDict.append(bnkDct)
indx += 1
}
//println(self.bankDict)
//Store data in file
//File path and name
if let dirs : [String] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String] {
let dir = dirs[0] //documents directory
let filePath = dir.stringByAppendingPathComponent("JSONFromServer.txt");
NSKeyedArchiver.archiveRootObject(self.bankDict, toFile: filePath)
// println(filePath)
}
}
}
task.resume()
}
}
Your loadJson function takes a completion handler as a parameter but your loadJson function doesn't call it when it is done. Fix that first.
Then in your viewDidLoad function, pass in a completion handler when you call loadJson. The completion code you provide is where you should read and process the file saved by loadJson.
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);
}
}