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'den nasıl veri okuduğu ve yazdığı hakkında konuşmadan önce, Realtime Database işlevini prototip haline getirmek ve test etmek için kullanabileceğiniz bir araç grubu olan Firebase Local Emulator Suite'den bahsedelim. Farklı veri modellerini denemek, güvenlik kurallarınızı optimize etmek veya arka uçla etkileşim kurmanın en uygun maliyetli yolunu bulmak için çalışıyorsanız canlı hizmetler dağıtmadan yerel olarak çalışabilmek iyi bir fikir olabilir.

Realtime Database emülatörü, Local Emulator Suite'un bir parçasıdır. Bu emülatör, uygulamanızın emülasyonlu veritabanı içeriğiniz ve yapılandırmanızın yanı sıra isteğe bağlı olarak emülasyonlu 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ü kullanmanın birkaç adımı vardır:

  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 çalıştırın.
  3. Uygulamanızın prototip kodundan her zamanki gibi bir Realtime Database platform SDK'sı veya Realtime Database REST API'si kullanarak çağrı yapma.

Realtime Database ve Cloud Functions ile ilgili ayrıntılı bir adım adım açıklamalı kılavuz mevcuttur. Local Emulator Suite girişine de göz atmanız önerilir.

FIRDatabaseReference alma

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

Swift

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

ref = Database.database().reference()

Objective-C

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

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

Veri yazma

Bu dokümanda, Firebase verilerini okuma ve yazmayla ilgili temel bilgiler ele alınmaktadır.

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

Temel yazma işlemleri

Temel yazma işlemleri için, verileri belirtilen bir referansa kaydetmek ve bu yoldaki mevcut verileri değiştirmek üzere setValue'ü kullanabilirsiniz. Bu yöntemi kullanarak şunları yapabilirsiniz:

  • Kullanılabilir 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:

Swift

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

Objective-C

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

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

Swift

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

Objective-C

Not: Bu Firebase ürünü, uygulama klipsi 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 FIRDataEventTypeValue etkinliklerini gözlemlemek üzere FIRDatabaseReference observeEventType:withBlock öğesini kullanın.

Etkinlik türü Tipik kullanım
FIRDataEventTypeValue Bir yolun tüm içeriklerindeki değişiklikleri okuyup dinleyebilirsiniz.

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

Aşağıdaki örnekte, bir sosyal blog uygulamasının veritabanından bir yayınla ilgili ayrıntıları aldığı gösterilmektedir:

Swift

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

Objective-C

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

Dinleyici, value mülkünde etkinlik sırası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 olur.

Verileri bir kez okuma

getData() işlevini kullanarak bir kez okuma

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

Genel olarak, arka uçtaki verilerde yapılan güncellemelerden haberdar olmak için verileri okumak üzere yukarıda açıklanan değer etkinliği tekniklerini kullanmanız gerekir. Bu teknikler, kullanımınızı ve faturalandırmanızı azaltır ve kullanıcılarınıza internete bağlanırken ve internete bağlanmazken en iyi deneyimi sunmak için optimize edilir.

Verilere yalnızca bir kez ihtiyacınız varsa veritabanındaki verilerin anlık görüntüsünü almak için getData() işlevini kullanabilirsiniz. getData() herhangi bir nedenle sunucu değerini döndüremiyorsa istemci yerel depolama önbelleğini araştırır 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 seferde nasıl alınacağı gösterilmektedir:

Swift

Not: Bu Firebase ürünü, uygulama klipsi 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ü, uygulama klipsi 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()'ün gereksiz kullanımı bant genişliği kullanımını artırabilir ve performans kaybına neden olabilir. Bu durum, yukarıda gösterildiği gibi gerçek zamanlı bir dinleyici kullanılarak önlenebilir.

Verileri gözlemciyle bir kez okuma

Bazı durumlarda, sunucuda güncel bir değer olup olmadığını kontrol etmek yerine yerel önbelleğe ait değerin hemen döndürülmesini isteyebilirsiniz. Bu 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 veya etkin 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 kullanıcının profilini yüklemek için bu yöntemi kullanır:

Swift

Not: Bu Firebase ürünü, uygulama klipsi 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ü, uygulama klipsi 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

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

updateChildValues çağrısı yaparken anahtar için bir yol belirterek alt düzeydeki alt öğe değerlerini güncelleyebilirsiniz. Veriler daha iyi ölçeklendirme için birden fazla konumda depolanıyorsa veri dağıtımını kullanarak bu verilerin tüm örneklerini güncelleyebilirsiniz. Örneğin, sosyal blog uygulaması bir yayın oluşturmak ve aynı anda yayını son etkinlik feed'inde ve yayını yapan kullanıcının etkinlik feed'inde güncellemek isteyebilir. Bunun için blog uygulaması aşağıdaki gibi bir kod kullanır:

