Swift: Creating a csv file in Documents Directory - ios

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

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.

value of type String has no member stringByAppendingPathComponet [duplicate]

I have a project which I am migrating from Obj-C to Swift 3.0 (and I am quite a noob in Swift).
How do I translate this line?
NSString *folder = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"myfolder"];
I managed to get resource path:
let resoursePath = Bundle.main.resoursePath;
But how do I get path to a subfolder named "myfolder"?
I need to get a path the subfolder, not path to the files inside it.
In Swift-3 make URL, and call appendingPathComponent:
let resourcePath = Bundle.main.resourcePath
let subdir = URL(fileURLWithPath:resourcePath!).appendingPathComponent("sub").path
or simply
let subdir = Bundle.main.resourceURL!.appendingPathComponent("sub").path
(thanks, Martin R!)
See this Q&A on information on stringByAppendingPathComponent method in Swift.
You can do something like this:
let folderURL = resourceURL(to: "myfolder")
func resourceURL(to path: String) -> URL? {
return URL(string: path, relativeTo: Bundle.main.resourceURL)
}
You can use this method to get listing of the bundle subdirectory and get resources only of the specific type:
Bundle.main.paths(forResourcesOfType: type, inDirectory: folder)
Swift 5.0
Swift 5:
Caveat: Assumes that your folder is actually in the bundle; (that you added the folder to the project via 'Add Folder Reference', not by the 'Add Groups' option).
bundle.main.path(forResource: "myfolder", ofType: nil)
Objective-C: (your example)
NSString *folder = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"myfolder"];
Other Swift ways:
As a URL (Apple's preferred method, the :URL? is only included here to clarify that this does not produce a string):
let folderURL:URL? = Bundle.main.url(forResource: "myfolder", withExtension: nil)
Appending as a string:
let folder = Bundle.main.resourcePath?.appending("/myfolder")
// or
let y = Bundle.main.resourcePath?.appending("/").appending("myfolder")
The following literal translation fails, as appendingPathComponent is no longer available in the Swift String type which is slightly different from NSString:
let folder = Bundle.main.resourcePath?.appendingPathComponent("myfolder")

How to get path to a subfolder in main bundle?

I have a project which I am migrating from Obj-C to Swift 3.0 (and I am quite a noob in Swift).
How do I translate this line?
NSString *folder = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"myfolder"];
I managed to get resource path:
let resoursePath = Bundle.main.resoursePath;
But how do I get path to a subfolder named "myfolder"?
I need to get a path the subfolder, not path to the files inside it.
In Swift-3 make URL, and call appendingPathComponent:
let resourcePath = Bundle.main.resourcePath
let subdir = URL(fileURLWithPath:resourcePath!).appendingPathComponent("sub").path
or simply
let subdir = Bundle.main.resourceURL!.appendingPathComponent("sub").path
(thanks, Martin R!)
See this Q&A on information on stringByAppendingPathComponent method in Swift.
You can do something like this:
let folderURL = resourceURL(to: "myfolder")
func resourceURL(to path: String) -> URL? {
return URL(string: path, relativeTo: Bundle.main.resourceURL)
}
You can use this method to get listing of the bundle subdirectory and get resources only of the specific type:
Bundle.main.paths(forResourcesOfType: type, inDirectory: folder)
Swift 5.0
Swift 5:
Caveat: Assumes that your folder is actually in the bundle; (that you added the folder to the project via 'Add Folder Reference', not by the 'Add Groups' option).
bundle.main.path(forResource: "myfolder", ofType: nil)
Objective-C: (your example)
NSString *folder = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"myfolder"];
Other Swift ways:
As a URL (Apple's preferred method, the :URL? is only included here to clarify that this does not produce a string):
let folderURL:URL? = Bundle.main.url(forResource: "myfolder", withExtension: nil)
Appending as a string:
let folder = Bundle.main.resourcePath?.appending("/myfolder")
// or
let y = Bundle.main.resourcePath?.appending("/").appending("myfolder")
The following literal translation fails, as appendingPathComponent is no longer available in the Swift String type which is slightly different from NSString:
let folder = Bundle.main.resourcePath?.appendingPathComponent("myfolder")

filemanager.createFileAtPath works not correctly

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

`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