I have an [URL]
which represent a set of special parent directories. I am given another [URL]
which represents files scattered around the system. I want to know if any of these files are in any of my special directories, or any of their subdirectories. Is there a simple/intended way to do this, without manually parsing/traversing an absolute URL's path?
A slightly improved, Swift 5 version combining nice features of both Umair and rmaddy's existing excellent answers.
Like Umair's answer, this correctly handles the case where a folder in the path is a partial match (that is, it will not say that /foo/bar-baz/bang
has /foo/bar/
as an ancestor).
It also performs the canonicalization (standardizing paths to eliminate things like .
or ../
, and resolving symlinks) like rmaddy's answer.
func pathHasAncestor(maybeChild: URL, maybeAncestor: URL) -> Bool {
let ancestorComponents: [String] = canonicalize(maybeAncestor).pathComponents
let childComponents: [String] = canonicalize(maybeChild).pathComponents
return ancestorComponents.count < childComponents.count
&& !zip(ancestorComponents, childComponents).contains(where: !=)
}
func canonicalize(_ url: URL) -> URL {
url.standardizedFileURL.resolvingSymlinksInPath()
}
Here's a very simple unit test to verify the basic functionality:
import XCTest
class FileUtilsTests: XCTestCase {
func testPathHasAncestor() throws {
let leaf = URL(fileURLWithPath: "/foo/bar/baz")
let parent = leaf.deletingLastPathComponent()
XCTAssertTrue(pathHasAncestor(maybeChild: leaf, maybeAncestor: parent))
XCTAssertFalse(pathHasAncestor(maybeChild: URL(fileURLWithPath: "/foo/bar 1/baz"), maybeAncestor: parent))
XCTAssertFalse(pathHasAncestor(maybeChild: leaf, maybeAncestor: leaf))
}
}