filemanager.createFileAtPath works not correctly - ios

I try to create a PLIST-File with the NSFileManager and the method createFileAtPath. In the end, the file was created, it has the size of 0 Bytes, and i even can see the specific PLIST-Icon for that file in the Finder.
But when i want to open it (for example with Xcode) it says:The data couldn't be read because it isn't in the correct format.
I want to write to this file but when its not in the correct format i can't do this.
There is something wrong with the File-creation but i don't know what it is.
I hope you can help me with this.
Here is my code:
pListPath = NSURL(fileURLWithPath: reportsPath.path!).URLByAppendingPathComponent("myReports.plist", isDirectory: false)
let data: NSData = NSData()
var isDir: ObjCBool = false
if fileManager.fileExistsAtPath(pListPath.path!, isDirectory: &isDir)
{
print("File already exits")
}
else
{
let success = fileManager.createFileAtPath(pListPath.path!, contents: data, attributes: nil)
print("Was file created?: \(success)")
print("plistPath: \(pListPath)")
}
reports.path = .../UserDir/.../Documents/Reports
Any help is highly appreciated.

filemanager.createFileAtPath works absolutely correctly,
but you're creating an empty file by writing an empty NSData object to disk.
NSData objects are not implicitly serialized to a property list.
Either use the NSPropertyListSerialization class or – simpler - write an empty dictionary to disk.
let dictionary = NSDictionary()
let success = dictionary.writeToURL(pListPath, atomically: true)
print("Was file created?: \(success)")
print("plistPath: \(pListPath)")
PS: you don't need to create an URL from an URL
pListPath = reportsPath.URLByAppendingPathComponent("myReports.plist", isDirectory: false)
but I recommend to use more descriptive variable names to distinguish String paths and NSURL e.g. pListURL and reportsURL

Related

How to read a multidimensional String Array from a plist in Swift?

I'm using the following code to save a 2D String array to a plist:
func saveFavourites(favouriteStops: [[String]]) {
let directories = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)
if let library = directories.first {
if let libraryUrl = URL(string: library) {
let favouritesUrl = libraryUrl.appendingPathComponent("favourites.plist")
// Write favourites to disk
let favsArray = favouriteStops as NSArray
print(favsArray)
favsArray.write(toFile: favouritesUrl.path, atomically: true)
}
}
}
The above snippet properly creates the .plist file (confirmed by looking at the simulator's filesystem in ~/Library/Developer/CoreServices). However, when I try reading it back to a NSArray with the following snippet, it results in nil:
let directories = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)
if let library = directories.first {
if let libraryUrl = URL(string: library) {
let favouritesUrl = libraryUrl.appendingPathComponent("favourites.plist")
// favsToLoad is nil
let favsToLoad = NSArray(contentsOf: favouritesUrl)
// Do stuff with favsToLoad, if it would load properly
}
}
You're doing two very basic things wrong.
First, never make a URL from a file path by saying URL(string); this is a file on disk, so you must use URL.fileURL.
Second, don't start with a file path at all! Obtain the directory as a URL right from the start.
(Also, though I do not know whether this is the source of the issue, do not read and write directly in the Library directory. Use the Documents directory, the Application Support directory, or similar.)
So, for example, I would write:
let fm = FileManager.default
let docsurl = try fm.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let favouritesurl = docsurl.appendingPathComponent("favourites.plist")
I see your problem. You misspelled "favorites". :)
But seriously...
Plists can only contain a very small set of "property list objects": (dictionaries, arrays, strings, numbers (integer and float), dates, binary data, and Boolean values).
If your array's "object graph" (the objects the array contains and any container objects inside the array recursively contain) contain anything other than the above types, the save will fail.
I don't honestly know what gets saved when it fails. Have you tried opening the plist file in a text editor and looking at it?
My guess is that something other than a string has snuck into your array, it's not one of the above types, and THAT'S why it's failing.

Saving NSData from CoreData Managed Object in a CloudKit Asset

