ngstschr
ngstschr

Reputation: 2319

How to use NSURL with relative path?

So, I'm building a command line tool in Swift that takes a filepath as an argument. I check if the filepath exists, else I throw an error. When I use an absolute path, everything works as it should. When I use a relative path, it doesn't.

code:

  let pwdURL = URL(string: "file://" + FileManager.default.currentDirectoryPath)
  print("pwdURL: ", pwdURL ?? "no pwdURL")
  let pathURL = URL(string: path, relativeTo: pwdURL)
  print("pathURL: ", pathURL ?? "no pathURL")

  do {
    let _ = try pathURL?.checkResourceIsReachable()
  }
    catch {
    print(error)
    throw ValidationError("\(path) Is not a valid path.")
  }

command:

From the /Users/myUser/Projects/myProject folder, in the terminal.

  swift run myProject ../../Downloads/myFile.pdf

output:

  pwdURL:  file:///Users/myUser/Projects/myProject
  pathURL:  ../../Downloads/myFile.pdf -- file:///Users/myUser/Projects/myProject
  
  Error Domain=NSCocoaErrorDomain Code=260 "The file “myFile.pdf” couldn’t be opened because there is no such file." UserInfo={NSURL=../../Downloads/myFile.pdf -- file:///Users/myUser/Projects/myProject, NSFilePath=/Users/Downloads/myFile.pdf, NSUnderlyingError=0x7fad39d0c280 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
  Error: ../../Downloads/myFile.pdf Is not a valid path.

I was expecting it to look for /Users/myUser/Downloads/myFile.pdf, because that is a valid path from where I type the command in the terminal. Also, when I look at the output from the pathURL (../../Downloads/myFile.pdf -- file:///Users/myUser/Projects/myProject), it looks like this would resolve to the correct path? But as you can see, there is an error because it is looking for /Users/Downloads/myFile.pdf, which doesn't exist.

Upvotes: 3

Views: 2156

Answers (1)

Rob Napier
Rob Napier

Reputation: 299345

It is required that the relativeTo parameter end in a / for this to work the way you're expecting. From the NSURL docs (emphasis added):

This method allows you to create a URL relative to a base path or URL. For example, if you have the URL for a folder on disk and the name of a file within that folder, you can construct a URL for the file by providing the folder’s URL as the base path (with a trailing slash) and the filename as the string part.

So what you need here is:

let pwdURL = URL(string: "file://" + FileManager.default.currentDirectoryPath + "/")

But I would actually do it this way:

let pwdURL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)

or:

let pwdURL = Process().currentDirectoryURL

I typically find it easier to do this using appendingPathComponent, which is a bit easier to use correctly:

let pathURL = pwdURL.appendingPathComponenent(path)

The difference it that this will leave the ../.. in the path rather than resolving those away, so it does depend on which you want.

Upvotes: 3

Related Questions