(İ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ç grubunu tanıtalım: Firebase Local Emulator Suite. 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ü, uygulamanızın emüle edilmiş veritabanı içeriğiniz ve yapılandırmanızın yanı sıra isteğe bağlı olarak emüle edilen proje kaynaklarınızla (işlevler, diğer veritabanları ve güvenlik kuralları) etkileşim kurmasına olanak tanıyan Local Emulator Suite'ın bir parçasıdır.
Realtime Database emülatörünü kullanmanın birkaç adımı vardır:
- Emülatöre bağlanmak için uygulamanızın test yapılandırmasına bir kod satırı ekleyin.
- Yerel proje dizininizin kökünden
firebase emulators:start
çalıştırın. - 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. Ayrıca Local Emulator Suite tanıtımına da göz atmalısınız.
FIRDatabaseReference alma
Veri tabanından veri okumak veya yazmak için FIRDatabaseReference
örneğine ihtiyacınız vardır:
Swift
var ref: DatabaseReference! ref = Database.database().reference()
Objective-C
@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, Database
referansına yazılır ve referansa eşzamansız bir işleyici eklenerek alınır. İşleyici bir kez verilerin ilk durumu için tetiklenir ve veriler her değiştiğinde bir kez daha 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
self.ref.child("users").child(user.uid).setValue(["username": username])
Objective-C
[[[self.ref child:@"users"] child:authResult.user.uid] setValue:@{@"username": username}];
setValue
bu şekilde kullanıldığında, tüm alt düğümler de 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
self.ref.child("users/\(user.uid)/username").setValue(username)
Objective-C
[[[[_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
of observeEventType:withBlock
işlevini kullanın.
Etkinlik türü | Tipik kullanım |
---|---|
FIRDataEventTypeValue |
Bir yolun tüm içeriğindeki değişiklikleri okuma ve dinleme. |
Etkinlik sırasında mevcut olduğu için, belirli bir yoldaki verileri 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ının ayrıntılarını aldığı gösterilmektedir:
Swift
refHandle = postRef.observe(DataEventType.value, with: { snapshot in // ... })
Objective-C
_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
değeri 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, verilerde yapılan güncellemelerle ilgili arka uçtan bildirim almak amacıyla verileri okumak için yukarıda açıklanan değer etkinlikleri tekniklerini kullanmanız gerekir. Bu teknikler, kullanımınızı ve faturalandırmanızı azaltır, kullanıcılarınıza hem internete bağlıyken hem değilken 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()
işlevini kullanabilirsiniz. getData()
, herhangi bir nedenle sunucu değerini döndüremezse istemci, yerel depolama önbelleğini kontrol eder 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
do { let snapshot = try await ref.child("users/\(uid)/username").getData() let userName = snapshot.value as? String ?? "Unknown" } catch { print(error) }
Objective-C
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 beklenmeyen veya etkin dinleme gerektirmesi beklenmeyen veriler için faydalı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
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
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 düğümlerine aynı anda yazmak için updateChildValues
yöntemini kullanın.
updateChildValues
çağrısı yapılırken, anahtarın yolunu belirterek alt düzey alt değerleri 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ı şuna benzer bir kod kullanır:
Swift
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
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 aynı anda anahtar 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
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
[[[_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 referans vererek 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 ayır
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 çağırma 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 kaydet
Artımlı sayaçlar gibi eşzamanlı değişiklikler nedeniyle bozulabilen verilerle çalışırken işlem işlemi kullanabilirsiniz. Bu işleme bir güncelleme işlevi ve isteğe bağlı bir tamamlama geri çağırması olmak üzere iki bağımsız değişken verirsiniz. 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ı uygulamasında, kullanıcıların yayınlara yıldız eklemesine ve yıldızlarını kaldırmasına ve bir yayının kaç yıldız aldığını aşağıdaki şekilde takip etmesine olanak tanıyabilirsiniz:
Swift
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
[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şlem kullanmak, birden çok kullanıcının aynı yayına aynı anda yıldız göstermesi veya istemcinin eski verileri varsa yıldız sayısının yanlış olmasını önler. FIRMutableData
sınıfında yer alan değer, başlangıçta istemcinin yol için bilinen son değeridir. Aksi takdirde 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. Bu işlem, işlem kabul edilene veya çok fazla deneme yapılana kadar tekrar eder.
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
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
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ız eklediği bir yayına yıldız eklemesi gibi uygulamaya özgü çakışmaları tespit etmek ve 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 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ısından etkilenmeden yanıt vermeye devam eder.
Bağlantı yeniden kurulduktan sonra uygulamanız, uygun etkinlik grubunu alır. Böylece, istemci herhangi bir özel kod yazmak zorunda kalmadan mevcut sunucu durumuyla senkronize edilir.
Online ve çevrimdışı özellikler hakkında daha fazla bilgi başlıklı makalede çevrimdışı davranış hakkında daha fazla bilgi vereceğiz.