0Pricing
Swift Academy · レッスン

手動のencode(to:)とinit(from:)

シリアライズ処理を完全に制御します。

「手動のencode(to:)とinit(from:)」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。

自動合成だけでは不十分な場合

JSONの構造がプロパティと一致しないことがあります。計算による変換やデフォルト値へのフォールバック、フラット化が必要な場合は、encode(to:) と init(from:) を手動で実装します。

import Foundation

struct Temperature: Codable {
    var celsius: Double
}

print("Manual coding gives full control")

最初にCodingKeysを宣言する

手動コーディングは CodingKeys 列挙型の宣言から始まります。キー付きコンテナは、このキーを使って値の読み書きを行います。

import Foundation

struct User {
    var name: String
    var age: Int
    enum CodingKeys: String, CodingKey {
        case name
        case age
    }
}

print(User.CodingKeys.name.stringValue)

init(from:)の実装

init(from decoder:) は container(keyedBy:) を使ってキー付きコンテナを取得し、その後、各プロパティを対応するキーでデコードします。

import Foundation

struct User: Decodable {
    var name: String
    var age: Int
    enum CodingKeys: String, CodingKey { case name, age }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        name = try c.decode(String.self, forKey: .name)
        age = try c.decode(Int.self, forKey: .age)
    }
}

let u = try JSONDecoder().decode(User.self, from: "{\"name\":\"Ada\",\"age\":36}".data(using: .utf8)!)
print(u.name, u.age)

encode(to:)の実装

encode(to encoder:) は container(keyedBy:) を使って変更可能なキー付きコンテナを取得し、encode(_:forKey:) で各プロパティを書き込みます。

import Foundation

struct User: Encodable {
    var name: String
    var age: Int
    enum CodingKeys: String, CodingKey { case name, age }
    func encode(to encoder: Encoder) throws {
        var c = encoder.container(keyedBy: CodingKeys.self)
        try c.encode(name, forKey: .name)
        try c.encode(age, forKey: .age)
    }
}

let data = try JSONEncoder().encode(User(name: "Ada", age: 36))
print(String(data: data, encoding: .utf8)!)

手動で完全なCodableを実装する

両方のメソッドを実装すると、両方向にカスタムロジックを持つ、完全な Codable 型になります。

import Foundation

struct Point: Codable {
    var x: Int
    var y: Int
    enum CodingKeys: String, CodingKey { case x, y }
    init(x: Int, y: Int) { self.x = x; self.y = y }
    init(from d: Decoder) throws {
        let c = try d.container(keyedBy: CodingKeys.self)
        x = try c.decode(Int.self, forKey: .x)
        y = try c.decode(Int.self, forKey: .y)
    }
    func encode(to e: Encoder) throws {
        var c = e.container(keyedBy: CodingKeys.self)
        try c.encode(x, forKey: .x)
        try c.encode(y, forKey: .y)
    }
}

let back = try JSONDecoder().decode(Point.self, from: try JSONEncoder().encode(Point(x: 1, y: 2)))
print(back.x, back.y)

デフォルト値を指定する

キーが存在しない場合にエラーを投げる代わりに、decodeIfPresent とnil合体演算子を使ってデフォルト値を指定できます。

import Foundation

struct Settings: Decodable {
    var theme: String
    enum CodingKeys: String, CodingKey { case theme }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        theme = try c.decodeIfPresent(String.self, forKey: .theme) ?? "light"
    }
}

let s = try JSONDecoder().decode(Settings.self, from: "{}".data(using: .utf8)!)
print(s.theme)

値を変換する

手動デコードを使うと、生のJSONをより扱いやすい表現に変換できます。ここでは、華氏で入力された値を摂氏として保存しています。

import Foundation

struct Reading: Decodable {
    var celsius: Double
    enum CodingKeys: String, CodingKey { case fahrenheit }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        let f = try c.decode(Double.self, forKey: .fahrenheit)
        celsius = (f - 32) * 5 / 9
    }
}

