Press "Enter" to skip to content

Month: December 2017

NSFileHandle permission errors

In a macOS programming project I have a command that allows the user to open a file containing some configuration options. This file is a property list, so the contents were being read using NSDictionary(contentsOf:); this method simply returns nil if an error occurs. I wanted to provide more details about the failure than simply “The file could not be read”, so I looked for a Foundation method that would throw an NSError. I settled on FileHandle(forReadingFrom:) and inserted the following code:

    do {
        _ = try FileHandle(forReadingFrom: url)
    }
    catch let error as NSError {
        let alert = NSAlert()
        alert.messageText = "An error occurred while reading the options file.".localized()
        alert.informativeText = error.localizedDescription
        alert.beginSheetModal(for: mainWindow) { (response) in
            if response == NSAlertSecondButtonReturn {
                OperationQueue.main.addOperation { self.resetOptions(nil) }
            }
        }
        return
    }

Then I set up a few test cases, including a file that was owned by another user (with no read permission). To my surprise, the error description implied that I was unable to write the file:

[application icon redacted]

…and while that’s also true, that’s certainly not the error I expected to get.

I looked at the error object in the debugger, and sure enough, the error code was 513 (NSFileWriteNoPermissionError) and not 257 (NSFileReadNoPermissionError). Weird.

Well, I know I’m trying to read the file, and I don’t want to confuse the user, so I changed the catch clause to handle this case:

    catch var error as NSError {
        if error.code == NSFileWriteNoPermissionError {
            error = NSError(domain: NSCocoaErrorDomain, code: NSFileReadNoPermissionError, userInfo: error.userInfo)
        }
        let alert = NSAlert()
        //...
        return
    }

…and that seemed to do nicely. But I can’t find any documentation on whether this is expected behavior or what I might be doing wrong.

Comments closed