Published on

Firestore: massivlar (arrays) va map obyektlari bilan ishlash

Authors

1. Array (Massiv) bilan ishlashdagi eng katta xato!

Tasavvur qiling, biz foydalanuvchining yoqtirgan narsalarini (masalan, kino, sport, kitob) bazada preferences: ["Sports", "Movies"] ko'rinishidagi Array sifatida saqlamoqchimiz.

Yangi o'rganayotgan ko'pchilik dasturchilar yangi element qo'shishda quyidagi mantiqni ishlatishadi:

  1. Bazadan foydalanuvchini (hujjatni) yuklab olamiz.
  2. Swift'dagi preferences.append("Books") orqali mahalliy o'zgaruvchiga qo'shamiz.
  3. Yangilangan massivni qaytadan bazaga updateData qilib jo'natamiz.

Nega bu usul juda xavfli? Tasavvur qiling, foydalanuvchi bir vaqtning o'zida ham telefonida, ham iPad'ida ilovaga kirgan. Telefonida "Sport" ni qo'shdi, ayni shu millisekundda iPad'ida "Kino" ni qo'shdi. Ikkala qurilma ham bazadan eski holatni o'qiydi va o'zining qo'shgan narsasini bazaga qayta yozadi. Natijada bittasining ma'lumoti o'chib ketadi (Data overwrite).

To'g'ri usul: FieldValue.arrayUnion va arrayRemove

Bunday muammolarni oldini olish uchun Firebase maxsus server-side komandalarni taqdim etgan. Bunda siz bazadan ma'lumotni yuklab olishingiz shart emas. Shunchaki Firebase'ga "Mana shu qiymatni borib o'sha massivning oxiriga qo'shib qo'y" deysiz.

Massivga ma'lumot qo'shish (Append)

Biz FieldValue.arrayUnion dan foydalanamiz:

func addUserPreference(userId: String, preference: String) async throws {
    let data: [String: Any] = [
        "preferences": FieldValue.arrayUnion([preference])
    ]

    try await userCollection.document(userId).updateData(data)
}

Massivdan ma'lumot o'chirish (Remove)

Buning uchun FieldValue.arrayRemove ishlatiladi:

func removeUserPreference(userId: String, preference: String) async throws {
    let data: [String: Any] = [
        "preferences": FieldValue.arrayRemove([preference])
    ]

    try await userCollection.document(userId).updateData(data)
}

Bu buyruqlar to'g'ridan-to'g'ri serverda bajariladi, shuning uchun ham bir vaqtda 10 ta joydan o'zgartirilsa ham ma'lumotlar yo'qolmaydi (Concurrency-safe).

2. Map (Murakkab obyektlar) bilan ishlash

Deylik, foydalanuvchining qaysidir bitta sevimli kinosi bor. Bu shunchaki matn (String) emas, u o'zining nomi, reytingi, va yiliga ega bo'lgan alohida model:

struct Movie: Codable {
    let id: String
    let title: String
    let isPopular: Bool
}

Biz ushbu Movie modelini to'g'ridan-to'g'ri asosiy DBUser ichiga bitta "property" sifatida qo'shib yuborishimiz mumkin:

struct DBUser: Codable {
    let userId: String
    // ... boshqa maydonlar ...
    var favoriteMovie: Movie? // Custom obyekt (Bazada bu Map deyiladi)
}

Buni bazadan o'qib olish (Decode) juda oson, oldingi darsdagi Codable buni avtomatik tarzda tushunadi (chunki Movie ham Codable qilingan).

Map obyektni bazaga yozishdagi muammo

Agar siz ushbu oynadagi o'zgarishni yangilamoqchi bo'lsangiz:

let myMovie = Movie(id: "1", title: "Avatar 2", isPopular: true)

// BU XATO! Firebase bunday maxsus Struct'ni updateData ichida to'g'ridan to'g'ri tushunmaydi
let data: [String: Any] = ["favorite_movie": myMovie]

Bunday yozish ilovaning "Crash" bo'lishiga (qotib qolishiga) sabab bo'ladi. Sababi, updateData funksiyasi faqat sodda ma'lumot turlarini (Dictionary) qabul qiladi.

Yechim: Biz custom Swift obyektimizni Firebase Encoder yordamida majburiy ravishda [String: Any] (Dictionary) shakliga o'tkazib olishimiz kerak:

func addFavoriteMovie(userId: String, movie: Movie) async throws {
    // 1. Movie obyektini Dictionary'ga aylantirish (Encode)
    guard let movieData = try? Firestore.Encoder().encode(movie) else {
        throw URLError(.badServerResponse)
    }

    // 2. Hosil bo'lgan Map'ni bazaga jo'natish
    let data: [String: Any] = [
        "favorite_movie": movieData
    ]

    try await userCollection.document(userId).updateData(data)
}

Xulosa

Array va Map'lar Firestore'da ma'lumotlarni tartibli va jamlangan holatda saqlash uchun juda zo'r vositalar hisoblanadi.

  1. Array ro'yxatlari uchun har doim FieldValue.arrayUnion va FieldValue.arrayRemove ishlating.
  2. Murakkab Struct modellarini bazaga (Faqat bitta maydonini update qilish orqali) yozyotganda, albatta uni Firestore.Encoder().encode(...) orqali Dictionary'ga o'zgartirib oling.

Ushbu qoidalar ma'lumotlar xavfsizligini taminlaydi va kutilmagan dastur xatoliklarining (crash) oldini oladi. Keyingi darslarda biz yanada ko'proq foydalanuvchilar (kolleksiyalar) orasidan qidiruv va filtrlash qanday ishlashini ko'rib chiqamiz!

Buy mea coffee