Published on

Swiftda Codable

Authors

Codable protokoli

CodableEncodable va Decodable ni birlashtirgan protokol. U Swift ob'ektlarni JSON, XML, Property List kabi formatlarga aylantirish (encode) va qaytarish (decode) imkonini beradi. iOS dasturlashda tarmoq so'rovlari (API dan JSON olish), fayl saqlash va ma'lumotlar bazasi bilan ishlashda har kuni kerak.

Asosiy komponentlar: JSONEncoder — Swift ob'ektni JSON ga aylantiradi. JSONDecoder — JSON ni Swift ob'ektga aylantiradi. Struct yoki class Codable ga mos kelsa — barcha property lar avtomatik encode/decode bo'ladi.

Asosiy encode va decode

Quyida oddiy Codable struct yaratish, uni JSON ga aylantirish (encode) va JSON dan qaytarish (decode) ko'rsatilgan.

// ═══════════════════════════════════════
//  CODABLE STRUCT
// ═══════════════════════════════════════
struct Foydalanuvchi: Codable {
    let id: Int
    let ism: String
    let email: String
    let yosh: Int
}

// ═══════════════════════════════════════
//  ENCODE — Swift → JSON
// ═══════════════════════════════════════
let ali = Foydalanuvchi(id: 1, ism: "Ali", email: "ali@mail.com", yosh: 25)

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted  // Chiroyli format

let jsonData = try encoder.encode(ali)
let jsonMatn = String(data: jsonData, encoding: .utf8)!
print(jsonMatn)
// {
//   "id" : 1,
//   "ism" : "Ali",
//   "email" : "ali@mail.com",
//   "yosh" : 25
// }


// ═══════════════════════════════════════
//  DECODE — JSON → Swift
// ═══════════════════════════════════════
let jsonString = """
{
    "id": 2,
    "ism": "Vali",
    "email": "vali@mail.com",
    "yosh": 30
}
"""

let data = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()

let vali = try decoder.decode(Foydalanuvchi.self, from: data)
print(vali.ism)   // "Vali"
print(vali.email) // "vali@mail.com"

CodingKeys — nom moslash

Ko'p API lar JSON kalitlarni snake_case (masalan, user_id) da yuboradi, lekin Swift da camelCase (userId) ishlatiladi. CodingKeys enum bu nomlarni moslashtiradi. Yoki JSONDecoder ning keyDecodingStrategy ni .convertFromSnakeCase ga o'rnatib, avtomatik konvertatsiya qilish mumkin.

// ═══════════════════════════════════════
//  SERVER JSON — snake_case (ko'p API lar shunday)
// ═══════════════════════════════════════
// {
//   "user_id": 1,
//   "full_name": "Ali Valiyev",
//   "email_address": "ali@mail.com",
//   "is_active": true
// }

struct Foydalanuvchi: Codable {
    let userId: Int          // user_id → userId
    let fullName: String     // full_name → fullName
    let emailAddress: String // email_address → emailAddress
    let isActive: Bool       // is_active → isActive

    // JSON kalitlarni Swift property larga moslashtirish
    enum CodingKeys: String, CodingKey {
        case userId = "user_id"
        case fullName = "full_name"
        case emailAddress = "email_address"
        case isActive = "is_active"
    }
}

// YOKI — avtomatik konvertatsiya
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
// Endi CodingKeys yozish shart emas — avtomatik ishlaydi!

Nested JSON — ichma-ich tuzilma

Real API lardan keladigan JSON ko'pincha ichma-ich bo'ladi — ob'ekt ichida ob'ekt, massiv. Har bir ichki tuzilma uchun alohida Codable struct yaratiladi. Swift avtomatik ravishda ularni to'g'ri decode qiladi.

// ═══════════════════════════════════════
//  ICHMA-ICH JSON
// ═══════════════════════════════════════
// {
//   "id": 1,
//   "name": "Ali",
//   "address": {
//     "city": "Toshkent",
//     "street": "Navoiy ko'chasi",
//     "zip": "100001"
//   },
//   "phones": ["998901234567", "998712345678"]
// }

