Handling a new URL parsing behavior in iOS 17
The new URL parsing is better but can cause problems for code written before.
Apple’s URL parsing implemented RFC1738/1808 standard and was changed to RFC3986 starting from iOS 17. This is what is contained in their old-version URL documentation:
For apps linked on or after iOS 17 and aligned OS versions,
URL
parsing has updated from the obsolete RFC 1738/1808 parsing to the same RFC 3986 parsing asURLComponents
. This unifies the parsing behaviors of theURL
andURLComponents
APIs. Now,URL
automatically percent- and IDNA-encodes invalid characters to help create a valid URL.
Actually, it can produce problems with code written before, because now URL(string: "Invalid URL")
is not nil
.
The Difference
// iOS 16
let validURL = URL(string: "https://google.com") // -> https://google.com
let url = URL(string: "Not an URL") // -> nil
// iOS 17
let validURL = URL(string: "https://google.com") // -> https://google.com
let url = URL(string: "Not an URL") // -> Not%an%URL
The new behavior is convenient for new projects because now you can combine your link with, for example, a search string and be calm that it works, even in cases with spaces and Cyrillic:
// iOS 16
let validURL = URL(string: "https://google.com/клята русня") // -> nil
// iOS 17
let validURL = URL(string: "https://google.com/клята русня") // -> https://google.com/%D0%BA%D0%BB%D1%8F%D1%82%D0%B0%20%D1%80%D1%83%D1%81%D0%BD%D1%8F
But if you have a code that checks URL
for nil
, now you can see unpredictable cases that break your logic in iOS 17:
func setupLabel(with text: String) {
if let url = URL(string: text) {
// make label tappable
} else {
// just setup a text
}
}
This code works correctly in iOS 16, but in iOS 17 it sets every string as a URL. It is just one example.
Apple updated the URL initializer for iOS 17 with a new Bool
parameter encodingInvalidCharacters
that has a default value (true
) — it is what makes the behavior unpredictable. You can revert the old behavior and add false
directly:
// iOS 16
let url = URL(string: "Not an URL") // => nil
// iOS 17
let url = URL(string: "Not an URL", encodingInvalidCharacters: false) // => nil
Conclusion
It would help if you remembered the difference between old and new URL initializers. It is not a huge problem, but it’s necessary to check. Overall, the new API is better and implements a more recent standard.
If you like my content and want to support me, please buy me a coffee and subscribe!
Also, I have the Telegram channel Swift in UA, where you can find more exciting things.
Thank you for reading. See you soon!