はじめに
長年利用しているAPIだと実装者が入れ替わり、APIの設計も少しずつ差分が生じてきますよね。
APIサイドに改善要望を出せば良いのかもしれないですが、AndroidやWebなど他のデバイスでも変更対応が入ってしまい、全デバイス横断の大掛かりな修正対応になってしまう可能性もありますよね。
私自身このような辛い経験を何度かしてきて、その時に役に立った方法を紹介します。
返り値が何型でもBoolに変換する
目標はこれらのような true/false以外の値でも、問題なくBool値としてAPIから取得し扱えるようにすることです。
.json
{
"isLiked": "t",
"isFollowed": "true",
"isBookmark": 1,
"isPurchased": true
}
対応方法
PropertyWrapperを作成します。
AnyToBool.swift
@propertyWrapper
struct AnyToBool: Decodable {
var wrappedValue: Bool
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let boolValue = try? container.decode(Bool.self) {
wrappedValue = boolValue
} else if let intValue = try? container.decode(Int.self) {
wrappedValue = intValue != 0
} else if let stringValue = try? container.decode(String.self) {
let trueSet: Set = ["t", "T", "true"]
wrappedValue = trueSet.contains(stringValue)
} else {
wrappedValue = false
}
}
}
使い方
お気に入りしたかや購入したかのフラグを管理する情報をAPIから取得する。
Feature型には4つのBool変数を持っています。
Feature.swift
struct Feature: Decodable {
@AnyToBool var isLiked: Bool
@AnyToBool var isFollowed: Bool
@AnyToBool var isBookmark: Bool
@AnyToBool var isPurchased: Bool
}
以下のJSONのように真偽値に関する情報が true/false以外の場合でも正しくBool型に変換して取得できることが確認できる。
FeatureTest.swift
let jsonData = """
{
"isLiked": "t",
"isFollowed": "true",
"isBookmark": 1,
"isPurchased": true
}
""".data(using: .utf8)!
let decodedFeature = try! JSONDecoder().decode(Feature.self, from: jsonData)
print(decodedFeature.isLiked) // true
print(decodedFeature.isFollowed) // true
print(decodedFeature.isBookmark) // true
print(decodedFeature.isPurchased) // true