Swift

Not: Bu Firebase ürünü, uygulama klipsi 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ü, uygulama klipsi 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, /posts/$postid'daki tüm kullanıcıların yayınlarını içeren bir yayın oluşturmak için childByAutoId kullanılır ve getKey() ile anahtar aynı anda alınır. Anahtar daha sonra /user-posts/$userid/$postid adresindeki kullanıcının yayınlarında ikinci bir giriş oluşturmak için kullanılabilir.

Bu yolları kullanarak, updateChildValues çağrısını tek seferde yaparak JSON ağacındaki birden fazla konumda eşzamanlı güncellemeler yapabilirsiniz. Bu örnekte, yeni yayın her iki konumda da bu şekilde 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 bağlandığını bilmek istiyorsanız tamamlama bloğu ekleyebilirsiniz. Hem setValue hem de updateChildValues, yazma işlemi veritabanına bağlandığında ç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 nedenini belirten bir hata nesnesi iletilir.

Swift

Not: Bu Firebase ürünü, uygulama klipsi 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ü, uygulama klipsi 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, söz konusu verilerin konumuna ait bir referans üzerinde removeValue işlevini ç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 kaldırma

Gözlemciler, bir ViewController'den ayrıldığınızda verileri otomatik olarak senkronize etmeyi durdurmaz. Doğru şekilde kaldırılmayan gözlemciler, verileri yerel bellekle senkronize etmeye devam eder. Artık ihtiyaç duyulmayan gözlemcileri, 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 herkese açık kullanıcı adları, geri arama engellemesini kaldırmak için kullanılabilir.

Bir veritabanı referansına birden fazla dinleyici eklendiyse bir etkinlik tetiklendiğinde 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ı yapıldığında, dinleyicinin alt düğümlerine kayıtlı dinleyiciler otomatik olarak kaldırılmaz. Bu referansları veya herkese açık kimlikleri kaldırmanız da gerekir.

Verileri işlem olarak kaydetme

Artımlı sayaçlar gibi eşzamanlı değişiklikler nedeniyle bozulabilen verilerle çalışırken işlem işlemi kullanabilirsiniz. Bu işleme iki bağımsız değişken gönderirsiniz: bir güncelleme işlevi ve isteğe bağlı bir tamamlama geri çağırma işlevi. 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 yayınlara yıldız eklemesine ve yıldızları kaldırmasına izin verebilir ve bir yayının aldığı yıldız sayısını aşağıdaki gibi takip edebilirsiniz:

Swift

Not: Bu Firebase ürünü, uygulama klipsi 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ü, uygulama klipsi 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ı yayına yıldız atarsa 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 böyle bir değer 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 veya reddeder. İşlem reddedilirse sunucu mevcut değeri istemciye döndürür. İstemci de güncellenmiş değerle işlemi tekrar çalıştırır. İşlem kabul edilene veya çok fazla deneme yapılana kadar bu işlem 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/yıldız işaretini kaldıran kullanıcının kimliği ve artan yıldız sayısı. Kullanıcının yayını favorilediğini zaten biliyorsak işlem yerine atomik artış işlemi kullanabiliriz.

Swift

Not: Bu Firebase ürünü, uygulama klipsi 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ü, uygulama klipsi 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 kod bir işlem işlemi kullanmadığından, çakışan bir güncelleme varsa otomatik olarak yeniden çalıştırılmaz. Ancak artma işlemi doğrudan veritabanı sunucusunda gerçekleştiği için çakışma olasılığı yoktur.

Kullanıcının daha önce yıldızla işaretlediği bir yayını yıldızla işaretlemesi gibi uygulamaya özgü çakışmaların tespit edilip reddedilmesini 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 düzgün çalışmaya devam eder.

Bir Firebase veritabanına bağlı her istemci, etkin verilerin kendi dahili sürümünü korur. Veriler ilk olarak 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" temelinde senkronize eder.

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

Bağlantı yeniden kurulduktan sonra uygulamanız, özel kod yazmak zorunda kalmadan istemcinin mevcut sunucu durumuyla senkronize edilmesi için uygun etkinlik grubunu alır.

Online ve çevrimdışı özellikler hakkında daha fazla bilgi başlıklı makalede çevrimdışı davranış hakkında daha fazla bilgi vereceğiz.

Sonraki adımlar