Apple platformlarında Veri Okuma ve Yazma

(İsteğe bağlı) Firebase Local Emulator Suite ile prototip yapın ve test edin

Uygulamanızın Gerçek Zamanlı Veritabanından nasıl okuduğu ve ona nasıl yazdığı hakkında konuşmadan önce, Gerçek Zamanlı Veritabanı işlevselliğini prototiplemek ve test etmek için kullanabileceğiniz bir dizi aracı tanıtalım: Firebase Local Emulator Suite. Farklı veri modelleri deniyorsanız, güvenlik kurallarınızı optimize ediyorsanız veya arka uçla etkileşim kurmanın en uygun maliyetli yolunu bulmaya çalışıyorsanız, canlı hizmetleri dağıtmadan yerel olarak çalışabilmek harika bir fikir olabilir.

Gerçek Zamanlı Veritabanı öykünücüsü, uygulamanızın öykünülmüş veritabanı içeriğiniz ve yapılandırmanızın yanı sıra isteğe bağlı olarak öykünülmüş proje kaynaklarınızla (işlevler, diğer veritabanları ve güvenlik kuralları) etkileşim kurmasını sağlayan Yerel Öykünücü Paketinin bir parçasıdır.

Gerçek Zamanlı Veritabanı öykünücüsünü kullanmak yalnızca birkaç adımı içerir:

  1. Öykünücüye bağlanmak için uygulamanızın test yapılandırmasına bir kod satırı ekleme.
  2. Yerel proje dizininizin kökünden, firebase emulators:start çalıştırılıyor.
  3. Her zamanki gibi bir Realtime Database platformu SDK'sını kullanarak veya Realtime Database REST API'yi kullanarak uygulamanızın prototip kodundan çağrı yapmak.

Gerçek Zamanlı Veritabanı ve Bulut İşlevlerini içeren ayrıntılı bir kılavuz mevcuttur. Ayrıca Local Emulator Suite tanıtımına da göz atmalısınız.

FIRDatabaseReference'ı edinin

Veritabanından veri okumak veya yazmak için bir FIRDatabaseReference örneğine ihtiyacınız vardır:

Süratli

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
var ref: DatabaseReference!

ref = Database.database().reference()

Amaç-C

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
@property (strong, nonatomic) FIRDatabaseReference *ref;

self.ref = [[FIRDatabase database] reference];

Veri yaz

Bu belge Firebase verilerini okuma ve yazmanın temellerini kapsar.

Firebase verileri bir Database referansına yazılır ve referansa eşzamansız bir dinleyici eklenerek alınır. Dinleyici, verinin başlangıç ​​durumu için bir kez ve veri değiştiğinde tekrar tetiklenir.

Temel yazma işlemleri

Temel yazma işlemleri için, verileri belirli bir referansa kaydetmek ve o yoldaki mevcut verileri değiştirmek için setValue kullanabilirsiniz. Bu yöntemi şu amaçlarla kullanabilirsiniz:

  • Mevcut JSON türlerine karşılık gelen geçiş türleri aşağıdaki gibidir:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

Örneğin setValue ile bir kullanıcıyı aşağıdaki gibi ekleyebilirsiniz:

Süratli

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
self.ref.child("users").child(user.uid).setValue(["username": username])

Amaç-C

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
[[[self.ref child:@"users"] child:authResult.user.uid]
    setValue:@{@"username": username}];

setValue bu şekilde kullanılması, alt düğümler de dahil olmak üzere belirtilen konumdaki verilerin üzerine yazar. Ancak nesnenin tamamını yeniden yazmadan bir alt öğeyi yine de güncelleyebilirsiniz. Kullanıcıların profillerini güncellemesine izin vermek istiyorsanız kullanıcı adını aşağıdaki şekilde güncelleyebilirsiniz:

Süratli

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
self.ref.child("users/\(user.uid)/username").setValue(username)

Amaç-C

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

Verileri oku

Değer olaylarını dinleyerek verileri okuyun

Bir yoldaki verileri okumak ve değişiklikleri dinlemek için, FIRDataEventTypeValue olaylarını gözlemlemek üzere FIRDatabaseReference observeEventType:withBlock kullanın.

