Apple platformlarında Veri Okuma ve Yazma

(İsteğe bağlı) Firebase Local Emulator Suite ile prototip oluşturma ve test etme

Uygulamanızın Realtime Database ile nasıl okuma ve yazma işlemi yaptığından bahsetmeden önce, Realtime Database işlevselliğini prototip oluşturmak ve test etmek için kullanabileceğiniz bir dizi aracı tanıtalım: Firebase Local Emulator Suite. Farklı veri modellerini 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.

Realtime Database emülatörü, Local Emulator Suite'nin bir parçasıdır. Bu araç, uygulamanızın emüle edilmiş veritabanı içeriğiniz ve yapılandırmanızla, ayrıca isteğe bağlı olarak emüle edilmiş proje kaynaklarınızla (işlevler, diğer veritabanları ve güvenlik kuralları) etkileşim kurmasını sağlar.

Realtime Database emülatörünü kullanmak için yalnızca birkaç adım gerekir:

  1. Emülatöre bağlanmak için uygulamanızın test yapılandırmasına bir kod satırı ekleyin.
  2. Yerel proje dizininizin kökünden firebase emulators:start komutunu çalıştırın.
  3. Uygulamanızın prototip kodundan Realtime Database platform SDK'sını her zamanki gibi kullanarak veya Realtime Database REST API'yi kullanarak çağrı yapma.

Realtime Database ve Cloud Functions ile ilgili ayrıntılı birrehber mevcuttur. Ayrıca Local Emulator Suite giriş bölümüne de göz atmanız gerekir.

FIRDatabaseReference alma

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

Swift

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
var ref: DatabaseReference!

ref = Database.database().reference()

Objective-C

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
@property (strong, nonatomic) FIRDatabaseReference *ref;

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

Veri yazma

Bu belgede, Firebase verilerini okuma ve yazma ile ilgili temel bilgiler verilmektedir.

Firebase verileri bir Database referansına yazılır ve referansa eşzamansız bir dinleyici eklenerek alınır. Dinleyici, verilerin ilk durumu için bir kez, veriler her değiştiğinde ise tekrar tetiklenir.

Temel yazma işlemleri

Temel yazma işlemleri için setValue kullanarak verileri belirtilen bir referansa kaydedebilir ve bu yoldaki mevcut verilerin yerini alabilirsiniz. Bu yöntemi kullanarak şunları yapabilirsiniz:

  • Kullanılabilir JSON türlerine karşılık gelen kart türleri aşağıdaki gibidir:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

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

Swift

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
self.ref.child("users").child(user.uid).setValue(["username": username])

Objective-C

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

setValue öğesini bu şekilde kullanmak, belirtilen konumdaki verilerin (alt düğümler dahil) üzerine yazar. Ancak yine de tüm nesneyi yeniden yazmadan bir çocuğu güncelleyebilirsiniz. Kullanıcıların profillerini güncellemesine izin vermek istiyorsanız kullanıcı adını aşağıdaki gibi güncelleyebilirsiniz:

Swift

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
self.ref.child("users/\(user.uid)/username").setValue(username)

Objective-C

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

Verileri okuma

Değer etkinliklerini dinleyerek verileri okuma

Bir yoldaki verileri okumak ve değişiklikleri dinlemek için observeEventType:withBlock FIRDatabaseReference işlevini kullanarak FIRDataEventTypeValue etkinliklerini gözlemleyin.

Etkinlik türü Tipik kullanım
FIRDataEventTypeValue Bir yolun tüm içeriğindeki değişiklikleri okuma ve dinleme

Belirli bir yoldaki verileri, etkinlik sırasında olduğu şekliyle okumak için FIRDataEventTypeValue etkinliğini kullanabilirsiniz. Bu yöntem, dinleyici eklendiğinde bir kez, veriler (alt öğeler dahil) her değiştiğinde ise tekrar tetiklenir. Etkinlik geri çağırmasına, alt veriler de dahil olmak üzere söz konusu konumdaki tüm verileri içeren bir snapshot iletilir. Veri yoksa anlık görüntü, exists() özelliğini çağırdığınızda false, value özelliğini okuduğunuzda ise nil değerini döndürür.

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

Swift

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
refHandle = postRef.observe(DataEventType.value, with: { snapshot in
  // ...
})

Objective-C

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

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

Verileri bir kez okuma

getData() kullanarak bir kez okuma

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

