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 üzerinde nasıl okuma ve yazma yaptığından bahsetmeden önce, Realtime Database işlevinin prototipini oluşturmak ve test etmek için kullanabileceğiniz bir dizi araçtan bahsedelim: Firebase Local Emulator Suite. Farklı veri modellerini deniyor, güvenlik kurallarınızı optimize ediyor veya arka uçla etkileşim kurmanın en uygun maliyetli yolunu bulmaya çalışıyorsanız canlı hizmetleri dağıtmadan yerel olarak çalışmak iyi bir fikir olabilir.

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

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

Realtime Database ve Cloud Functions ile ilgili ayrıntılı bir açıklamayı inceleyebilirsiniz. Local Emulator Suite girişine de göz atmanız önerilir.

FIRDatabaseReference alma

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

Swift

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

ref = Database.database().reference()

Objective-C

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

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

Verileri yazma

Bu belgede, Firebase verilerini okuma ve yazmayla ilgili temel bilgiler yer almaktadır.

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 ve veriler her değiştiğinde tekrar tetiklenir.

Temel yazma işlemleri

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

  • Mevcut JSON türlerine karşılık gelen kart türleri şunlardır:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

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

Swift

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

Objective-C

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

setValue yöntemini bu şekilde kullanmak, alt düğümler de dahil olmak üzere belirtilen konumdaki verilerin üzerine yazar. Ancak nesnenin tamamını yeniden yazmadan 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 Klibi hedefinde kullanılamaz.
self.ref.child("users/\(user.uid)/username").setValue(username)

Objective-C

Not: Bu Firebase ürünü, Uygulama Klibi 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 FIRDatabaseReference öğesinin observeEventType:withBlock yöntemini 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 anındaki haliyle okumak için FIRDataEventTypeValue etkinliğini kullanabilirsiniz. Bu yöntem, dinleyici eklendiğinde bir kez ve alt öğeler de dahil olmak üzere veriler her değiştiğinde tekrar tetiklenir. Etkinlik geri çağırma işlevine, alt veriler de dahil olmak üzere ilgili konumdaki tüm verileri içeren bir snapshot iletilir. Veri yoksa anlık görüntü, exists() yöntemini çağırdığınızda false değerini, value özelliğini okuduğunuzda ise nil değerini döndürür.

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

Swift

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

Objective-C

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

Dinleyici, etkinlik sırasında veritabanında belirtilen konumdaki verileri içeren bir FIRDataSnapshot nesnesini value özelliğinde 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 internete bağlı olup olmamasından bağımsız olarak veritabanı sunucularıyla etkileşimleri yönetmek için tasarlanmıştır.

Genellikle, arka uçtan gelen verilerle ilgili güncellemelerden haberdar olmak için verileri okumak üzere yukarıda açıklanan değer etkinlikleri tekniklerini kullanmanız gerekir. Bu teknikler, kullanımınızı ve faturalandırmanızı azaltır ve kullanıcılarınıza çevrimiçi ve çevrimdışı olduklarında en iyi deneyimi sunacak şekilde 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() yöntemini kullanabilirsiniz. getData() herhangi bir nedenle sunucu değerini döndüremezse istemci, yerel depolama önbelleğini inceler ve değer hâlâ bulunamazsa 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 Klibi 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 Klibi 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() yönteminin 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.

Gözlemci ile verileri 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 veya etkin dinleme gerektirmesi beklenmeyen veriler için kullanışlıdır. Örneğin, önceki örneklerdeki blog uygulaması, kullanıcı yeni bir yayın 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 Klibi 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 Klibi 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 yöntemini çağırırken anahtar için bir yol belirterek alt düzey alt değerleri güncelleyebilirsiniz. Veriler daha iyi ölçeklendirme için birden fazla konumda depolanıyorsa veri yayma yöntemini kullanarak bu verilerin tüm örneklerini güncelleyebilirsiniz. Örneğin, bir sosyal blog uygulaması bir yayın oluşturmak ve aynı anda bu yayını son etkinlik feed'ine ve yayını yapan kullanıcının etkinlik feed'ine güncellemek isteyebilir. Bunun için blog uygulaması şu şekilde bir kod kullanır:

Swift

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