Etkinlik tipi Tipik kullanım
FIRDataEventTypeValue Bir yolun tüm içeriğindeki değişiklikleri okuyun ve dinleyin.

Olay anında mevcut olan belirli bir yoldaki verileri okumak için FIRDataEventTypeValue olayını kullanabilirsiniz. Bu yöntem, dinleyici eklendiğinde bir kez tetiklenir ve çocuklar da dahil olmak üzere veriler her değiştiğinde tekrar tetiklenir. Olay geri çağrısına, alt veriler de dahil olmak üzere o konumdaki tüm verileri içeren bir snapshot iletilir. Veri yoksa, exists() öğesini çağırdığınızda anlık görüntü false , value özelliğini okuduğunuzda ise nil değerini döndürür.

Aşağıdaki örnek, veritabanından bir gönderinin ayrıntılarını alan bir sosyal blog uygulamasını göstermektedir:

Süratli

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
refHandle = postRef.observe(DataEventType.value, with: { snapshot in
  // ...
})

Amaç-C

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

Dinleyici, value özelliğinde olay anında veritabanında belirtilen konumdaki verileri içeren bir FIRDataSnapshot alır. Değerleri NSDictionary gibi uygun yerel türe atayabilirsiniz. Konumda veri yoksa value nil .

Verileri bir kez okuyun

getData() kullanarak bir kez okuyun

SDK, uygulamanızın çevrimiçi veya çevrimdışı olmasına bakılmaksızın veritabanı sunucularıyla olan etkileşimleri yönetmek için tasarlanmıştır.

Genel olarak, arka uçtan verilerdeki güncellemelerden haberdar olmak amacıyla verileri okumak için yukarıda açıklanan değer olayı tekniklerini kullanmalısınız. Bu teknikler, kullanımınızı ve faturalandırmanızı azaltır ve kullanıcılarınıza çevrimiçi ve çevrimdışıyken en iyi deneyimi sunmak için optimize edilmiştir.

Verilere yalnızca bir kez ihtiyacınız varsa, veritabanındaki verilerin anlık görüntüsünü almak için getData() kullanabilirsiniz. Herhangi bir nedenle getData() sunucu değerini döndüremezse, istemci yerel depolama önbelleğini araştıracak ve değer hala bulunamazsa bir hata döndürecektir.

Aşağıdaki örnek, bir kullanıcının herkese açık kullanıcı adının veritabanından tek bir kez alınmasını gösterir:

Süratli

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
do {
  let snapshot = try await ref.child("users/\(uid)/username").getData()
  let userName = snapshot.value as? String ?? "Unknown"
} catch {
  print(error)
}

Amaç-C

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
NSString *userPath = [NSString stringWithFormat:@"users/%@/username", uid];
[[ref child:userPath] getDataWithCompletionBlock:^(NSError * _Nullable error, FIRDataSnapshot * _Nonnull snapshot) {
  if (error) {
    NSLog(@"Received an error %@", error);
    return;
  }
  NSString *userName = snapshot.value;
}];

getData() 'nın gereksiz kullanımı, bant genişliği kullanımını artırabilir ve performans kaybına neden olabilir; bu, yukarıda gösterildiği gibi gerçek zamanlı bir dinleyici kullanılarak önlenebilir.

Verileri bir gözlemciyle bir kez okuyun

Bazı durumlarda, sunucuda güncelleştirilmiş bir değer olup olmadığını kontrol etmek yerine, yerel önbellekteki değerin hemen döndürülmesini isteyebilirsiniz. Bu gibi durumlarda, verileri yerel disk önbelleğinden hemen almak için observeSingleEventOfType kullanabilirsiniz.

Bu, yalnızca bir kez yüklenmesi gereken ve sık sık değişmesi beklenmeyen veya aktif dinleme gerektirmeyen veriler için kullanışlıdır. Örneğin, önceki örneklerdeki blog uygulaması, yeni bir gönderi yazmaya başladığında kullanıcının profilini yüklemek için bu yöntemi kullanır:

Süratli

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
let userID = Auth.auth().currentUser?.uid
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { snapshot in
  // Get user value
  let value = snapshot.value as? NSDictionary
  let username = value?["username"] as? String ?? ""
  let user = User(username: username)

  // ...
}) { error in
  print(error.localizedDescription)
}

