4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

事の経緯

あるとき、URLの初期化が絡んだUTのケースでテストが失敗するようになった。
しかし、該当する実装はいじっていない。
謎挙動だ。。

でも影響してないからヨシ!としてしばらく目を背けていました。
これにちゃんと向き合って深ぼってみたよという話です。

問題

テストコードを簡単に作ってみました。

import UIKit

func test() {
    let path = "https://www.google.com/"

    guard let url = URL(string: path) else {
        print("nil")
        return
    }

    print(url) // https://www.google.com/
}

test()

これは正常系なので期待値は想定通り。

次に、本来URLに渡したら値を返さないはずの値を入れてみました。

//let path = "https://www.google.com/"
let path1 = "^-^" // URLで許容されていない記号
let path2 = "ほげ" // 日本語
let path3 = "" // 空欄
path1の結果: "%5E-%5E"
path2の結果: "%E3%81%BB%E3%81%92"
path3の結果: "nil"

本来ならnilが表示されて欲しいはずが、URLエンコードされて表示されてしまっていました。
かろうじて空文字は期待通りになりましたが、落ちていたUTのケース的に不正なURLで問題ないかを確認するようなケースだったので、じゃあ空文字にすればテスト通るから良いよね!ということにはなりませんでした。

解決策

ここで一度公式のドキュメントを見直してみると、URLの初期化に関するページで重要な記述を見つけました。

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.

要するに、

  • iOS17以降のバージョンだと従来のURL解析は廃止されてURLComponentsと同じ解析方法を用いるように更新された
  • 無効な文字を自動的にエンコードするようになった

といったことが言及されています。

じゃあiOS17ではどうすれば従来通りの挙動になるのか?というところですが、init(string:encodingInvalidCharacters:)を使うと良いです。
encodingInvalidCharactersfalseにすることで、URL文字列が厳密に有効であるかを確認するようになります。

下記のように修正すると、無事に従来通りの挙動になりました。

func test() {
    let path = "^-^"

    guard let url = URL(string: path, encodingInvalidCharacters: false) else {
        print("nil") // 早期リターンのケースに入り、nilが表示される
        return
    }

    print(url)
}

とはいえ、この初期化のメソッドはiOS17以降から利用できるものになるので、iOS16以前のバージョンも対応している場合は条件を分岐して利用するのが良いでしょう。

if #available(iOS 17.0, *) {
    let url = URL(string: path, encodingInvalidCharacters: false)
    // 何らかの処理
} else {
    let url = URL(string: path)
    // 何らかの処理
}

まとめ

挙動に困惑しましたが、蓋を開けてみれば原因と解決策はシンプルなものでした。
同じように困った人がいれば助けになると幸いです。

(こんな記事書いといてもうすぐiOS18が出そう...次のOSの準備をせねば...!)

参照

  1. init(string:) | Apple Developer Documentation
  2. init(string:encodingInvalidCharacters:) | Apple Developer Documentation
  3. [Swift] iOS17(Swift 5.9) で日本語ドメインのURLが生成できるようになってる!
4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?