How to handle symlinks when reading data from a file path in swift
Asked Answered
O

1

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;
}
Overbid answered 1/6, 2016 at 12:19 Comment(9)
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?Asset
Also, is your app sandboxed?Asset
Why do you even check for existence explicitly? Just try to read in the contents and handle file-not-found as appropriate.Gradeigh
@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 noteOverbid
@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.Overbid
@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.Overbid
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.Gwinn
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.Overbid
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.Gwinn
G
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
Gwinn answered 3/6, 2016 at 14:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.