Handling a new URL parsing behavior in iOS 17

The new URL parsing is better but can cause problems for code written before.

Ihor Malovanyi
2 min readSep 29, 2023

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 as URLComponents. This unifies the parsing behaviors of the URL and URLComponents 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!

--

--