Amaç-C

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
NSString *userID = [FIRAuth auth].currentUser.uid;
[[[_ref child:@"users"] child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  // Get user value
  User *user = [[User alloc] initWithUsername:snapshot.value[@"username"]];

  // ...
} withCancelBlock:^(NSError * _Nonnull error) {
  NSLog(@"%@", error.localizedDescription);
}];

Verileri güncelleme veya silme

Belirli alanları güncelle

Diğer alt düğümlerin üzerine yazmadan bir düğümün belirli alt öğelerine aynı anda yazmak için updateChildValues ​​yöntemini kullanın.

updateChildValues ​​öğesini çağırırken, anahtar için bir yol belirterek alt düzey alt değerleri güncelleyebilirsiniz. Veriler daha iyi ölçeklendirmek için birden fazla konumda depolanıyorsa, veri yayma özelliğini kullanarak bu verilerin tüm örneklerini güncelleyebilirsiniz. Örneğin, bir sosyal blog uygulaması bir gönderi oluşturmak ve aynı anda bunu en son etkinlik akışına ve gönderiyi gönderen kullanıcının etkinlik akışına güncellemek isteyebilir. Bunu yapmak için blog uygulaması şuna benzer bir kod kullanır:

Süratli

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
guard let key = ref.child("posts").childByAutoId().key else { return }
let post = ["uid": userID,
            "author": username,
            "title": title,
            "body": body]
let childUpdates = ["/posts/\(key)": post,
                    "/user-posts/\(userID)/\(key)/": post]
ref.updateChildValues(childUpdates)

Amaç-C

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
NSString *key = [[_ref child:@"posts"] childByAutoId].key;
NSDictionary *post = @{@"uid": userID,
                       @"author": username,
                       @"title": title,
                       @"body": body};
NSDictionary *childUpdates = @{[@"/posts/" stringByAppendingString:key]: post,
                               [NSString stringWithFormat:@"/user-posts/%@/%@/", userID, key]: post};
[_ref updateChildValues:childUpdates];

Bu örnek /posts/$postid adresindeki tüm kullanıcılar için gönderileri içeren düğümde bir gönderi oluşturmak ve getKey() ile aynı anda anahtarı almak için childByAutoId kullanır. Anahtar daha sonra kullanıcının /user-posts/$userid/$postid adresindeki gönderilerinde ikinci bir giriş oluşturmak için kullanılabilir.

Bu yolları kullanarak, JSON ağacındaki birden fazla konuma, updateChildValues ​​çağrısıyla eşzamanlı güncellemeler gerçekleştirebilirsiniz; örneğin bu örnekte her iki konumda da yeni gönderi nasıl oluşturulur. Bu şekilde yapılan eşzamanlı güncellemeler atomiktir: ya tüm güncellemeler başarılı olur ya da tüm güncellemeler başarısız olur.

Tamamlama Bloğu Ekle

Verilerinizin ne zaman kaydedildiğini bilmek istiyorsanız bir tamamlama bloğu ekleyebilirsiniz. Hem setValue hem de updateChildValues veritabanına yazma işlemi tamamlandığında çağrılan isteğe bağlı bir tamamlama bloğunu alır. Bu dinleyici, hangi verilerin kaydedildiğini ve hangi verilerin hala senkronize edildiğini takip etmek için yararlı olabilir. Çağrı başarısız olursa, dinleyiciye hatanın neden oluştuğunu belirten bir hata nesnesi iletilir.

Süratli

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
do {
  try await ref.child("users").child(user.uid).setValue(["username": username])
  print("Data saved successfully!")
} catch {
  print("Data could not be saved: \(error).")
}

Amaç-C

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
[[[_ref child:@"users"] child:user.uid] setValue:@{@"username": username} withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  if (error) {
    NSLog(@"Data could not be saved: %@", error);
  } else {
    NSLog(@"Data saved successfully.");
  }
}];

Verileri sil

Verileri silmenin en basit yolu, söz konusu verinin konumuna ilişkin bir referans üzerinden removeValue çağırmaktır.