I have a small image stored as NSData in a CoreData Managed Object that I need to save in a CloudKit record as CKAsset.
I was hoping to simply cast the NSData from the managed object like this:
myCKRecord["document"] = myCDRecord.document as! CKAsset
Unfortunately, this cast does not work, so I have made a work-around by writing a file with the NSData, and using the file to initialise the CKAsset.
class myManagedObject: NSManagedObject {
#NSManaged var image: NSData?
#NSManaged var name: String
func createCloudKitAsset(myCDRecord : myManagedObject) -> CKRecord
let myCKRecordID = CKRecordID(recordName: myCDRecord.name)
let myCKRecord = CKRecord(recordType: "myCKType", recordID: myCKRecordID)
let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let localURL = NSURL.fileURLWithPath(dirPaths.first + "/" + recordName)
let fileManager = NSFileManager.defaultManager()
fileManager.createFileAtPath(localURL.path!, contents: myCDRecord.image, attributes: nil)
myCKRecord["image"] = CKAsset(fileURL: localURL)
...
}
This works, but is untidy, and I still have the temporary file to be cleaned up after the CK record is saved.
Hopefully, someone can show me a better way to do this.
Thanks
It is bad practice to store large documents in Core Data. The blob data type should only be used for small images such as thumbnails.
Instead, your Core Data entity should just point to the correct file, either via your own directory scheme, and/or via a URL attribute. You would then just have to create the CKAsset with the fileURL parameter from the Core Data entity. To access the data, you would use the fileURL of the CKAsset, which will be different.

Swift: Creating a csv file in Documents Directory

I am creating a registration form application. The registration form data will be saved in a CSV file. I need to create a csv file in the documents directory and be able to write to it. I'm pretty new to Swift so I'm having a bit of trouble. This is what I've got so far.
var fileManager = NSFileManager.defaultManager()
var fileHandle: NSFileHandle
#IBAction func submitForm(sender: AnyObject) {
mySingleton.filename = mySingleton.filename + ".csv"
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let filePath = documentsPath + mySingleton.filename
if !fileManager.fileExistsAtPath(mySingleton.filename) {
fileManager.createFileAtPath(filePath, contents: nil, attributes: nil)
fileHandle = ...
}
}
Obviously the code I'm having trouble with is the FileHandle which is what allows to to modify the file. In objective-c it would look something like this:
fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:event];
Then obviously if I wanted to write to the file I could do something like this:
[fileHandle seekToEndOfFile];
[fileHandle writeData:[formData dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandle closeFile];
So I'm really only struggling with that one part. Thanks for the help!
In Swift, fileHandleForUpdatingAtPath has become an initializer of NSFileHandle, adopting the Swift naming convention of avoiding repetition and taking the end of the Objective-C method name, making it the parameter name:
let fileHandle = NSFileHandle(forUpdatingAtPath: yourPath)
Make sure to add these two keys in info.plist and set them to "YES"
Supports opening documents in place
Application supports iTunes file sharing

Swift: Saving NSDictionary to a file, because NSUserDefaults is to slow

I'm using the NSUserDefaults for saving more the 10 big NSDictionarys.
Because the performace is slow, I have the idea to saving the Data into one [or 10 files] instead of using the NSUserDefaults. I need the data just by the starting of the app, so the idea is, saving the data to a file and saving into NSUserDefaults only data I need fast.
I found this solution on stack overflow:
Read and write data from text file
let dirs : [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if ((dirs) != nil) {
let dir = dirs![0]; //documents directory
let path = dir.stringByAppendingPathComponent(file);
let text = "some text"
//writing
text.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: nil);
//reading
let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
}
That works for strings. But when I try to use
NameOfMyDict.writeToFile
I get an error, because Dicts don't have the writeToFile function.
How can I save Dicts to a file?
NSDictionary provides a function to save to a file. It is part of Foundation framework:
writeToFile(_ path: String,
atomically flag: Bool) -> Bool
If you use a swift Dictionary<>, you may need to cast as a NSDictionary. All elements of the dictionary should implement NSCoding protocol.
You could serialize it to JSON and save the JSON string to a file. See NSJSONSerialization.

`NSBundle.mainBundle().URLForResource` always returns `nil`

I am new to Swift and am using Xcode 6.
I am attempting to read data from the app's plist file, but it is not working.
The data.plist file is included in Xcode's Supporting Files group.
I am using the code below:
var dataList = NSDictionary(contentsOfURL:NSBundle.mainBundle().URLForResource("data", withExtension:"plist"))
however the NSURL:
NSBundle.mainBundle().URLForResource("data", withExtension:"plist")
always returns nil.
I don't know what is wrong.
Generally you would want to use this code to create your plist. This finds the the path to your plist and then moves it into the documents directory if it isn't already there. If you don't move it, you are not allowed to write to it, hence this chunk of code is vital. To fetch the information from the plist, use the second bit of code. Obviously if you have an array rather than a dictionary, you would have to alter it to deal with that.
var path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
path = path.stringByAppendingPathComponent("data.plist")
let fileManager = NSFileManager.defaultManager()
if !fileManager.fileExistsAtPath(path) {
let sourcePath = NSBundle.mainBundle().pathForResource("data", ofType: "plist")
fileManager.copyItemAtPath(sourcePath, toPath: path, error: nil)
}
.
let dict = NSMutableDictionary(contentsOfFile: path) as NSMutableDictionary

Resources