Genel olarak, arka uçtaki verilerde yapılan güncellemelerden haberdar olmak için yukarıda açıklanan değer etkinlikleri tekniklerini kullanarak veri okumanız gerekir. Bu teknikler, kullanımınızı ve faturalandırmanızı azaltır. Ayrıca, kullanıcılarınıza hem online hem de offline olarak 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. getData() herhangi bir nedenle sunucu değerini döndüremiyorsa istemci, yerel depolama önbelleğini yoklar ve değer hâlâ bulunamazsa bir hata döndürür.

Aşağıdaki örnekte, bir kullanıcının herkese açık kullanıcı adının veritabanından tek bir kez nasıl alındığı gösterilmektedir:

Swift

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

Objective-C

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
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() öğesinin gereksiz kullanımı, bant genişliği kullanımını artırabilir ve performans kaybına yol açabilir. Bu durum, yukarıda gösterildiği gibi gerçek zamanlı bir dinleyici kullanılarak önlenebilir.

Verileri bir gözlemciyle bir kez okuma

Bazı durumlarda, sunucuda güncellenmiş 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 ya da aktif dinleme gerektirmesi beklenmeyen veriler için kullanışlıdır. Örneğin, önceki örneklerdeki blog uygulaması, kullanıcı yeni bir gönderi yazmaya başladığında profilini yüklemek için bu yöntemi kullanır:

Swift

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
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)
}

Objective-C

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
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üncelleme

Bir düğümün belirli alt öğelerine diğer alt düğümleri üzerine yazmadan aynı anda yazmak için updateChildValues yöntemini kullanın.

updateChildValues işlevini çağırırken anahtar için bir yol belirterek alt düzeydeki alt değerleri güncelleyebilirsiniz. Veriler daha iyi ölçeklendirme için birden fazla konumda depolanıyorsa veri dağıtımı kullanarak bu verilerin tüm örneklerini güncelleyebilirsiniz. Örneğin, bir sosyal blog uygulaması bir gönderi oluşturmak ve aynı anda hem son etkinlik feed'ini hem de gönderiyi yayınlayan kullanıcının etkinlik feed'ini güncellemek isteyebilir. Bunu yapmak için blog uygulaması şu gibi bir kod kullanır:

Swift

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
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)

Objective-C

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
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 örnekte, childByAutoId kullanılarak /posts/$postid adresindeki tüm kullanıcıların yayınlarını içeren düğümde bir yayın oluşturuluyor ve aynı anda getKey() ile anahtar alınıyor. Ardından anahtar, kullanıcının /user-posts/$userid/$postid adresindeki gönderilerinde ikinci bir giriş oluşturmak için kullanılabilir.

Bu yolları kullanarak, updateChildValues için tek bir çağrıyla JSON ağacındaki birden fazla konumda eşzamanlı güncellemeler yapabilirsiniz. Örneğin, bu örnekte yeni gönderi her iki konumda da oluşturulur. Bu şekilde yapılan eşzamanlı güncellemeler atomiktir: Tüm güncellemeler başarılı olur veya tüm güncellemeler başarısız olur.

Tamamlama bloğu ekleme

Verilerinizin ne zaman işlendiğini öğrenmek istiyorsanız bir tamamlama bloğu ekleyebilirsiniz. Hem setValue hem de updateChildValues, yazma işlemi veritabanına kaydedildiğinde çağrılan isteğe bağlı bir tamamlama bloğu alır. Bu dinleyici, hangi verilerin kaydedildiğini ve hangi verilerin hâlâ 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.

Swift

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

