Reputation: 2319
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
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