Skip to content

Commit eeb3262

Browse files
authored
Merge pull request #2847 from readdle/win-url-relative-path
[Windows] Fix NSURL.relativePath
2 parents 514c9e9 + b81e28f commit eeb3262

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

Sources/Foundation/NSURL.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,19 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying {
570570

571571
// The same as path if baseURL is nil
572572
open var relativePath: String? {
573-
return CFURLCopyFileSystemPath(_cfObject, kCFURLPOSIXPathStyle)?._swiftObject
573+
guard var url = CFURLCopyFileSystemPath(_cfObject, kCFURLPOSIXPathStyle)?._swiftObject else {
574+
return nil
575+
}
576+
#if os(Windows)
577+
// Per RFC 8089:E.2, if we have an absolute Windows/DOS path we can
578+
// begin the URL with a drive letter rather than a `/`
579+
let scalars = Array(url.unicodeScalars)
580+
if isFileURL, url.isAbsolutePath,
581+
scalars.count >= 3, scalars[0] == "/", scalars[2] == ":" {
582+
url.removeFirst()
583+
}
584+
#endif
585+
return url
574586
}
575587

576588
/* Determines if a given URL string's path represents a directory (i.e. the path component in the URL string ends with a '/' character). This does not check the resource the URL refers to.

Tests/Foundation/Tests/TestURL.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,22 @@ class TestURL : XCTestCase {
149149
XCTAssertEqual(url1, url3, "\(url1) was not equal to \(url3)")
150150
}
151151

152+
func test_relativeFilePath() {
153+
let url1 = URL(fileURLWithPath: "/this/is/absolute", relativeTo: nil)
154+
XCTAssertNil(url1.baseURL, "Absolute URLs should have no base URL")
155+
XCTAssertEqual(url1.path, url1.relativePath, "URLs without base path should have equal path and relativePath")
156+
157+
let url2 = URL(fileURLWithPath: "this/is/relative", relativeTo: nil)
158+
XCTAssertNotNil(url2.baseURL, "Relative URLs should have base URL assigned")
159+
XCTAssertNotEqual(url2.path, url2.relativePath, "URLs without base path should have different path and relativePath")
160+
161+
#if os(Windows)
162+
let url3 = URL(fileURLWithPath: "C:\\this\\is\\absolute", relativeTo: nil)
163+
XCTAssertNil(url3.baseURL, "Absolute URLs should have no base URL")
164+
XCTAssertEqual(url3.path, url3.relativePath, "URLs without base path should have equal path and relativePath")
165+
#endif
166+
}
167+
152168
/// Returns a URL from the given url string and base
153169
private func URLWithString(_ urlString : String, baseString : String?) -> URL? {
154170
if let baseString = baseString {
@@ -780,6 +796,7 @@ class TestURL : XCTestCase {
780796
var tests: [(String, (TestURL) -> () throws -> Void)] = [
781797
("test_URLStrings", test_URLStrings),
782798
("test_fileURLWithPath_relativeTo", test_fileURLWithPath_relativeTo ),
799+
("test_relativeFilePath", test_relativeFilePath),
783800
// TODO: these tests fail on linux, more investigation is needed
784801
("test_fileURLWithPath", test_fileURLWithPath),
785802
("test_fileURLWithPath_isDirectory", test_fileURLWithPath_isDirectory),

0 commit comments

Comments
 (0)