Ruby's Pathname.relative_path_from documentation.
In objc there is already KSFileUtilities' ks_stringRelativeToURL method, that is very close. I'm looking for a pure swift solution that can run on Linux.
I prefer a solution uses file://
URL's, but String
is also fine.
Filesystems can be case sensitive/insensitive. It may be tricky to determine the relative path.
Example of inputs and expected output:
| Long Path | Relative to Path | Return Value |
|--------------------------------|------------------|-------------------|
| /usr/X11/agent/47.gz | /usr/X11 | agent/47.gz |
| /usr/share/man/meltdown.1 | /usr/share/cups | ../man/meltdown.1 |
| file:///var/logs/x/y/z/log.txt | file:///var/logs | x/y/z/log.txt |
Swift already has FileManager.getRelationship(_:of:in:toItemAt:), but it doesn't return a relative path.
There is no such method in the Swift standard library or in the Foundation framework, as far as I know.
Here is a possible implementation as an extension method of URL
:
extension URL {
func relativePath(from base: URL) -> String? {
// Ensure that both URLs represent files:
guard self.isFileURL && base.isFileURL else {
return nil
}
// Remove/replace "." and "..", make paths absolute:
let destComponents = self.standardized.pathComponents
let baseComponents = base.standardized.pathComponents
// Find number of common path components:
var i = 0
while i < destComponents.count && i < baseComponents.count
&& destComponents[i] == baseComponents[i] {
i += 1
}
// Build relative path:
var relComponents = Array(repeating: "..", count: baseComponents.count - i)
relComponents.append(contentsOf: destComponents[i...])
return relComponents.joined(separator: "/")
}
}
My test code:
func test(_ p1: String, _ p2: String) {
let u1 = URL(fileURLWithPath: p1)
let u2 = URL(fileURLWithPath: p2)
print(u1.relativePath(from: u2) ?? "<ERROR>")
}
test("/usr/X11/agent/47.gz", "/usr/X11") // "agent/47.gz"
test("/usr/share/man/meltdown.1", "/usr/share/cups") // "../man/meltdown.1"
test("/var/logs/x/y/z/log.txt", "/var/logs") // "x/y/z/log.txt"
Remarks:
standardized
method of URL
).Addendum: @neoneye wrapped this into a Swift package: SwiftyRelativePath.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With