Thanks!!!!
We are looking into it and believe me, we really really would want to make it Sendable
Thanks!!!!
We are looking into it and believe me, we really really would want to make it Sendable
Correct. One might imagine Swift might have a URL
literal type in the future where the URL
string is checked by the compiler at compile time. Unfortunately such change is way beyond this proposal though.
Using File System within resourceValues
is totally understandable (the caching issues mentioned above aside). However I do not understand the logic behind the FS check during URL construction: there could be no file/directory at the time of the check and there could be a file/directory in there soon thereafter (doing URL actual usage). Or vice versa, at the time of the check there could be a file/directory but soon thereafter (at the time of URL actual usage) the situation can change and there can be a directory/file/absence where file/directory previously was. The FS check during URL initialisation looks misplaced and fragile.
I'm genuinely curious: what are some use cases for this? Do people sort URL
s very often?
If we were to implement Comparable
, what would be the best way to compare two URL
s? Lexicographically comparing the underlying URL strings would be the most obvious approach, but I'm worried about some edge cases that might not make sense. For example:
scheme
be part of the comparison? If so, file:///www.b.com < https:///www.a.com
might be unexpected.https://example.com/item?color=blue&name=spaceship < https://example.com/item?name=spaceship&color=blue
make sense?Apparently not
But one use case is to store URLs in a sorted data structure. For those situations, it doesn't really matter what you sort by - as long as two URLs consistently compare as <
or not, so you can insert them in a consistent place and look them up using binary search.
In some sense, you can think of it as something like a hash function, except that it requires 2 values as input rather than a single value, but returns a consistent result. You can use that result to apply useful algorithms (such as binary search), but the actual terms "greater than" or "less than" are not particularly meaningful.
As for whether it makes sense? You could make the same argument for String
- can you even compare strings of characters as being "greater than" or "less than" other strings of characters? Does it make sense to say that "dog" > "deer" > "city" > "andromeda galaxy"
?
In Swift, we have a rule (not technically enforced, but very much encouraged) that you shouldn't conform types you don't own to protocols you don't own. Since Foundation owns URL and the standard library owns Comparable, only Foundation can safely provide that conformance.
And yes, I can prove that it had already been fixed some time.
One comment about the StaticString
initializer, which I think is a great addition.
It seems the most ergonomic way to specify a file URL would be use the absolute file path, e.g. "/Library/Fonts"
as opposed to "file:///Library/Fonts"
.
This is what developers are familiar with from the current fileURL methods, and the recent FilePath
type is initialized in this way.
I realize that requiring the URL scheme makes the caller's intent explicit and gives a simple rule for use and assertion ("URL scheme required") with no exceptions.
But I suspect in use, folks would prefer that an absolute path string be treated as initializing a file URL, without the additional ceremony of the file:// scheme. (Personally, I find the triple '/' a bit error-prone to type.)
I also realize there is a complex web of initializers that might make that approach ambiguous or otherwise not possible.
But if it is possible, I think allowing this would make the API more ergonomic and not introduce a big issue in terms of programmer error / inability to assert correctness.
Great point! Thanks!
Thanks for the suggestion. Personally I'm not 100% sure whether we should require a scheme for each string either. Initially, I was envisioning the opposite: we treat strings with the file:///
scheme or no scheme (such as "/System/Library"
) as file paths and all other strings as web addresses. We eventually decided to opt-in for a simpler rule: every URL must have a scheme.
However, adding another parameter is essentially splitting this requirement into two places. This, IMO, might create some confusion. For example:
init("https://www.example.com", .file)
?.web
, we might create unexpected results such as URL("/System/Frameworks")
being a web address.I must admit there isn't a logical answer to why I chose Collection
over Sequence
, but I agree with you Sequence
would've been a better choice here. I'll update the proposal to reflect this.
As far as String
vs StringProtocol
goes, I chose String
because the existing API explicitly asks for String
and I didn't think we need to change it.
Yes absolutely.
That's an interesting idea! Thanks for the suggestion.
As mentioned earlier, Foundation.URL
isn’t actually very good for file paths. You should really be moving to Swift System’s FilePath
s wherever practical. In the meantime, URLs do need a scheme, so if you want to use it you’re going to need to provide one.
You’re still going to want to move to FilePath
someday, though. It may as well be now.
Thanks for bringing this up. We are aware that the NSURL
parser is quite old and doesn't support many of the modern URLs (for example, you can't construct fine URLs like https://i❤.ws), and there's a difference between URL
and URLComponents
parsing. Updating the parser is something we are investing in and evaluating for the future. Unfortunately, it won't make it in this proposal.
Haha, this was in my original proposal. We decide to not go down this route because it's potentially ambiguous. Consider this scenario:
let url = URL("file:///System/Frameworks/Foundation")
let foundation = url + ".framework"
What should foundation
be? file:///System/Frameworks/Foundation.framework
or file:///System/Frameworks/Foundation/.framework
?
I agree. It would be nice if we can have some compile time type safety for different URL
types. This would go into the same "big picture" bucket alongside the parser update.
Yes. This system was designed primarily for the use case where a URL
is passed between several different layers of code to minimize the possible repeated disk access. We decide to use the RunLoop
as the "trigger" for cache cleaning to minimize the chance of possible stale data.
I don't think URL
is LosslessStringConvertible
because there are many Strings that can't be represented by an URL
. For example, spaces in a web address URL will always be invalid: URL(https://www.e ample.com)
will fail.
I agree. I'm working on updating the proposal for the next revision.
This is a misunderstanding of the semantic requirements of LosslessStringConvertible
: Bool
conforms to that protocol and there are exactly two strings that convert to Bool
("true"
and "false"
).
Conforming to this protocol requires lossless roundtripping from URL
to String
back to URL
. There may well be certain "value-preserving" details of URL
that cannot be encoded in its String
representation which preclude conformance, but what you describe is not at all a requirement.
If the description returns absoluteString
, then it wouldn't preserve the base/relative parts, but this is also how JSONEncoder
and JSONDecoder
override the Codable
representation of URLs.
LosslessStringConvertible
conformance would mean that you can use String.init(_:)
— rather than String.init(describing:)
or interpolation. This is convenient because String(myURL)
and URL(myString)
with unlabelled arguments are the natural way to spell those inverse operations, and they could also be used by generic code (for any conforming type).
I don't think this is a showstopper. For example:
let newUrl = url + "System" + "Frameworks" + "Foundation" + ".framework"
let newUrl = url + "System/Frameworks/Foundation/.framework" // also ok
let newUrl = url + "picture" + FileExtension("png")
let newUrl = url + "picture.png" // also ok
where URL has both static func + (URL, String)
and func + (URL, FileExtension)
and FileExtension
is a simple struct holding a string.
Or alternatively:
let newUrl = url / "System" / "Frameworks" / "Foundation" / ".framework"
let newUrl = url / "System/Frameworks/Foundation/.framework" // also ok
let newUrl = url / "picture" | "png"
let newUrl = url / "picture.png" // also ok
Pretty cool. I like "abusing" the /
operator for concatenating paths.