0
votes

Below is my code - I have tried to get the document directory path and with standard FileManager singleton tried to create a file, but I am not able to create the file, as the error -

Unable to store data: Error Domain=NSCocoaErrorDomain Code=4 "The file “CrashLog.txt” doesn’t exist."

UserInfo={NSFilePath=file:///Users/ABC/Library/Developer/CoreSimulator/Devices/87317777-63E7-422B-A55F-878E3267AFB8/data/Containers/Data/Application/4B41AA87-E4B9-4EE4-A67F-AC3B018913CC/Documents/CrashLog, NSUnderlyingError=0x600000244ec0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}

Code in development -

let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
if (paths.count > 0) {
    let documentsDirectory = paths[0]
    let logFilePath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("CrashLog.txt").absoluteString
    let _string = "Hello"
    //Create file at given path
    let data = _string.data(using: .utf8)
    //let attributes = FileManager.default.attributesOfItem(atPath: logFilePath)
    let fileExists : Bool = FileManager.default.fileExists(atPath: logFilePath)
    print(fileExists)
    let isFileCreated =  FileManager.default.createFile(atPath: logFilePath, contents: data, attributes: nil)
    print("ifFileCreated", isFileCreated)
}
3
Nvm, I didn't properly read your code. Whatever @Larme says! - Akaino
let logFilePath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("CrashLog.txt") instead, don't use the absoluteString, and so: (atPath: logFilePath.path) (twice) should do the trick. - Larme
I don't remember the exact answer for this, but bear in mind that .absoluteString and .path return different things, I remember having an issue that was caused by using absoluteString instead of path. - Scriptable

3 Answers

2
votes

Here's my take on what you've done. Adopt the URL-based means of working with files. The best way to write data (for this example, at least), is to use Data's ability (not FileManager) to write to a file, again, using a URL. In most cases, you don't need to worry whether the file exists or not; just do it, and handle any error that arises.

    if var url = try? FileManager.default.url(for: .documentDirectory,
                                              in: .userDomainMask,
                                              appropriateFor: nil,
                                              create: false) {
        url = url.appendingPathComponent("CrashLog").appendingPathExtension("txt")
        let _string = "Hello"
        if let data = _string.data(using: .utf8) {
            do {
                try data.write(to: url)
                print("successful")
            } catch {
                print("unsuccessful")
            }
        }
    }
0
votes

The appendingPathComponent method if the receiver (e.g. parameter) does not end with a trailing slash, then it may read file metadata to determine whether the resulting path is a directory. That means it may produce the error you are seeing, so better use the appendingPathComponent(_:isDirectory:) instead.

For example:

let logFilePath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("CrashLog.txt", isDirectory: false).absoluteString
0
votes

The API absoluteString is wrong. The correct API is path

absoluteString returns the entire URL string representation including the scheme file://. On the other hand the path API of FileManager expects file system paths, the string without the scheme.

You are encouraged to use the URL related API anyway and you can write Data directly to disk without explicitly creating a file.

let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let logFileURL = documentsURL.appendingPathComponent("CrashLog.txt")
let string = "Hello"

let data = Data(string.utf8)

let fileExists = FileManager.default.fileExists(atPath: logFileURL.path)
print(fileExists)
do {
    try data.write(to: logFileURL)
    print("data written")
} catch { print(error) }