setValue veya updateChildValues ​​gibi başka bir yazma işleminin değeri olarak nil belirterek de silebilirsiniz. Tek bir API çağrısında birden fazla alt öğeyi silmek için bu tekniği updateChildValues ​​ile kullanabilirsiniz.

Dinleyicileri ayırın

ViewController ayrıldığınızda gözlemciler veri senkronizasyonunu otomatik olarak durdurmaz. Bir gözlemci düzgün bir şekilde kaldırılmazsa verileri yerel belleğe senkronize etmeye devam eder. Bir gözlemciye artık ihtiyaç duyulmadığında, ilişkili FIRDatabaseHandle removeObserverWithHandle yöntemine ileterek onu kaldırın.

Bir referansa geri çağırma bloğu eklediğinizde, bir FIRDatabaseHandle döndürülür. Bu tanıtıcılar geri arama bloğunu kaldırmak için kullanılabilir.

Bir veritabanı referansına birden fazla dinleyici eklenmişse, bir olay meydana geldiğinde her dinleyici çağrılır. Bu konumdaki verilerin senkronizasyonunu durdurmak için, bir konumdaki tüm gözlemcileri, removeAllObservers yöntemini çağırarak kaldırmalısınız.

Bir dinleyicide removeObserverWithHandle veya removeAllObservers çağırmak, alt düğümlerinde kayıtlı dinleyicileri otomatik olarak kaldırmaz; Ayrıca bunları kaldırmak için bu referansları veya tanıtıcıları da takip etmelisiniz.

Verileri işlem olarak kaydet

Artan sayaçlar gibi eş zamanlı değişiklikler nedeniyle bozulabilecek verilerle çalışırken işlem işlemini kullanabilirsiniz. Bu işleme iki argüman verirsiniz: bir güncelleme işlevi ve isteğe bağlı bir tamamlama geri çağrısı. Güncelleme işlevi, verinin mevcut durumunu argüman olarak alır ve yazmak istediğiniz yeni durumu döndürür.

Örneğin, örnek sosyal blog uygulamasında, kullanıcıların gönderilere yıldız eklemesine ve yıldızları kaldırmasına ve bir gönderinin kaç yıldız aldığını aşağıdaki şekilde takip etmesine izin verebilirsiniz:

Süratli

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
  if var post = currentData.value as? [String: AnyObject],
    let uid = Auth.auth().currentUser?.uid {
    var stars: [String: Bool]
    stars = post["stars"] as? [String: Bool] ?? [:]
    var starCount = post["starCount"] as? Int ?? 0
    if let _ = stars[uid] {
      // Unstar the post and remove self from stars
      starCount -= 1
      stars.removeValue(forKey: uid)
    } else {
      // Star the post and add self to stars
      starCount += 1
      stars[uid] = true
    }
    post["starCount"] = starCount as AnyObject?
    post["stars"] = stars as AnyObject?

    // Set value and report transaction success
    currentData.value = post

    return TransactionResult.success(withValue: currentData)
  }
  return TransactionResult.success(withValue: currentData)
}) { error, committed, snapshot in
  if let error = error {
    print(error.localizedDescription)
  }
}

Amaç-C

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
[ref runTransactionBlock:^FIRTransactionResult * _Nonnull(FIRMutableData * _Nonnull currentData) {
  NSMutableDictionary *post = currentData.value;
  if (!post || [post isEqual:[NSNull null]]) {
    return [FIRTransactionResult successWithValue:currentData];
  }

  NSMutableDictionary *stars = post[@"stars"];
  if (!stars) {
    stars = [[NSMutableDictionary alloc] initWithCapacity:1];
  }
  NSString *uid = [FIRAuth auth].currentUser.uid;
  int starCount = [post[@"starCount"] intValue];
  if (stars[uid]) {
    // Unstar the post and remove self from stars
    starCount--;
    [stars removeObjectForKey:uid];
  } else {
    // Star the post and add self to stars
    starCount++;
    stars[uid] = @YES;
  }
  post[@"stars"] = stars;
  post[@"starCount"] = @(starCount);

  // Set value and report transaction success
  currentData.value = post;
  return [FIRTransactionResult successWithValue:currentData];
} andCompletionBlock:^(NSError * _Nullable error,
                       BOOL committed,
                       FIRDataSnapshot * _Nullable snapshot) {
  // Transaction completed
  if (error) {
    NSLog(@"%@", error.localizedDescription);
  }
}];