Objective-C

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
[[[_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 silin

Verileri silmenin en basit yolu, verilerin konumuna yapılan bir referansta removeValue işlevini çağırmaktır.

Ayrıca nil veya updateChildValues gibi başka bir yazma işlemi için değer olarak setValue belirterek de silebilirsiniz. Bu tekniği updateChildValues ile birlikte kullanarak tek bir API çağrısında birden fazla alt öğeyi silebilirsiniz.

Dinleyicileri ayırma

Gözlemciler, ViewController'dan ayrıldığınızda verileri otomatik olarak senkronize etmeyi durdurmaz. Bir gözlemci düzgün şekilde kaldırılmazsa verileri yerel belleğe senkronize etmeye devam eder. Artık ihtiyaç duyulmayan bir gözlemciyi, ilişkili FIRDatabaseHandle öğesini removeObserverWithHandle yöntemine ileterek kaldırın.

Bir referansa geri arama bloğu eklediğinizde FIRDatabaseHandle döndürülür. Bu tutma yerleri, geri arama engelini kaldırmak için kullanılabilir.

Bir veritabanı referansına birden fazla dinleyici eklenmişse bir etkinlik oluşturulduğunda her dinleyici çağrılır. Bu konumdaki verilerin senkronizasyonunu durdurmak için removeAllObservers yöntemini çağırarak konumdaki tüm gözlemcileri kaldırmanız gerekir.

Bir dinleyicide removeObserverWithHandle veya removeAllObservers çağrısı yapmak, alt düğümlerine kayıtlı dinleyicileri otomatik olarak kaldırmaz. Bu dinleyicileri kaldırmak için bu referansları veya tutamaçları da takip etmeniz gerekir.

Verileri işlem olarak kaydetme

Eşzamanlı değişiklikler nedeniyle bozulabilecek verilerle (ör. artımlı sayaçlar) çalışırken işlem işlemi kullanabilirsiniz. Bu işleme iki bağımsız değişken iletirsiniz: bir güncelleme işlevi ve isteğe bağlı bir tamamlanma geri çağırması. Güncelleme işlevi, verilerin mevcut durumunu bağımsız değişken 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 izin verebilir, ayrıca bir gönderinin kaç yıldız aldığını aşağıdaki gibi takip edebilirsiniz:

Swift

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
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)
  }
}

Objective-C

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
[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);
  }
}];

İşlem kullanmak, birden fazla kullanıcı aynı anda aynı gönderiye yıldız verirse veya istemcide eski veriler varsa yıldız sayılarının yanlış olmasını önler. FIRMutableData sınıfında bulunan değer, başlangıçta istemcinin yol için bilinen son değeridir veya yoksa nil olur. Sunucu, ilk değeri mevcut değeriyle karşılaştırır ve değerler eşleşirse işlemi kabul eder, eşleşmezse reddeder. İşlem reddedilirse sunucu, mevcut değeri istemciye döndürür. İstemci, işlemi güncellenmiş değerle tekrar çalıştırır. Bu işlem, işlem kabul edilene veya çok fazla deneme yapılana kadar tekrarlanır.

Atomik sunucu tarafı artışları

Yukarıdaki kullanım alanında, veritabanına iki değer yazıyoruz: gönderiye yıldız ekleyen/kaldıran kullanıcının kimliği ve artırılmış yıldız sayısı. Kullanıcının yayını yıldızladığını zaten biliyorsak işlem yerine atomik artırma işlemi kullanabiliriz.

Swift

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
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)

Objective-C

Not: Bu Firebase ürünü, App Clip hedefinde kullanılamaz.
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 kodda işlem işlemi kullanılmadığı için çakışan bir güncelleme olduğunda 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.

Uygulamaya özgü çakışmaları (ör. kullanıcının daha önce yıldızladığı bir gönderiyi tekrar yıldızlaması) tespit edip reddetmek istiyorsanız bu kullanım alanı için özel güvenlik kuralları yazmanız gerekir.

Verilerle çevrimdışı çalışma

Bir istemcinin ağ bağlantısı kesilirse uygulamanız doğru şekilde çalışmaya devam eder.

Firebase veritabanına bağlı her istemci, etkin verilerin kendi dahili sürümünü korur. Veriler yazılırken önce bu yerel sürüme yazılır. Ardından Firebase istemcisi, bu verileri uzak veritabanı sunucularıyla ve diğer istemcilerle "en iyi çaba" prensibine göre senkronize eder.

Sonuç olarak, veritabanına yapılan tüm yazma işlemleri, sunucuya herhangi bir veri yazılmadan önce yerel etkinlikleri anında tetikler. Bu sayede uygulamanız, ağ gecikmesinden veya bağlantısından bağımsız olarak yanıt vermeye devam eder.

Bağlantı yeniden kurulduğunda uygulamanız, istemcinin mevcut sunucu durumuyla senkronize olması için uygun etkinlik kümesini alır. Bu işlem için özel kod yazmanız gerekmez.

Çevrimdışı davranış hakkında daha fazla bilgiyi Online ve çevrimdışı özellikler hakkında daha fazla bilgi başlıklı makalede bulabilirsiniz.

Sonraki adımlar