(İsteğe bağlı) Firebase Local Emulator Suite ile prototip oluşturun ve test edin
Uygulamanızın Realtime Database'den nasıl okuyup yazdığından bahsetmeden önce, Realtime Database işlevselliğini prototiplemek ve test etmek için kullanabileceğiniz bir dizi araç tanıtalım: Firebase Local Emulator Suite. Farklı veri modelleri 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ış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ü Paketi'nin bir parçasıdır.
Gerçek Zamanlı Veritabanı öykünücüsünü kullanmak yalnızca birkaç adımı içerir:
- Öykünücüye bağlanmak için uygulamanızın test yapılandırmasına bir kod satırı ekleme.
- Yerel proje dizininizin kökünden,
firebase emulators:start
. - Her zamanki gibi bir Realtime Database platformu SDK'sını veya Realtime Database REST API'sini kullanarak uygulamanızın prototip kodundan çağrı yapma.
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.
Bir FIRDatabaseReference alın
Veritabanından veri okumak veya yazmak için bir FIRDatabaseReference
örneğine ihtiyacınız vardır:
Süratli
var ref: DatabaseReference! ref = Database.database().reference()
Amaç-C
@property (strong, nonatomic) FIRDatabaseReference *ref; self.ref = [[FIRDatabase database] reference];
Veri yaz
Bu belge, Firebase verilerini okuma ve yazmayla ilgili temel bilgileri kapsar.
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 değiştiğinde yeniden tetiklenir.
Temel yazma işlemleri
Temel yazma işlemleri için, setValue
kullanarak verileri belirtilen bir referansa kaydedip, o yoldaki mevcut tüm verileri değiştirebilirsiniz. Bu yöntemi aşağıdakiler için kullanabilirsiniz:
- Kullanılabilir JSON türlerine karşılık gelen türleri aşağıdaki gibi geçirin:
-
NSString
-
NSNumber
-
NSDictionary
-
NSArray
-
Örneğin, setValue
ile bir kullanıcıyı aşağıdaki gibi ekleyebilirsiniz:
Süratli
self.ref.child("users").child(user.uid).setValue(["username": username])
Amaç-C
[[[self.ref child:@"users"] child:authResult.user.uid] setValue:@{@"username": username}];
setValue
bu şekilde kullanmak, alt düğümler de dahil olmak üzere belirtilen konumdaki verilerin üzerine yazar. Ancak, tüm nesneyi 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:
Süratli
self.ref.child("users/\(user.uid)/username").setValue(username)
Amaç-C
[[[[_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 için observeEventType:withBlock
FIRDatabaseReference
kullanın.
Etkinlik tipi | Tipik kullanım |
---|---|
FIRDataEventTypeValue | Bir yolun tüm içeriğindeki değişiklikleri okuyun ve dinleyin. |
FIRDataEventTypeValue
olayını, olay sırasında var olduğu gibi, belirli bir yoldaki verileri okumak için kullanabilirsiniz. Bu yöntem, dinleyici eklendiğinde bir kez tetiklenir ve herhangi bir çocuk da dahil olmak üzere veriler her değiştiğinde tekrar tetiklenir. Olay geri araması, alt veriler de dahil olmak üzere o konumdaki tüm verileri içeren bir snapshot
iletilir. Veri yoksa, exists()
işlevini çağırdığınızda anlık görüntü false
ve value
özelliğini okuduğunuzda nil
değerini döndürür.
Aşağıdaki örnek, veri tabanından bir gönderinin ayrıntılarını alan bir sosyal blog uygulamasını göstermektedir:
Süratli
refHandle = postRef.observe(DataEventType.value, with: { snapshot in // ... })
Amaç-C
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { NSDictionary *postDict = snapshot.value; // ... }];
Dinleyici, value
özelliğinde olay 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
.
Verileri bir kez oku
getData() kullanarak bir kez okuyun
SDK, uygulamanız ister çevrimiçi ister çevrimdışı olsun, veritabanı sunucularıyla etkileşimleri yönetmek için tasarlanmıştır.
Genel olarak, arka uçtan verilerde yapılan güncellemelerden haberdar olmak için verileri okumak için yukarıda açıklanan değer olayları 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ışı olduklarında en iyi deneyimi sunmak için optimize edilmiştir.
Verilere yalnızca bir kez ihtiyacınız varsa, veri tabanından 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ırır ve değer hala bulunamazsa bir hata döndürür.
Aşağıdaki örnek, bir kullanıcının herkese açık kullanıcı adının veritabanından tek seferde alınmasını gösterir:
Süratli
ref.child("users/\(uid)/username").getData(completion: { error, snapshot in guard error == nil else { print(error!.localizedDescription) return; } let userName = snapshot.value as? String ?? "Unknown"; });
Amaç-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ın gereksiz kullanımı, bant genişliği kullanımını artırabilir ve yukarıda gösterildiği gibi gerçek zamanlı bir dinleyici kullanılarak önlenebilecek performans kaybına yol açabilir.
Bir gözlemci ile verileri bir kez okuyun
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 durumlarda, verileri yerel disk önbelleğinden hemen almak için observeSingleEventOfType
kullanabilirsiniz.
Bu, yalnızca bir kez yüklenmesi gereken ve sık değişmesi beklenmeyen veya etkin dinleme gerektirmeyen veriler için kullanışlıdır. Örneğin, önceki örneklerdeki blog uygulaması, bir kullanıcı yeni bir gönderi yazmaya başladığında profilini yüklemek için bu yöntemi kullanır:
Süratli
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
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
, anahtar için bir yol belirterek alt düzey alt değerleri güncelleyebilirsiniz. Daha iyi ölçeklendirmek için veriler 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 onu 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ı şöyle bir kod kullanır:
Süratli
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
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
postid konumundaki tüm kullanıcılar için gönderileri içeren düğümde bir gönderi oluşturmak ve aynı anda anahtarı getKey()
ile almak için childByAutoId
öğesini 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, bu örneğin yeni gönderiyi her iki konumda nasıl oluşturduğu gibi, updateChildValues
tek bir çağrıyla JSON ağacındaki birden çok konumda eşzamanlı güncellemeler gerçekleştirebilirsiniz. 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.
Bir Tamamlama Bloğu Ekle
Verilerinizin ne zaman teslim edildiğini bilmek istiyorsanız, bir tamamlama bloğu ekleyebilirsiniz. Hem setValue
hem de updateChildValues
, yazma işlemi veritabanına yapıldığında çağrılan isteğe bağlı bir tamamlama bloğu alır. Bu dinleyici, hangi verilerin kaydedildiğini ve hangi verilerin hala senkronize edildiğini takip etmek için faydalı olabilir. Çağrı başarısız olursa, dinleyiciye hatanın neden oluştuğunu belirten bir hata nesnesi iletilir.
Süratli
ref.child("users").child(user.uid).setValue(["username": username]) { (error:Error?, ref:DatabaseReference) in if let error = error { print("Data could not be saved: \(error).") } else { print("Data saved successfully!") } }
Amaç-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 sil
Verileri silmenin en basit yolu, o verinin konumuna bir referans üzerinden removeValue
ç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 çok çocuğu silmek için bu tekniği updateChildValues
ile kullanabilirsiniz.
Dinleyicileri ayır
Bir ViewController
ayrıldığınızda, gözlemciler verileri eşitlemeyi otomatik olarak durdurmaz. Bir gözlemci düzgün bir şekilde kaldırılmazsa, verileri yerel belleğe eşitlemeye devam eder. Artık bir gözlemciye ihtiyaç kalmadığında, ilişkili FIRDatabaseHandle
removeObserverWithHandle
yöntemine geçirerek onu kaldırın.
Bir başvuruya bir geri arama bloğu eklediğinizde, bir FIRDatabaseHandle
döndürülür. Bu tutamaçlar, geri arama bloğunu kaldırmak için kullanılabilir.
Bir veritabanı referansına birden çok dinleyici eklendiyse, bir olay ortaya çıktığında her dinleyici çağrılır. Bu konumdaki verilerin eşitlenmesini durdurmak için, removeAllObservers
yöntemini çağırarak bir konumdaki tüm gözlemcileri kaldırmanız gerekir.
Bir dinleyicide removeObserverWithHandle
veya removeAllObservers
öğesinin çağrılması, alt düğümlerinde kayıtlı dinleyicileri otomatik olarak kaldırmaz; bunları kaldırmak için bu referansları veya tutamaçları da takip etmelisiniz.
Verileri işlem olarak kaydedin
Artımlı sayaçlar gibi eşzamanlı değişikliklerle bozulabilecek verilerle çalışırken, bir işlem işlemi 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, verilerin mevcut durumunu bir argüman olarak alır ve yazmak istediğiniz yeni istenen 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 gibi takip etmesine izin verebilirsiniz:
Süratli
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
[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ı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, yoksa nil
. Sunucu, başlangıç değerini mevcut değerle karşılaştırır ve değerler eşleşirse işlemi kabul eder veya reddeder. İşlem reddedilirse, sunucu, işlemi güncellenen değerle yeniden çalıştıran istemciye mevcut değeri döndü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 atan/yıldızı kaldıran kullanıcının kimliği ve artan yıldız sayısı. Kullanıcının gönderiye yıldız eklediğini zaten biliyorsak, işlem yerine atomik bir artış işlemi kullanabiliriz.
Süratli
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
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 kullanmaz, bu nedenle çakışan bir güncelleme varsa 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 eklemiş olduğu bir gönderiye yıldız eklemesi gibi uygulamaya özel çakışmaları algılamak ve reddetmek istiyorsanız, bu kullanım durumu için özel güvenlik kuralları yazmalısınız.
Verilerle çevrimdışı çalışın
Bir istemci ağ bağlantısını kaybederse uygulamanız düzgün şekilde çalışmaya devam eder.
Bir Firebase veritabanına bağlanan her istemci, herhangi bir etkin verinin kendi dahili sürümünü tutar. Veri yazıldığında, ö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 hemen önce yerel olayları tetikler. Bu, uygulamanızın ağ gecikmesinden veya bağlantıdan bağımsız olarak 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 eşitlenir.
Ç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
- Veri listeleriyle çalışma
- Verileri nasıl yapılandıracağınızı öğrenin
- Çevrimiçi ve çevrimdışı yetenekler hakkında daha fazla bilgi edinin