I have a folder with 4 subfolders in my iOS application with each of these containing about 20 files each. I would like to be able to iterate through each folder and print out the filenames. I am not sure how to go about doing this.
Here is what I have tried:
let docsPath = NSBundle.mainBundle().resourcePath! + "/Samples";
let fileManager = NSFileManager.defaultManager()
var error: NSError?
let docsArray = fileManager.contentsOfDirectoryAtPath(docsPath, error:&error)
println(docsArray)
This prints out nil. I expect it to print out each of the filenames. How do I make this happen?
You have two problems here:
1)
Check your built app to see if "Samples" is really ending up in the built binary. From the error of "The operation couldn’t be completed", I'm thinking you aren't copying "Samples" into the compiled app bundle or at least into the place you're expecting it to be.
2)
The call you're doing will give you the contents of the folder, but not the contents of the subfolders which is what you really want to list.
Use NSDirectoryEnumerator instead to get the contents of that folder and subfolders. Here is a related question that might give you one direction to go.
You can use the NSFileManager's enumerator if you want to get all the files including inside subdirectories.
Simple example:
if let enumerator = fileManager.enumeratorAtURL(docsPath, includingPropertiesForKeys: nil, options: nil, errorHandler: nil) {
while let url = enumerator.nextObject() as? NSURL {
println(url)
}
}
Nevermind, I figured it out:
var docsPath = NSBundle.mainBundle().resourcePath! + "/Snare";
let fileManager = NSFileManager.defaultManager()
var error: NSError?
let docsArray = fileManager.contentsOfDirectoryAtPath(docsPath, error:&error)
//println(error!.localizedDescription)
println(docsArray)
for filename in docsArray!
{
let subfolderPath = docsPath + "/"+(filename as! String)
println(docsPath)
let subarray = fileManager.contentsOfDirectoryAtPath(subfolderPath, error: &error)
println(subarray)
}
Related
I have to rename some files in a directory in the Documents directory. I'm trying with URLResourceValues:
let fileURLs = try! FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil)
fileURLS.forEach { fileURL in
// calculate newFileName
var resourceValues = URLResourceValues()
resourceValues.name = newFilename
var mutableFileURL = fileURL
try! mutableFileURL.setResourceValues(resourceValues)
}
After setting resourceValues.name I see this in the console...
key NSMutableString "NSURLNameKey" 0x00000001e7a8a368
value NSString "newName.jpg" 0x0000000283f61fe0
So that part is working. The try! completes without crashing so there were no errors thrown. But mutableFileURL is unchanged. It's got the old name, not the new name.
I see in the docs that setting read-only properties or setting properties not supported will be ignored and will not throw an error. But in my research I see this approach used commonly for renaming files. And I don't think it's a write access thing because if I use the old way it works fine:
try! FileManager.default.moveItem(at: fileURL, to: newFileURL)
What could I be missing here?
P.S. app targets iOS 14, running on a real device running iOS 16.1
You have not shown how you know things didn't work. But you say:
But mutableFileURL is unchanged
Your code isn't supposed to change mutableFileURL. If you're looking at mutableFileURL to see what happened, that's the problem.
The URL here is just a pointer to the file on disk. That's the whole point of the setResourceValues approach. Your code changes the name of the file on disk (and mutableFileURL, the pointer, is now invalid and should just be thrown away).
If you re-fetch the contents of the documents directory, you'll see that your code worked just fine.
Complete example, showing both what I think you are doing and how to do this correctly:
// --- create the file ---
let fm = FileManager.default
let docsurl = try fm.url(
for: .documentDirectory, in: .userDomainMask,
appropriateFor: nil, create: false
)
let newurl = docsurl.appending(path: "howdy.txt")
try "howdy".write(to: newurl, atomically: true, encoding: .utf8)
let arr = try fm.contentsOfDirectory(
at: docsurl, includingPropertiesForKeys: nil
)
arr.forEach { print($0.lastPathComponent) }
// howdy.txt
// --- rename the file ---
var values = URLResourceValues()
values.name = "hello.txt"
var mutableurl = newurl
try mutableurl.setResourceValues(values)
// --- how did we do? this is _not_ the way:
print(mutableurl)
// file:///.../Documents/howdy.txt
// --- how did we do? this _is_ the way
let arr2 = try fm.contentsOfDirectory(
at: docsurl, includingPropertiesForKeys: nil
)
arr2.forEach { print($0.lastPathComponent) }
// hello.txt
I have encryptions in the form of dictionaries that I want to save to the document directory. I also want to be able to retrieve these dictionaries from the document directory to decrypt within the app. How can I write/read dictionaries to/from the document directory?
Dictionary has its own write method which writes a property list representation of the contents of the dictionary to a given URL. You can do it using below code:
Write
// Get application document directory path array
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true)
let fileName = "users"
if let documentPath = paths.first {
let filePath = NSMutableString(string: documentPath).appendingPathComponent(fileName)
let URL = NSURL.fileURL(withPath: filePath)
let dictionary = NSMutableDictionary(capacity: 0)
dictionary.setValue("valu1", forKey: "key1")
dictionary.setValue("valu2", forKey: "key2")
let success = dictionary.write(to: URL, atomically: true)
print("write: ", success)
}
Read
if let dictionary = NSMutableDictionary(contentsOf: URL){
print(dictionary)
}
Hope, it will work.
Steps include
Get Document URL
Write Date to File
Saving to Disk
Make use of syntax from here:
https://stackoverflow.com/a/26557965/6342609
I want to add some text like "Hi;Test;Pen" into my .csv file.
I have used the CSVImporter to insert the text of the .csv into my Xcode project. Here is the code, I have used for it:
guard let VokabelPath = Bundle.main.path(forResource: ResourceExample, ofType:"csv") else {
debugPrint(ResourceExample + " not found")
return
}
let importer = CSVImporter<[String]>(path: VokabelPath, delimiter: ";")
let importedRecords = importer.importRecords { $0 }
for record in importedRecords {
self.Vokabeln.insert(record, at: self.Vokabelzähler)
self.Vokabelzähler += 1
}
But for some reasons I want to
add something to the file
Change something in the file (e.g. in Line 4)
Delete the text in the file (e.g. filetext = ""
I have tried to use this Tutorial and I have made this code:
let fileName = "Tasks.csv"
let path = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
var csvText = "Test;Example;Apple;Pen\n"
do {
try csvText.write(to: path!, atomically: true, encoding: String.Encoding.utf8)
} catch {
print("Failed to create file")
print("\(error)")
}
guard let VokabelPath = Bundle.main.path(forResource: "Tasks", ofType:"csv") else {
debugPrint("Tasks not found")
return
}
let importer = CSVImporter<[String]>(path: VokabelPath, delimiter: ";")
let importedRecords = importer.importRecords { $0 }
for record in importedRecords {
self.Vokabeln.insert(record, at: self.Vokabelzähler)
self.Vokabelzähler += 1
print(record)
}
but he prints only "Tasks not found". What is my mistake?
I hope you can help me,
Thank you.
EDIT:
The person who made the first answer has written, I can't save files in the bundle.
He has written to me I should use NSSearchPathForDirectoriesInDomainsso I have updated my code and this works:
let DownloadPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let VokabelPath = DownloadPath.appendingPathComponent("Tasks.csv")
let InhaltData = Inhalt.data(using: .utf8)
print(VokabelPath)
FileManager.default.createFile(atPath: VokabelPath, contents: InhaltData, attributes: nil)
let importer = CSVImporter<[String]>(path: VokabelPath, delimiter: ";")
let importedRecords = importer.importRecords { $0 }
for record in importedRecords {
self.Vokabeln.insert(record, at: self.Vokabelzähler)
self.Vokabelzähler += 1
}
But I have one further question:
I hav some datas in my Xcode project and I can get them with Bundle.main. But I want to use only one function, so is it possible, to get them with NSSearchPathForDirectoriesInDomainstoo?
In order to change a file, you would need to import the data in, make the necessary changes to the data and then write the data back to the same file, overwriting the previous content. That is how you would make changes generally - you don't make changes directly to the file.
Does that make sense?
If it does, then you could easily modify the tutorial you pointed to (the one for creating and exporting CSV) and write back the data that you read from the original file after you make the modifications.
As for your question about not wanting to use a library, you can always write your own CSV parser. It is not very difficult since all you have to do is read the text from the file a line at a time and then separate out the values by comma. A simple parser is straightforward but allowing for more complicated cases could take a bit more effort. It would all depend on your use-cases and the type of data you'd be handling. The easiest way still would be to use your importing library and then output via custom code.
Regarding your additional questions, if the following code fails:
guard let VokabelPath = Bundle.main.path(forResource: "Tasks", ofType:"csv") else {
debugPrint("Tasks not found")
return
}
Then there is either an issue with the file name (or extension) or you have not included the file as part of your Resources. The above code simply gets the path for a given file in your Resources.
The above code is different from what I was talking about with regards to NSSearchPathForDirectoriesInDomains - that was for writing a file from your app. What I meant was that you could not write a file back to your Resources - instead, you would have to write the modified file to your Documents folder. Hope that clarifies things :)
I have a data in text format and that text format is located in my app local file. I did my research but i couldn't find anything about it. How to read that text file in swift. Should i use Sqlite or normal text file format with NSBundle. I hope someone help me to start something with. Thank you.
The following will place the contents of the file into a string:
let filePath = NSBundle.mainBundle().pathForResource("file", ofType: "txt");
let fileData = String(filePath!, encoding:NSUTF8StringEncoding, error:nil)!
var filePath = NSBundle.mainBundle().pathForResource("world_country_state", ofType:"json")
var nsMutData = NSData(contentsOfFile:filePath!)
var sJson = NSJSONSerialization.JSONObjectWithData(nsMutData!, options: .MutableContainers, error: nil) as! NSMutableDictionary
let arraycOuntry: NSArray = sJson.valueForKey("world")?.allKeys as! NSArray
let states: NSArray = sJson.valueForKey("world")?.valueForKey("USA") as! NSArray
pickerDataSoruce = states.mutableCopy() as! NSMutableArray
In this Code world_country_state.json is My File just You replace your file name.
i think this Code is very helpful for you.
When I test my application on iPad it run perfectly i.e. call the database , create folder and perform other task but in console it show me
2015-05-12 11:25:32.478 MyApp[291:19680] [PLLogging] Couldn't create log directory: The operation couldn’t be completed. (Cocoa error 513.).
When I search, found this answer NSFileManager creating folder (Cocoa error 513.)
But not abel to remove this.
Also when I shutdown and restart the iPad this line is not show
Question:
Why i get this statement in console?
The above statement can crash my app in future?
How to remove Cocoa error 513 ?
Here is my code to calling the database
let fileManager = NSFileManager()
var Sourcepath = NSBundle.mainBundle().resourcePath?.stringByAppendingPathComponent("DataBase.db");
let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as! String
let databaseStr = "DataBase.db"
let dbPath = docsPath.stringByAppendingPathComponent(databaseStr)
println(dbPath)
if(fileManager .fileExistsAtPath(dbPath) == false) {
var error:NSError?
fileManager.copyItemAtPath(Sourcepath!, toPath: dbPath, error: &error)
println(error)
}
and here is a functions which create a logs folder
func CreateLog(Log:String)
{
autoreleasepool{
var formatter:NSDateFormatter! = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd";
var DateString = formatter.stringFromDate(NSDate()).stringByAppendingString("_tempLogs")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss";
var FileManager:NSFileManager! = NSFileManager.defaultManager()
var LogFolder = NSBundle.mainBundle().resourcePath?.stringByAppendingPathComponent("Logs")
let searchPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as! String
let LogStr = "Logs"
let LogFolderPath = searchPath.stringByAppendingPathComponent(LogStr)
if(FileManager.fileExistsAtPath(LogFolderPath) == false)
{
var error:NSError?
FileManager.createDirectoryAtPath(LogFolderPath, withIntermediateDirectories: true, attributes: nil, error: &error)
}
var LogPath = LogFolderPath.stringByAppendingPathComponent(DateString).stringByAppendingPathExtension("txt")
var WriteString = (formatter.stringFromDate(NSDate()).stringByAppendingString(" ").stringByAppendingString(Log).stringByAppendingString("\n"));
var Data = WriteString.dataUsingEncoding(NSUTF8StringEncoding)
if(!FileManager.fileExistsAtPath(LogPath!))
{
FileManager.createFileAtPath(LogPath!, contents: Data, attributes: nil)
}
else
{
var output = NSFileHandle(forWritingAtPath: LogPath!);
output?.seekToEndOfFile();
output?.writeData(Data!);
output?.closeFile()
output = nil
}
formatter = nil
FileManager = nil
}
}
The 513 is a permission issue (NSFileWriteNoPermissionError).
Try to println the LogFolderPath and double check if it is correct (or post it here the result).
D.
The problem is that you are trying to create a file in your app's bundle. The bundle is read-only in iOS (and you should treat it as read-only on Mac OS as well, even though it is possible to write to your app bundle in Mac OS.)
Your code will work on the simulator because changing the app bundle is allowed on Mac OS, and the simulator runs under Mac OS.
Change your code to use your documents directory, temp directory, or some other directory in your bundle and you should be fine.
I've been thinking about this scenario, and I can't come up with any better suggestion than: Race Condition?
Could it be that two treads are trying to log at the same time? The first one creates the folder directly after the second tread is passing the .fileExistsAtPath check?
T1: Folder? -> No
T2: Folder? -> No
T1: Create
T2: WTF? There is a folder here already?
If this is the case, it could solve the problem by moving the create directory check and logic to a earlier stage, like on App start or similar.
Not sure this is the issue or that this is the definite answer, so just let's call it a qualified guess... :)
The format you using for getting date string is "yyyy-MM-dd HH:mm:ss" which introduces a space in the result string. Remove the space in the formatter so that it looks like this "yyyy-MM-ddHH:mm:ss" and try the above code or you could replace the space in the resulting path and write data to the file