前回の記事:
今回から、基本的な使い方とマッピング手法を学んでいきます。
Contents 非表示
4. 基本的な使い方
Mappableプロトコルとは
ObjectMapperの中心となるのが Mappable プロトコルです。
protocol Mappable {
init?(map: Map)
mutating func mapping(map: Map)
}この2つを実装することで、JSON → Model / Model → JSONの相互変換が可能となります。
基本的な考え方:
どのJSONキーを、どのプロパティに割り当てるか」を明示的に書く
これを一旦理解しておけばOKです。
init?(map:) の役割
init?(map:) = 初期化のタイミングで呼ばれるイニシャライザ
init?(map: Map) {
}基本的に中身は空のままで問題ありません。
しかし、以下のような用途で使われることがあります。
- 必須項目が存在しない場合に
nilを返す - mapの中身をチェックして生成可否を判断する
例:
init?(map: Map) {
if map.JSON["id"] == nil {
return nil
}
}
// = 「このJSONからModelを生成してよいか?」を判断する場所mapping(map:) の書き方
実際のマッピング処理は mapping(map:) に書きます。
mutating func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
}ここで使われている <- は、ObjectMapper独自の演算子です。
- 左辺:Modelのプロパティ
- 右辺:JSONのキー
という対応関係を表しています。
JSON → Model の変換例
実際のJSONを例に見てみましょう。
{
"id": 1,
"name": "Arthur"
}対応するModelは以下のようになります。
import ObjectMapper
struct User: Mappable {
var id: Int?
var name: String?
init?(map: Map) {}
mutating func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
}
}変換処理はシンプルです。
let jsonString = """
{
"id": 1,
"name": "Arthur"
}
"""
if let user = Mapper<User>().map(JSONString: jsonString) {
print(user.name) // Optional("Arthur")
}
5. よく使うマッピングテクニック
JSONキー名が異なる場合
JSONとModelで名前が一致しないケースは稀によくあります (適当)
{
"user_name": "Arthur"
}
var userName: String?この場合も、mapping でキーを指定するだけOKです。
mutating func mapping(map: Map) {
userName <- map["user_name"]
}Codable の CodingKeys よりも直感的に書けることがメリット?と感じる人もいるかもですね。
Optionalなプロパティの扱い
ObjectMapperでは、Optionalの扱いも簡単です。
var age: Int?JSONにキーが存在しない、または null の場合でも、
クラッシュせず nil が代入されます。
mutating func mapping(map: Map) {
age <- map["age"]
}特別な設定は不要です。
ネストしたJSONのマッピング
実務でよく見るネスト構造です。
{
"id": 1,
"profile": {
"email": "test@example.com"
}
}
// Modelを分ける場合
struct Profile: Mappable {
var email: String?
init?(map: Map) {}
mutating func mapping(map: Map) {
email <- map["email"]
}
}
struct User: Mappable {
var id: Int?
var profile: Profile?
init?(map: Map) {}
mutating func mapping(map: Map) {
id <- map["id"]
profile <- map["profile"]
}
}
配列のマッピング:
{
"users": [
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" }
]
}
struct Response: Mappable {
var users: [User]?
init?(map: Map) {}
mutating func mapping(map: Map) {
users <- map["users"]
}
}このあたりは Codable と比べても直感的で、
ObjectMapperが「柔軟」と言われる理由の一つです。
6. ObjectMapperの便利機能
TransformTypeの使い方
ObjectMapperの強みのひとつが Transform です。
JSONとModelの型が一致しない場合に変換処理を挟めます。
Transformは TransformType プロトコルを使います。
public protocol TransformType {
associatedtype Object
associatedtype JSON
func transformFromJSON(_ value: Any?) -> Object?
func transformToJSON(_ value: Object?) -> JSON?
}
// 要するに、**「JSON → Swift」「Swift → JSON」両方向の変換を書く」**だけ日付(Date)の変換
APIでよくあるのが、Stringで返ってくる日付です。
{
"created_at": "2024-12-01T10:00:00Z"
}ObjectMapperには、あらかじめ用意された DateTransform があります。
struct Article: Mappable {
var createdAt: Date?
init?(map: Map) {}
mutating func mapping(map: Map) {
createdAt <- (map["created_at"], DateTransform())
}
}
// フォーマット指定も可能
DateTransform(dateFormat: "yyyy-MM-dd'T'HH:mm:ssZ")Codable で DateFormatter を毎回書くよりも、
マッピング箇所に変換ロジックを閉じ込められるのが特徴です。(最近のCodableもそこまで煩雑ではない記憶ですが)
カスタムTransformの例
例えば、Intで返ってくるフラグをBoolに変換したい場合。
{
"is_active": 1
}
struct IntToBoolTransform: TransformType {
func transformFromJSON(_ value: Any?) -> Bool? {
guard let intValue = value as? Int else { return nil }
return intValue == 1
}
func transformToJSON(_ value: Bool?) -> Int? {
guard let value = value else { return nil }
return value ? 1 : 0
}
}
// 使用例
mutating func mapping(map: Map) {
isActive <- (map["is_active"], IntToBoolTransform())
}「変換を部品化できる」 点が、ObjectMapperの特徴。
7. Codableとの比較
書きやすさの違い
まずはシンプルなModelで比較してみます。
ObjectMapper
struct User: Mappable {
var id: Int?
var name: String?
init?(map: Map) {}
mutating func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
}
}Codable
struct User: Codable {
let id: Int
let name: String
}単純な構造なら、Codableの圧勝。
柔軟性の違い
JSONキー変換、型変換、ネスト処理が増えてくると差が出ます。(といってもこれは感じ方に個人差がありますが)
Codable(例)
enum CodingKeys: String, CodingKey {
case userName = "user_name"
}DateやBool変換も init(from:) を書く必要があり、
Modelが肥大化しやすくなります。
ObjectMapper
userName <- map["user_name"]
isActive <- (map["is_active"], IntToBoolTransform())変換ロジックをmappingに集約できるのが強み。
既存プロジェクトでの使い分け
実務では、以下のような判断がよさそうかもです。
- 新規開発
- → Codable一択
- 既存ObjectMapperプロジェクト
- → 無理に置き換えない
- API仕様が不安定/型揺れが激しい
- → ObjectMapperの方が楽な場合あり
特にBFFや古いAPIを叩くアプリでは、
ObjectMapperの「許容力」が助けになるケースもあります。
今から使うなら
結論:
- 学ぶ価値:✅(既存コード理解のため)
- 新規採用:❌(基本はしない)
- 保守運用:⭕️(状況次第)
ObjectMapperは
「過去の遺産」ではなく「読めると強いライブラリ」
という位置付けが一番しっくりきそうです、おそらく。
8. まとめ
- なぜ昔はこれが選ばれていたのか
- Codableが「進化」だと実感できる理由
- 柔軟性と簡潔さのトレードオフ
この辺りを改めて復習できて非常に有意義な学びとなりました。
単なる「古いライブラリ」ではなく、
Swiftの歴史を理解する上でも意味のある存在と再認識できて、今後の開発でも詰まったらここに戻ってきて見直そうと思います、自分でも。