// Har bir ichki ob'ekt — alohida Codable struct
struct Manzil: Codable {
    let city: String
    let street: String
    let zip: String
}

struct Shaxs: Codable {
    let id: Int
    let name: String
    let address: Manzil      // Ichki ob'ekt
    let phones: [String]      // Massiv
}

let json = """
{
    "id": 1,
    "name": "Ali",
    "address": {
        "city": "Toshkent",
        "street": "Navoiy ko'chasi",
        "zip": "100001"
    },
    "phones": ["998901234567", "998712345678"]
}
""".data(using: .utf8)!

let shaxs = try JSONDecoder().decode(Shaxs.self, from: json)
print(shaxs.address.city)   // "Toshkent"
print(shaxs.phones[0])      // "998901234567"

Sana bilan ishlash

JSON da sanalar turli formatlarda keladi: ISO 8601 ("2024-01-15T10:30:00Z"), Unix timestamp (1705312200), yoki custom ("15.01.2024"). JSONDecoder ning dateDecodingStrategy xususiyatini o'rnatib, qaysi formatni ishlatishni aytish kerak.

struct Hodisa: Codable {
    let nomi: String
    let sana: Date
}

// ISO 8601 format: "2024-01-15T10:30:00Z"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601

// Custom format
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
decoder.dateDecodingStrategy = .formatted(formatter)
// Endi "15.01.2024" formatini tushunadi

Massiv decode qilish

JSON massivni decode qilish uchun [Struct].self ishlatiladi. Har bir element alohida decode bo'ladi.

// ═══════════════════════════════════════
//  JSON MASSIV
// ═══════════════════════════════════════
let jsonMassiv = """
[
    {"id": 1, "ism": "Ali", "email": "ali@mail.com", "yosh": 25},
    {"id": 2, "ism": "Vali", "email": "vali@mail.com", "yosh": 30},
    {"id": 3, "ism": "Gani", "email": "gani@mail.com", "yosh": 22}
]
""".data(using: .utf8)!

// [Foydalanuvchi].self — massiv sifatida decode
let foydalanuvchilar = try JSONDecoder().decode(
    [Foydalanuvchi].self,
    from: jsonMassiv
)
print(foydalanuvchilar.count)  // 3

Optional va default qiymatlar

Optional property — JSON da topilmasa nil bo'ladi (crash emas). Lekin Codable da default qiymat (var miqdor: Int = 1) ishlamaydi — buning uchun init(from:) ni o'zingiz yozishingiz va decodeIfPresent bilan default qiymat berishingiz kerak.

struct Mahsulot: Codable {
    let id: Int
    let nomi: String
    let tavsif: String?        // JSON da bo'lmasa nil
    let narx: Double
    let rasm: String?          // JSON da bo'lmasa nil
    var miqdor: Int = 1        // Lekin Codable da default ishlamaydi!
}

// Default qiymat uchun — init(from:) yozish kerak
struct MahsulotYaxshi: Codable {
    let id: Int
    let nomi: String
    let tavsif: String?
    let narx: Double
    var miqdor: Int

    enum CodingKeys: String, CodingKey {
        case id, nomi, tavsif, narx, miqdor
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        nomi = try container.decode(String.self, forKey: .nomi)
        tavsif = try container.decodeIfPresent(String.self, forKey: .tavsif)
        narx = try container.decode(Double.self, forKey: .narx)
        // Default qiymat — JSON da bo'lmasa 1
        miqdor = try container.decodeIfPresent(Int.self, forKey: .miqdor) ?? 1
    }
}

🎯 Topshiriq

Quyidagi JSON ni decode qiling:

{"user": {"name": "Ali", "age": 25}, "posts": [{"title": "Salom", "likes": 10}]}

Uchta struct yarating: APIJavob, Foydalanuvchi, Post. Decode qilib, user.name va birinchi post.title ni chiqaring. Encode qilib JSON matnga aylantiring.

Buy mea coffee