Bir işlemin kullanılması, birden fazla kullanıcının aynı gönderiye aynı anda yıldız vermesi veya istemcinin eski verileri olması durumunda yıldız sayımlarının hatalı olmasını önler. FIRMutableData sınıfının içerdiği değer, başlangıçta istemcinin yol için bilinen son değeridir veya yoksa nil . Sunucu, başlangıç ​​değerini mevcut değeriyle karşılaştırır ve değerler eşleşirse işlemi kabul eder veya reddeder. İşlem reddedilirse, sunucu mevcut değeri istemciye döndürür ve istemci, işlemi güncellenmiş değerle yeniden çalıştırır. Bu, işlem kabul edilene veya çok fazla deneme yapılana kadar tekrarlanır.

Atomik sunucu tarafı artışları

Yukarıdaki kullanım durumunda veritabanına iki değer yazıyoruz: gönderiye yıldız veren/yıldızını kaldıran kullanıcının kimliği ve artan yıldız sayısı. Kullanıcının gönderiye başrol oynadığını zaten biliyorsak, işlem yerine atomik artış işlemini kullanabiliriz.

Süratli

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
let updates = [
  "posts/\(postID)/stars/\(userID)": true,
  "posts/\(postID)/starCount": ServerValue.increment(1),
  "user-posts/\(postID)/stars/\(userID)": true,
  "user-posts/\(postID)/starCount": ServerValue.increment(1)
] as [String : Any]
Database.database().reference().updateChildValues(updates)

Amaç-C

Not: Bu Firebase ürünü, App Clip hedefinde mevcut değildir.
NSDictionary *updates = @{[NSString stringWithFormat: @"posts/%@/stars/%@", postID, userID]: @TRUE,
                        [NSString stringWithFormat: @"posts/%@/starCount", postID]: [FIRServerValue increment:@1],
                        [NSString stringWithFormat: @"user-posts/%@/stars/%@", postID, userID]: @TRUE,
                        [NSString stringWithFormat: @"user-posts/%@/starCount", postID]: [FIRServerValue increment:@1]};
[[[FIRDatabase database] reference] updateChildValues:updates];

Bu kod bir işlem işlemi kullanmadığından, çakışan bir güncelleme olması durumunda otomatik olarak yeniden çalıştırılmaz. Ancak artırma işlemi doğrudan veritabanı sunucusunda gerçekleştiği için çakışma olasılığı yoktur.

Bir kullanıcının daha önce yıldız eklediği bir gönderiye yıldız eklemesi gibi uygulamaya özel çakışmaları tespit etmek ve reddetmek istiyorsanız, o kullanım durumu için özel güvenlik kuralları yazmalısınız.

Verilerle çevrimdışı çalışma

Bir istemcinin ağ bağlantısını kaybederse uygulamanız düzgün çalışmaya devam edecektir.

Firebase veritabanına bağlı her istemci, tüm etkin verilerin kendi dahili sürümünü korur. Veriler yazıldığında önce bu yerel versiyona yazılır. Firebase istemcisi daha sonra bu verileri uzak veritabanı sunucularıyla ve diğer istemcilerle "en iyi çabayı" esas alarak senkronize eder.

Sonuç olarak, veritabanına yapılan tüm yazma işlemleri, sunucuya herhangi bir veri yazılmadan önce yerel olayları hemen tetikler. Bu, uygulamanızın ağ gecikmesine veya bağlantısına bakılmaksızın yanıt vermeye devam edeceği anlamına gelir.

Bağlantı yeniden kurulduğunda uygulamanız uygun olay kümesini alır, böylece istemci herhangi bir özel kod yazmak zorunda kalmadan mevcut sunucu durumuyla senkronize edilir.

Çevrimiçi ve çevrimdışı yetenekler hakkında daha fazla bilgi edinin bölümünde çevrimdışı davranış hakkında daha fazla konuşacağız.

Sonraki adımlar