iOS 16でゼロ幅スペースが原因でURL変換に失敗する
最近、iOSアプリの開発中にJSONデコードが失敗する問題が発生した。原因はURLにゼロ幅スペース(U+200B
)が含まれていたためだ。iOS 17以降のデバイスではこの問題が発生しておらず、発見が遅れてしまった。本記事では、この問題の詳細とその解決策について説明する。
検証環境
- Xcode 16 (16A242d)
- iOS 16.4 (URLの変換に失敗)
- iOS 17.0 (URLの変換に成功)
- iOS 18.0 (URLの変換に成功)
問題の再現
以下のコードで問題を再現できる。
import SwiftUI
struct Person: Codable {
let name: String
let url: URL
}
func action() {
let zwspJsonString = """
{
"name": "Wada Kenji",
"url": "http://example.com/index.html"
}
"""
do {
let jsonData = zwspJsonString.data(using: .utf8)!
let person = try JSONDecoder().decode(Person.self, from: jsonData)
print("Name: \(person.name)")
} catch {
print("Decoding failed: \(error)")
}
}
問題の詳細
JSONデコード自体はゼロ幅スペースの影響を受けないため、name
フィールドにゼロ幅スペースが含まれていても正常にデコードされる。
しかし、zwspJsonString
を使うと、url
フィールドのデコードに失敗し、以下のようなエラーが発生する。
Decoding failed: dataCorrupted(Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "url", intValue: nil)], debugDescription: "Invalid URL string.", underlyingError: nil))
zwspJsonString
は見た目ではわからないが、「Wada」と「Kenji」の間、URLの最後にゼロ幅スペースが含まれている。この特殊文字は、テキストエディタや専用のViewerで確認できる。
iOS 17以上では問題なくデコードできるが、iOS 16ではURL(string:)
メソッドがゼロ幅スペースを含む文字列の場合にnil
を返してしまうためデコードに失敗する。
解決策
以下の2つの解決策が考えられる。
1. アプリ側でデータクリーニングをおこなう
URLを生成する前にゼロ幅スペースを除去することで解決できる。
import Foundation
extension String {
func removeZeroWidthSpaces() -> String {
return self.replacingOccurrences(of: "\u{200B}", with: "")
}
}
struct Person: Codable {
let name: String
let url: URL
enum CodingKeys: String, CodingKey {
case name, url
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
let urlString = try container.decode(String.self, forKey: .url)
let cleanedUrlString = urlString.removeZeroWidthSpaces()
guard let url = URL(string: cleanedUrlString) else {
throw DecodingError.dataCorrupted(
DecodingError.Context(
codingPath: container.codingPath + [CodingKeys.url],
debugDescription: "Invalid URL string."
)
)
}
self.url = url
}
}
Person
のイニシャライザ内でremoveZeroWidthSpaces()
メソッドを使ってゼロ幅スペースを除去してからURLを生成している。この方法で修正は可能だが、対応文字列の抜けやパフォーマンスへの影響を考えると、アプリ(クライアント側)で対応するのはおすすめできない。
2. JSONの提供者への依頼
JSONデータの提供者に、URLフィールドにゼロ幅スペースを含めないよう依頼する。こちらの方が根本的な解決策になるだろう。
まとめ
アプリ側でゼロ幅スペースを除去することで問題を解決できるが、ストアへの申請やユーザーへの浸透に時間がかかるため、即時解決とは言えない。また、変換漏れやパフォーマンスの問題も考慮する必要があり、アプリ側での対応には限界がある。
一方で、JSONデータの提供者に修正を依頼できれば、それが最も迅速で根本的な解決策となるだろう。