Couldn't create log directory - ios

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

Related

URLResourceValues not working for renaming a file

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

Userdefault Save and Read URL FilePath [duplicate]

This question already has an answer here:
NSArrayURL , userDefault filePath and URL
(1 answer)
Closed 5 years ago.
I Download pdf file Like This And Saved the Path in Userdefault
var documents = PDFDocument
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let fileName = urlString as NSString;
var arrayUrl = [String]()
arrayUrl.append(filePath)
self.defaults.set(arrayUrl, forKey: Constants.myKeyURL)
The First Time when App Run This documents get Value Well But Next Time When I don't need to Download Again documents it's Null but The ArrayURL ints same Value !!
var arrayUrl = [String]()
self.defaults.stringArray(forKey: Constants.myKeyURL)
arrayUrl = self.defaults.stringArray(forKey: Constants.myKeyURL)!
self.documents = arrayUrl.flatMap { PDFDocument(url: URL(fileURLWithPath: $0) ) }
print(self.documents)
DispatchQueue.main.async {
self.tableView.reloadData()
}
You shouldn't save the file's fullpath, for security reasons your app's container name gets changed, so the document directory would also changed every time your app gets launched/reloaded.
Instead, save only the file's name along its extension (use the property lastPathComponent from the downloaded url), and whenever you want to load these files just append the name to the document directory.

How to add text into a .csv data in Swift?

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 :)

Print all filenames in a folder in iOS application

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)
}

Seems like my Text file isn't storing persistently in Documents Directory

NOTE: Possibly Resolved: Was missing ".path!" to documentsUrl.path!.stringByAppendingPathComponent(path)
I am saving a text file to Applications Documents Directory. Using
var path = ""
let date = NSDate()
let file = "\(date).txt"
let joined = "\n".join(ActionArray)
if let dirs : [String] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String] {
let dir = dirs[0]
path = dir.stringByAppendingPathComponent(file);
println(path)
joined.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: nil);
}
println path
/Users/dustin/Library/Developer/CoreSimulator/Devices/5EA056FC-114D-4C98-9B35-29B46760BE3A/data/Containers/Data/Application/2AB9FD4A-25AB-4385-9CFB-A66AEFA8C1A3/Documents/2015-05-16 08:24:08 +0000.txt
Then store path and some other data to an Entity, which will be used to call back the text file later.
When I do a fetchRequest on the entity and print out the array I get
(
" (entity: Entity; id: 0xd000000000040000 ; data: {\n date = \"2015-05-16 08:18:20 +0000\";\n path = \"/Users/dustin/Library/Developer/CoreSimulator/Devices/5EA056FC-114D-4C98-9B35-29B46760BE3A/data/Containers/Data/Application/4A9C17AB-57C1-47CB-B9B1-E0E5CA494B4E/Documents/2015-05-16 08:24:08\";\n})"
)
the path is now missing the " +0000". When I call on the path from the Entity.path, my text file is found...BUT when I completely close app (home-swipe-^), the text file search...
let fileContent = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)!
var fileContentArr = split(fileContent) {$0 == "\n"}
I get
fatal error: unexpectedly found nil while unwrapping an Optional value
What am I doing wrong to write a persistent text file to draw upon later regardless if App reset?
EDIT: I've taken advice to notice directory path changes between App sessions, So I've instead stored file name to Entity and get Documents Path at read time. I still get error.
I'm doing some debugging and my println's are as follows
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as! NSURL
// now lets get the directory contents (including folders)
if let directoryContents = NSFileManager.defaultManager().contentsOfDirectoryAtPath(documentsUrl.path!, error: nil) {
println(directoryContents)
println(documentsUrl.path!)
}
[2015-05-17 04:25:48 +0000.txt, 2015-05-17 04:31:07 +0000.txt, iPro_Poker_HH_swift.sqlite, iPro_Poker_HH_swift.sqlite-shm, iPro_Poker_HH_swift.sqlite-wal]
/Users/dustindobrilovic/Library/Developer/CoreSimulator/Devices/5EA056FC-114D-4C98-9B35-29B46760BE3A/data/Containers/Data/Application/7CD25462-947C-40AA-97DB-4A845FED1451/Documents
My Entity Fetch array println()
(
" (entity: Hand; id: 0xd000000000080000 ; data: {\n path = \"2015-05-17 04:31:07 +0000.txt\";\n})"
)
I then combine documentsUrl.path! with path from entity to get println(stringDocumentsURL)
file:/Users/dustindobrilovic/Library/Developer/CoreSimulator/Devices/5EA056FC-114D-4C98-9B35-29B46760BE3A/data/Containers/Data/Application/7CD25462-947C-40AA-97DB-4A845FED1451/Documents/2015-05-17 04:31:07 +0000.txt
This is where Im getting a warning which is very next line.
let fileContent = String(contentsOfFile: stringDocumentsURL, encoding: NSUTF8StringEncoding, error: nil)!
fatal error: unexpectedly found nil while unwrapping an Optional value
>
Path changes across launches. Try to persist only the relative part of the path and reconstruct the absolute path before accessing it.

Resources