Bu yolları kullanarak, JSON ağacındaki birden fazla konumda aynı anda güncelleme yapmak için updateChildValues yöntemini tek bir kez çağırabilirsiniz. Bu örnekte, yeni yayın her iki konumda da oluşturulmaktadır. Bu şekilde yapılan eşzamanlı güncellemeler atomiktir: Güncellemelerin ya hepsi başarılı olur ya da hepsi başarısız olur.

Tamamlama bloğu ekleme

Verilerinizin ne zaman kaydedildiğini öğrenmek istiyorsanız bir tamamlama bloğu ekleyebilirsiniz. Hem setValue hem de updateChildValues, yazma işlemi veritabanına uygulandığı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 Klibi 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 Klibi 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 kolay yolu, verilerin konumuna referans vererek removeValue yöntemini çağırmaktır.

setValue veya updateChildValues gibi başka bir yazma işlemi için değer 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ırma

ViewController oturumundan ayrıldığınızda gözlemciler veri senkronizasyonunu otomatik olarak durdurmaz. Düzgün şekilde kaldırılmayan gözlemciler, verileri yerel bellekle senkronize etmeye devam eder. Bir gözlemciye artık ihtiyaç duyulmadığında, ilgili FIRDatabaseHandle öğesini removeObserverWithHandle yöntemine ileterek gözlemciyi kaldırın.

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

Bir veritabanı referansına birden fazla dinleyici eklenirse bir etkinlik tetiklendiğinde her dinleyici çağrılır. Bir konumda veri senkronizasyonunu durdurmak için removeAllObservers yöntemini çağırarak o konumdaki tüm gözlemcileri kaldırmanız gerekir.

Bir dinleyicide removeObserverWithHandle veya removeAllObservers yöntemini çağırmak, alt düğümlerinde kayıtlı dinleyicileri otomatik olarak kaldırmaz. Bu referansları veya tanıtıcıları da takip ederek kaldırmanız gerekir.

Verileri işlem olarak kaydetme

Artımlı sayaçlar gibi eşzamanlı değişiklikler nedeniyle bozulabilecek verilerle çalışırken işlem operasyonu kullanabilirsiniz. Bu işleme iki bağımsız değişken verirsiniz: 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, sosyal blog uygulaması örneğinde kullanıcıların yayınlara yıldız ekleyip kaldırmasına izin verebilir ve bir yayının kaç yıldız aldığını aşağıdaki gibi takip edebilirsiniz:

Swift

Not: Bu Firebase ürünü, Uygulama Klibi 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 Klibi 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 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 böyle bir değer yoksa nil değeridir. 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 de işlemi güncellenmiş değerle tekrar çalıştırır. Bu işlem, ödeme 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: yayına yıldız ekleyen/yıldızı kaldıran kullanıcının kimliği ve artırılan yıldız sayısı. Kullanıcının yayına yıldız eklediğini zaten biliyorsak işlem yerine atomik artırma işlemi kullanabiliriz.

Swift

Not: Bu Firebase ürünü, Uygulama Klibi 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 Klibi 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 operasyonu kullanılmadığından, ç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.

Kullanıcının daha önce yıldız eklediği bir yayına tekrar yıldız eklemesi gibi uygulamaya özel çakışmaları tespit edip reddetmek istiyorsanız bu kullanım alanı için özel güvenlik kuralları yazmanız gerekir.

Verilerle çevrimdışı çalışma

İstemcinin ağ bağlantısı kesilirse uygulamanız düzgün şekilde çalışmaya devam eder.

Firebase veritabanına bağlı her istemci, etkin verilerin kendi dahili sürümünü tutar. Veriler yazılırken önce bu yerel sürüme yazılır. Firebase istemcisi daha sonra bu verileri, uzak veritabanı sunucuları ve diğer istemcilerle "en iyi çaba" temelinde senkronize eder.

Sonuç olarak, veritabanına yapılan tüm yazma işlemleri, sunucuya herhangi bir veri yazılmadan önce yerel etkinlikleri hemen tetikler. Bu sayede uygulamanız, ağ gecikmesi veya bağlantı durumundan bağımsız olarak her zaman yanıt verir.

Bağlantı yeniden kurulduğunda uygulamanız uygun etkinlik grubunu alır. Böylece istemci, herhangi bir özel kod yazmaya gerek kalmadan mevcut sunucu durumuyla senkronize olur.

Çevrimdışı davranış hakkında daha fazla bilgiyi Online ve çevrimdışı özellikler hakkında daha fazla bilgi bölümünde bulabilirsiniz.

Sonraki adımlar