let r = try JSONDecoder().decode(Reading.self, from: "{\"fahrenheit\":212}".data(using: .utf8)!)
print(r.celsius)

ネストされたJSONをフラット化する

nestedContainer(keyedBy:forKey:) を使ってネストされたコンテナを読み取り、その値をフラットなSwift構造体のプロパティとして取り出せます。

import Foundation

struct Profile: Decodable {
    var city: String
    enum Keys: String, CodingKey { case address }
    enum AddressKeys: String, CodingKey { case city }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: Keys.self)
        let a = try c.nestedContainer(keyedBy: AddressKeys.self, forKey: .address)
        city = try a.decode(String.self, forKey: .city)
    }
}

let json = "{\"address\":{\"city\":\"Rome\"}}"
let p = try JSONDecoder().decode(Profile.self, from: json.data(using: .utf8)!)
print(p.city)

ネストされたコンテナへのエンコード

エンコードでは反対の操作もできます。nestedContainer(keyedBy:forKey:) を使って、フラットな構造体をネストされたJSONオブジェクトとして書き出します。

import Foundation

struct Profile: Encodable {
    var city: String
    enum Keys: String, CodingKey { case address }
    enum AddressKeys: String, CodingKey { case city }
    func encode(to encoder: Encoder) throws {
        var c = encoder.container(keyedBy: Keys.self)
        var a = c.nestedContainer(keyedBy: AddressKeys.self, forKey: .address)
        try a.encode(city, forKey: .city)
    }
}

let data = try JSONEncoder().encode(Profile(city: "Rome"))
print(String(data: data, encoding: .utf8)!)

デコード中に検証する

init(from:) は通常のイニシャライザーと同じものなので、値を検証し、範囲外の場合はカスタムエラーを投げられます。

import Foundation

struct Score: Decodable {
    var value: Int
    enum CodingKeys: String, CodingKey { case value }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        let v = try c.decode(Int.self, forKey: .value)
        guard (0...100).contains(v) else {
            throw DecodingError.dataCorruptedError(forKey: .value, in: c, debugDescription: "out of range")
        }
        value = v
    }
}

let s = try JSONDecoder().decode(Score.self, from: "{\"value\":80}".data(using: .utf8)!)
print(s.value)

手動実装と自動合成を組み合わせる

型は一方のメソッドだけを手動で実装し、もう一方をコンパイラーに自動合成させることができます。たとえば、init(from:) をカスタム実装し、エンコードは自動合成させる方法です。

import Foundation

struct Flag: Codable {
    var on: Bool
    enum CodingKeys: String, CodingKey { case on }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        on = (try? c.decode(Bool.self, forKey: .on)) ?? false
    }
    init(on: Bool) { self.on = on }
}

let data = try JSONEncoder().encode(Flag(on: true))
print(String(data: data, encoding: .utf8)!)

理解度チェック:手動コーディング

手書きのCodableメソッドについての理解度を確認しましょう。

まとめ:手動のencode(to:)とinit(from:)

コーディングを完全に制御する方法を学びました。

  • CodingKeys を宣言し、init(from:) と encode(to:) を実装します。
  • container(keyedBy:) と encode/decode(_:forKey:) を使います。
  • decodeIfPresent と ?? を組み合わせるとデフォルト値を指定できます。nestedContainer はJSONのフラット化やネスト化に使えます。
  • 値の検証や変換を行い、手動実装と自動合成されたメソッドを組み合わせることもできます。

よくある質問

「手動のencode(to:)とinit(from:)」レッスンは無料ですか?

はい。「手動のencode(to:)とinit(from:)」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。

「手動のencode(to:)とinit(from:)」で何を学びますか?

シリアライズ処理を完全に制御します。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Swift Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「手動のencode(to:)とinit(from:)」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSwift Academyレッスンでコードを書いて実行できますか?

はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 名前変更のためのCodingKeys
  2. キーと日付のデコード戦略
  3. 手動のencode(to:)とinit(from:)
  4. 異種JSONのデコード
← Swift Academyに戻る