SwiftLint:non_optional_string_data_conversion の落とし穴
この記事はQiitaで投稿した記事の移植記事です。
はじめに
APIから取得した data型を .utf8 の String に変換したい場合、SwiftLint ではタイトルの通り、non_optional_string_data_conversion
という warning が発生します。
今回は、このnon_optional_string_data_conversion
について気づいた点があったので、ご紹介します。
使用技術
- Swift: 5.10(6.0.0)
- Xcode16
- SwiftLint
non_optional_string_data_conversion
とは?
さて、今回の話の中心となる non_optional_string_data_conversion
とはなんでしょうか。
タイトルにもある通り、これは SwiftLint のルールの1つです。
このルールの内容は、
Prefer using UTF-8 encoded strings when converting between
String
andData
訳:String
とData
間の変換時に、UTF-8エンコードされた文字列を優先的に使用する
というものです。
例えば、以下のコードはこのルールには違反しません。
Data("foo".utf8)
String(decoding: data, as: UTF8.self)
一方で、以下のコードは違反となり、トリガーされます。トリガーされたコードは、Xcode上で warning
として表示されます。
"foo".data(using: .utf8)
String(data: data, encoding: .utf8)
注目すべきポイント
今回注目したいのは、Data 型をエンコードする際に使う、String(decoding: data, as: UTF8.self)
についてです。
このメソッドの便利な点は、String 型を返す点です。
一方、Xcode上で warning が出る String(data: data, encoding: .utf8)
は、Optional<String>
を返します。
では、両者の違いはなんでしょうか。
String(decoding: data, as: UTF8.self)
は、たとえ data
がUTF-8として正しくエンコードできない場合でも、String 型として値を返します。
それに対して、String(data: data, encoding: .utf8)
は、エンコードに失敗すると nil を返します。
つまり、String(decoding: data, as: UTF8.self)
は、デコードに失敗した場合でも文字化けした String 型を返してしまう可能性があるということです。
したがって、このイニシャライザを使用する際は、data
が確実に UTF-8 としてエンコード可能であることを確認した上で使用することをお勧めします。
以下は、この問題に関するサンプルコードです。
この問題の落とし穴
上記を踏まえて、今回注意したいのは、non_optional_string_data_conversion
がSwiftLint 上では "Enabled by default: Yes"、つまりデフォルトのルールとして設定されている点です。
そのため、.swiftlint.yml
ファイルに、このルールを除外する必要があります。
ルールの除外方法
このルールの除外方法は簡単です。
ファイル内で disabled_rules
の配下に non_optional_string_data_conversion
を追記するだけです。
disabled_rules:
- ... 除外したいその他のルール
- non_optional_string_data_conversion
- ...
エンコードが確実ではない場合の回避方法
では、UTF-8でエンコード可能であると確実に言えない場合、どのようにしてエンコードしたら良いでしょうか。
1つの解決策として、オプショナルバインディングして、nil の場合は別の文字列を返すのが良いでしょう。
以下は、そのサンプルことです。
if let html = String(data: invalidData, encoding: .utf8) {
print(html)
} else {
print("エンコード失敗")
}
最後に
今回紹介した「落とし穴」は、String(decoding: data, as: UTF8.self)
を推奨するルールが SwiftLint でデフォルトで有効化されている点にあります。
そのため、必要に応じて .swiftlint.yml
に設定を追加する必要があります。
また、エンコードが確実ではない場合、オプショナルバインディングをしてあげる必要があります。
参考
Discussion