6

I have a file path as a string. I want to:

  1. Test if there's a file there
  2. Read the contents of the file as a string

the problem I'm having is that sometimes that file path involves a symbolic link (symlink). Maybe to the file itself. Maybe to one of the directories above the file.

[EDIT] closing this because the following code (that I started with), actually works just fine, there were just multiple levels of user error involved. Thanks for the input folks.

func getUserResource(relativeFilePath: String) -> String? {
    let fileManager = NSFileManager.defaultManager()

    let userFilePath = NSHomeDirectory() + relativeFilePath

    if(fileManager.fileExistsAtPath(userFilePath))
    {
        do {
            return try String(contentsOfFile: userFilePath, encoding: NSUTF8StringEncoding);
        } catch {
            return nil;
        }
    }
    return nil;
}
masukomi
  • 10,313
  • 10
  • 40
  • 49
  • I've just used your code to read the contents of a text file via a symlink (created with `ln -s`) and it worked. Are you sure this is your issue? – Eric Aya Jun 03 '16 at 14:04
  • Also, is your app sandboxed? – Eric Aya Jun 03 '16 at 14:04
  • Why do you even check for existence explicitly? Just try to read in the contents and handle file-not-found as appropriate. – Tali Jun 03 '16 at 14:51
  • @EricD it works with a symlink that points to a FILE but not when there is a symlinked directory in the path above the file - I added a clarifying note – masukomi Jun 04 '16 at 12:01
  • @EricD the app is not sandboxed - I test for presence because exception handling as flow control is a bad practice, and exceptions are typically expensive to throw, and because there's a good chance the file won't be there. – masukomi Jun 04 '16 at 12:03
  • 1
    @EricD I was mistaken about the code not working. amongst other issues i'd managed to create a weird symlink structure that just confused the filesystem so _nothing_ was reading it. Thank you for your help. – masukomi Jun 04 '16 at 12:39
  • Did my answer help at all? You can award the bounty and accept the answer, and then you can close the question with the reason "this question was caused by a problem that can no longer be reproduced" if you want. – JAL Jun 06 '16 at 13:06
  • not... really. it just tells how to test if a file is present. My understanding is that you'll get 50% of the bounty anyway when it expires. I just don't want to mark it as an accepted answer since it really isn't. Not sure what happens if i close it before it expires. – masukomi Jun 06 '16 at 21:08
  • I'm not sure what you're asking then. The path will always follow the symlink, and my code will tell you if the symlink is to a directory or to a file, and you can open the file/directory as necessary. Maybe I misunderstood your question? Also happy to just wait for the bounty to expire if this is an issue that can no longer be reproduced. – JAL Jun 07 '16 at 00:46

1 Answers1

3

If you're not sure if the symlink leads to a file or directory, you should be using fileExistsAtPath(path:, isDirectory:). fileExistsAtPath will always return true for a symlink, because technically there is a file at that path. By passing a boolean pointer to isDirectory, you can follow the symlink to a file or to a directory:

Assume symlinkToSomeFile is a symbolic link to a file and symlinkToSomeDir is a symbolic link to a directory.

let symlinkFilePath = NSHomeDirectory() + "/temp/symlinkToSomeFile"
let symlinkDirPath = NSHomeDirectory() + "/temp/symlinkToSomeDir"

var fileCheck: ObjCBool = false
var dirCheck: ObjCBool = false

print(fileManager.fileExistsAtPath(symlinkFilePath, isDirectory: &fileCheck)) // true
print(fileCheck) // false
print(fileManager.fileExistsAtPath(symlinkDirPath, isDirectory: &dirCheck)) // true
print(dirCheck) // true
JAL
  • 41,701
  • 23
  • 172
  • 300