0Pricing
Swift Academy · درس

‏encode(to:) و init(from:) اليدويان

تحكم بالكامل في منطق التسلسل.

‏encode(to:) و init(from:) اليدويان درس مجاني في Swift Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في 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)

الترميز داخل حاوية متداخلة

يمكن للترميز إجراء العملية العكسية أيضاً: إذ يكتب بنية مسطّحة على شكل كائن JSON متداخل باستخدام nestedContainer(keyedBy:forKey:).

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/7) وفتح باقي دورة Swift Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Swift Academy 4 دروس في المجموع.

ماذا ستتعلم في «‏encode(to:) و init(from:) اليدويان»؟

تحكم بالكامل في منطق التسلسل. تتمرن على Swift Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Swift Academy؟

لا تُشترط خبرة سابقة. Swift Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «‏encode(to:) و init(from:) اليدويان»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Swift Academy هذا؟

نعم. كل درس في Swift Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ‏CodingKeys لإعادة التسمية
  2. استراتيجيات فك ترميز المفاتيح والتواريخ
  3. ‏encode(to:) و init(from:) اليدويان
  4. فك ترميز JSON غير المتجانس
← العودة إلى Swift Academy