(ज़रूरी नहीं) Firebase Local Emulator Suite की मदद से प्रोटोटाइप बनाना और जांच करना
आपका ऐप्लिकेशन Realtime Database से कैसे पढ़ता है और उसमें कैसे लिखता है, इस बारे में बात करने से पहले, Realtime Database की सुविधाओं के प्रोटोटाइप बनाने और उनकी जांच करने के लिए इस्तेमाल किए जा सकने वाले टूल के बारे में बताते हैं: Firebase Local Emulator Suite. अगर आपको अलग-अलग डेटा मॉडल आज़माने हैं, सुरक्षा नियमों को ऑप्टिमाइज़ करना है या बैक-एंड के साथ इंटरैक्ट करने का सबसे किफ़ायती तरीका ढूंढना है, तो लाइव सेवाओं को डिप्लॉय किए बिना स्थानीय तौर पर काम करना एक अच्छा आइडिया हो सकता है.
Realtime Database एमुलेटर, Local Emulator Suite का हिस्सा होता है. इससे आपके ऐप्लिकेशन को, एमुलेट किए गए डेटाबेस के कॉन्टेंट और कॉन्फ़िगरेशन के साथ-साथ, एमुलेट किए गए प्रोजेक्ट के रिसॉर्स (फ़ंक्शन, अन्य डेटाबेस, और सुरक्षा नियम) के साथ इंटरैक्ट करने में मदद मिलती है.
Realtime Database एमुलेटर का इस्तेमाल करने के लिए, ये चरण पूरे करें:
- एम्युलेटर से कनेक्ट करने के लिए, अपने ऐप्लिकेशन के टेस्ट कॉन्फ़िगरेशन में कोड की एक लाइन जोड़ना.
- अपनी लोकल प्रोजेक्ट डायरेक्ट्री के रूट से,
firebase emulators:start
चलाएं. - अपने ऐप्लिकेशन के प्रोटोटाइप कोड से कॉल करना. इसके लिए, Realtime Database प्लैटफ़ॉर्म SDK का इस्तेमाल हमेशा की तरह करें या Realtime Database REST API का इस्तेमाल करें.
Realtime Database और Cloud Functions से जुड़ा ज़्यादा जानकारी वाला वॉकथ्रू उपलब्ध है. आपको Local Emulator Suite के बारे में जानकारी भी देखनी चाहिए.
FIRDatabaseReference पाना
डेटाबेस से डेटा पढ़ने या उसमें डेटा लिखने के लिए, आपके पास FIRDatabaseReference
का एक इंस्टेंस होना चाहिए:
Swift
var ref: DatabaseReference! ref = Database.database().reference()
Objective-C
@property (strong, nonatomic) FIRDatabaseReference *ref; self.ref = [[FIRDatabase database] reference];
डेटा सेव करना
इस दस्तावेज़ में, Firebase डेटा को पढ़ने और उसमें डेटा जोड़ने के बारे में बुनियादी जानकारी दी गई है.
Firebase डेटा को Database
रेफ़रंस में लिखा जाता है और रेफ़रंस में असाइनोसाइनस लिसनर को अटैच करके उसे वापस पाया जाता है. डेटा की शुरुआती स्थिति के लिए, listener को एक बार ट्रिगर किया जाता है. इसके बाद, डेटा में बदलाव होने पर भी listener को ट्रिगर किया जाता है.
डेटा लिखने से जुड़ी बुनियादी कार्रवाइयां
डेटा को किसी तय रेफ़रंस में सेव करने के लिए, setValue
का इस्तेमाल किया जा सकता है. इससे उस पाथ में मौजूद किसी भी मौजूदा डेटा को बदल दिया जाता है. इस तरीके का इस्तेमाल करके:
- पास के टाइप, जो उपलब्ध JSON टाइप से मेल खाते हैं:
NSString
NSNumber
NSDictionary
NSArray
उदाहरण के लिए, setValue
के साथ उपयोगकर्ता को इस तरह जोड़ा जा सकता है:
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
का इस्तेमाल करने पर, बताई गई जगह पर मौजूद डेटा को बदल दिया जाता है. इसमें चाइल्ड नोड भी शामिल हैं. हालांकि, अब भी पूरे ऑब्जेक्ट को फिर से लिखे बिना, चाइल्ड को अपडेट किया जा सकता है. अगर आपको उपयोगकर्ताओं को अपनी प्रोफ़ाइल अपडेट करने की अनुमति देनी है, तो उपयोगकर्ता नाम को इस तरह अपडेट किया जा सकता है:
Swift
self.ref.child("users/\(user.uid)/username").setValue(username)
Objective-C
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];
डेटा पढ़ना
वैल्यू इवेंट को सुनकर डेटा पढ़ना
किसी पाथ पर डेटा पढ़ने और बदलावों को सुनने के लिए, FIRDatabaseReference
के observeEventType:withBlock
का इस्तेमाल करके FIRDataEventTypeValue
इवेंट देखें.
इवेंट प्रकार | आम तौर पर इस्तेमाल |
---|---|
FIRDataEventTypeValue |
किसी पाथ के पूरे कॉन्टेंट में हुए बदलावों को पढ़ना और सुनना. |
किसी दिए गए पाथ पर मौजूद डेटा को पढ़ने के लिए, FIRDataEventTypeValue
इवेंट का इस्तेमाल किया जा सकता है, क्योंकि यह इवेंट के समय मौजूद होता है. यह तरीका, listener के अटैच होने पर एक बार और हर बार डेटा में बदलाव होने पर फिर से ट्रिगर होता है. इसमें, डेटा में मौजूद किसी भी चाइल्ड एलिमेंट में होने वाले बदलाव भी शामिल हैं. इवेंट कॉलबैक को एक snapshot
पास किया जाता है, जिसमें उस जगह का सारा डेटा होता है. इसमें चाइल्ड डेटा भी शामिल होता है. अगर कोई डेटा नहीं है, तो स्नैपशॉट exists()
को कॉल करने पर false
और उसकी value
प्रॉपर्टी को पढ़ने पर nil
दिखाएगा.
यहां दिए गए उदाहरण में, डेटाबेस से किसी पोस्ट की जानकारी हासिल करने वाले सोशल ब्लॉगिंग ऐप्लिकेशन के बारे में बताया गया है:
Swift
refHandle = postRef.observe(DataEventType.value, with: { snapshot in // ... })
Objective-C
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { NSDictionary *postDict = snapshot.value; // ... }];
लिसनर को एक FIRDataSnapshot
मिलता है, जिसमें इवेंट के समय, डेटाबेस में तय की गई जगह पर मौजूद डेटा होता है. यह डेटा, value
प्रॉपर्टी में होता है. आपके पास, NSDictionary
जैसे सही नेटिव टाइप को वैल्यू असाइन करने का विकल्प है.
अगर जगह के लिए कोई डेटा मौजूद नहीं है, तो value
nil
होगा.
डेटा को एक बार पढ़ना
getData() का इस्तेमाल करके एक बार पढ़ना
एसडीके टूल को डेटाबेस सर्वर के साथ इंटरैक्शन मैनेज करने के लिए डिज़ाइन किया गया है. भले ही, आपका ऐप्लिकेशन ऑनलाइन हो या ऑफ़लाइन.
आम तौर पर, डेटा पढ़ने के लिए, आपको ऊपर बताई गई वैल्यू इवेंट की तकनीकों का इस्तेमाल करना चाहिए. इससे आपको बैकएंड से डेटा के अपडेट की सूचना मिलती है. इन तकनीकों से, आपके डेटा के इस्तेमाल और बिलिंग में कमी आती है. साथ ही, ये आपके उपयोगकर्ताओं को ऑनलाइन और ऑफ़लाइन, दोनों तरह से बेहतर अनुभव देने के लिए ऑप्टिमाइज़ की जाती हैं.
अगर आपको डेटा सिर्फ़ एक बार चाहिए, तो डेटाबेस से डेटा का स्नैपशॉट पाने के लिए, getData()
का इस्तेमाल किया जा सकता है. अगर किसी वजह से getData()
, सर्वर वैल्यू नहीं दिखा पाता है, तो क्लाइंट लोकल स्टोरेज कैश मेमोरी की जांच करेगा. अगर वैल्यू अब भी नहीं मिलती है, तो वह गड़बड़ी दिखाएगा.
इस उदाहरण में, डेटाबेस से उपयोगकर्ता के सार्वजनिक उपयोगकर्ता नाम को एक बार फिर से पाने का तरीका बताया गया है:
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()
का गलत इस्तेमाल करने से, बैंडविड्थ का इस्तेमाल बढ़ सकता है और परफ़ॉर्मेंस पर असर पड़ सकता है. हालांकि, ऊपर बताए गए तरीके से रीयल टाइम लिसनर का इस्तेमाल करके, इस समस्या से बचा जा सकता है.
ऑब्ज़र्वर की मदद से डेटा को एक बार पढ़ना
कुछ मामलों में, हो सकता है कि आप सर्वर पर अपडेट की गई वैल्यू की जांच करने के बजाय, स्थानीय कैश मेमोरी से वैल्यू तुरंत पाना चाहें. ऐसे मामलों में, observeSingleEventOfType
का इस्तेमाल करके, डिस्क कैश मेमोरी से तुरंत डेटा पाया जा सकता है.
यह उस डेटा के लिए फ़ायदेमंद है जिसे सिर्फ़ एक बार लोड करना होता है और जिसे बार-बार बदलने की ज़रूरत नहीं होती या जिसे सुनने की ज़रूरत नहीं होती. उदाहरण के लिए, पिछले उदाहरणों में दिया गया ब्लॉगिंग ऐप्लिकेशन, इस तरीके का इस्तेमाल करके उपयोगकर्ता की प्रोफ़ाइल को लोड करता है. ऐसा तब होता है, जब उपयोगकर्ता कोई नई पोस्ट लिखना शुरू करता है:
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); }];
डेटा अपडेट करना या मिटाना
चुनिंदा फ़ील्ड अपडेट करना
किसी नोड के दूसरे चाइल्ड नोड को ओवरराइट किए बिना, एक साथ कई चाइल्ड नोड में लिखने के लिए, updateChildValues
तरीके का इस्तेमाल करें.
updateChildValues
को कॉल करते समय, कुंजी के लिए पाथ तय करके, कम लेवल की चाइल्ड वैल्यू अपडेट की जा सकती हैं. अगर डेटा को बेहतर तरीके से स्केल करने के लिए, एक से ज़्यादा जगहों पर सेव किया गया है, तो डेटा फ़ैन-आउट का इस्तेमाल करके, उस डेटा के सभी इंस्टेंस अपडेट किए जा सकते हैं. उदाहरण के लिए, हो सकता है कि कोई सोशल ब्लॉगिंग ऐप्लिकेशन कोई पोस्ट बनाए और उसे हाल ही की गतिविधि वाले फ़ीड और पोस्ट करने वाले उपयोगकर्ता के गतिविधि फ़ीड में एक साथ अपडेट करे. ऐसा करने के लिए, ब्लॉगिंग ऐप्लिकेशन इस तरह के कोड का इस्तेमाल करता है:
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];
इस उदाहरण में, childByAutoId
का इस्तेमाल करके /posts/$postid
पर सभी उपयोगकर्ताओं की पोस्ट वाले नोड में एक पोस्ट बनाई गई है. साथ ही, getKey()
की मदद से कुंजी को भी वापस पाया गया है. इसके बाद, इस कुंजी का इस्तेमाल करके /user-posts/$userid/$postid
पर उपयोगकर्ता की पोस्ट में दूसरी एंट्री बनाई जा सकती है.
इन पाथ का इस्तेमाल करके, updateChildValues
को एक बार कॉल करके, JSON ट्री में एक साथ कई जगहों पर अपडेट किए जा सकते हैं. उदाहरण के लिए, यह उदाहरण दोनों जगहों पर नई पोस्ट कैसे बनाता है. इस तरह एक साथ किए गए अपडेट ऐटॉमिक होते हैं: या तो सभी अपडेट पूरे हो जाते हैं या सभी अपडेट पूरे नहीं होते.
'पूरा हो गया' ब्लॉक जोड़ना
अगर आपको यह जानना है कि आपका डेटा कब कमिट किया गया है, तो आपके पास 'पूरा हो गया' ब्लॉक जोड़ने का विकल्प है. setValue
और updateChildValues
, दोनों में एक वैकल्पिक 'पूरा होने का ब्लॉक' होता है. इसे तब कॉल किया जाता है, जब डेटाबेस में डेटा डालने की प्रोसेस पूरी हो जाती है. यह लिसनर, यह ट्रैक करने में मददगार हो सकता है कि कौनसा डेटा सेव हो गया है और कौनसा डेटा अब भी सिंक किया जा रहा है. अगर कॉल पूरा नहीं हो पाता है, तो Listener को गड़बड़ी का एक ऑब्जेक्ट भेजा जाता है. इससे पता चलता है कि कॉल पूरा न होने की वजह क्या है.
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."); } }];
डेटा मिटाना
डेटा मिटाने का सबसे आसान तरीका यह है कि डेटा की जगह के रेफ़रंस पर removeValue
को कॉल करें.
setValue
या updateChildValues
जैसे किसी अन्य लिखने वाले ऑपरेशन के लिए वैल्यू के तौर पर nil
तय करके भी मिटाया जा सकता है. एक ही एपीआई कॉल में कई बच्चों की जानकारी मिटाने के लिए, updateChildValues
के साथ इस तकनीक का इस्तेमाल किया जा सकता है.
लिसनर को अलग करना
जब आप किसी ViewController
से बाहर निकलते हैं, तब ऑब्ज़र्वर का डेटा अपने-आप सिंक होना बंद नहीं होता. अगर किसी ऑब्ज़र्वर को सही तरीके से नहीं हटाया जाता है, तो वह डेटा को लोकल मेमोरी में सिंक करता रहेगा. जब किसी ऑब्ज़र्वर की ज़रूरत न हो, तो उसे हटा दें. इसके लिए, removeObserverWithHandle
तरीके में उससे जुड़े FIRDatabaseHandle
को पास करें.
किसी रेफ़रंस में कॉलबैक ब्लॉक जोड़ने पर, FIRDatabaseHandle
दिखता है.
इन हैंडल का इस्तेमाल, कॉलबैक ब्लॉक को हटाने के लिए किया जा सकता है.
अगर डेटाबेस रेफ़रंस में कई लिसनर जोड़े गए हैं, तो कोई इवेंट होने पर हर लिसनर को कॉल किया जाता है. उस जगह पर डेटा सिंक होने की सुविधा बंद करने के लिए, आपको removeAllObservers
तरीका इस्तेमाल करके, उस जगह पर मौजूद सभी ऑब्ज़र्वर हटाने होंगे.
किसी लिसनर पर removeObserverWithHandle
या removeAllObservers
को कॉल करने से, उसके चाइल्ड नोड पर रजिस्टर किए गए लिसनर अपने-आप नहीं हटते. उन्हें हटाने के लिए, आपको उन रेफ़रंस या हैंडल पर भी नज़र रखनी होगी.
डेटा को लेन-देन के तौर पर सेव करना
अगर आपको ऐसे डेटा के साथ काम करना है जो एक साथ होने वाले बदलावों की वजह से खराब हो सकता है, तो ट्रांज़ैक्शन ऑपरेशन का इस्तेमाल करें. जैसे, इंक्रीमेंटल काउंटर. इस ऑपरेशन में दो आर्ग्युमेंट दिए जाते हैं: अपडेट फ़ंक्शन और ज़रूरी नहीं है कि पूरा होने पर कॉलबैक किया जाए. अपडेट फ़ंक्शन, डेटा की मौजूदा स्थिति को आर्ग्युमेंट के तौर पर लेता है और आपको जो नई स्थिति लिखनी है उसे दिखाता है.
उदाहरण के लिए, सोशल ब्लॉगिंग ऐप्लिकेशन के उदाहरण में, उपयोगकर्ताओं को पोस्ट पर स्टार देने और हटाने की अनुमति दी जा सकती है. साथ ही, इस बात का भी पता लगाया जा सकता है कि किसी पोस्ट को कितने स्टार मिले हैं. इसके लिए, यह तरीका अपनाएं:
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); } }];
ट्रांज़ैक्शन का इस्तेमाल करने से, स्टार की गिनती गलत होने से बचती है. ऐसा तब होता है, जब एक ही समय पर कई उपयोगकर्ता एक ही पोस्ट को स्टार करते हैं या क्लाइंट के पास पुराना डेटा होता है. FIRMutableData
क्लास में मौजूद वैल्यू, शुरुआत में क्लाइंट के पाथ की आखिरी वैल्यू होती है. अगर कोई वैल्यू नहीं है, तो यह nil
होती है. सर्वर, शुरुआती वैल्यू की तुलना उसकी मौजूदा वैल्यू से करता है. अगर वैल्यू मैच होती हैं, तो लेन-देन को स्वीकार कर लिया जाता है. अगर वैल्यू मैच नहीं होती हैं, तो लेन-देन अस्वीकार कर दिया जाता है. अगर लेन-देन अस्वीकार कर दिया जाता है, तो सर्वर क्लाइंट को मौजूदा वैल्यू दिखाता है. इसके बाद, क्लाइंट अपडेट की गई वैल्यू के साथ लेन-देन फिर से करता है. यह तब तक दोहराया जाता है, जब तक लेन-देन स्वीकार नहीं हो जाता या बहुत ज़्यादा कोशिशें नहीं की जातीं.
सर्वर साइड पर एक साथ कई बदलाव करना
ऊपर दिए गए इस्तेमाल के उदाहरण में, हम डेटाबेस में दो वैल्यू लिख रहे हैं: पोस्ट को स्टार देने/अनस्टार करने वाले उपयोगकर्ता का आईडी और स्टार की संख्या में हुई बढ़ोतरी. अगर हमें पहले से पता है कि उपयोगकर्ता पोस्ट को स्टार कर रहा है, तो हम लेन-देन के बजाय, एटमिक इंक्रीमेंट ऑपरेशन का इस्तेमाल कर सकते हैं.
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];
यह कोड, लेन-देन के ऑपरेशन का इस्तेमाल नहीं करता. इसलिए, अगर कोई अपडेट करने में कोई समस्या आती है, तो यह कोड अपने-आप फिर से नहीं चलता. हालांकि, डेटाबेस सर्वर पर सीधे तौर पर इंक्रीमेंट ऑपरेशन होने की वजह से, डेटा में कोई गड़बड़ी नहीं होती.
अगर आपको ऐप्लिकेशन से जुड़ी समस्याओं का पता लगाना है और उन्हें अस्वीकार करना है, तो आपको उस इस्तेमाल के उदाहरण के लिए, सुरक्षा से जुड़े कस्टम नियम लिखने चाहिए. उदाहरण के लिए, जब कोई उपयोगकर्ता किसी ऐसी पोस्ट को स्टार कर दे जिसे उसने पहले ही स्टार कर दिया है.
डेटा के साथ ऑफ़लाइन काम करना
अगर किसी क्लाइंट का नेटवर्क कनेक्शन बंद हो जाता है, तो भी आपका ऐप्लिकेशन ठीक से काम करता रहेगा.
Firebase डेटाबेस से कनेक्ट किया गया हर क्लाइंट, किसी भी ऐक्टिव डेटा का अपना इंटरनल वर्शन मैनेज करता है. डेटा लिखने पर, वह सबसे पहले इस लोकल वर्शन में लिखा जाता है. इसके बाद, Firebase क्लाइंट उस डेटा को रिमोट डेटाबेस के सर्वर और अन्य क्लाइंट के साथ "बेहतरीन तरीके" से सिंक करता है.
इस वजह से, डेटाबेस में किए गए सभी बदलाव, सर्वर में कोई डेटा लिखे जाने से पहले, तुरंत लोकल इवेंट को ट्रिगर करते हैं. इसका मतलब है कि नेटवर्क के इंतज़ार या कनेक्टिविटी के बावजूद, आपका ऐप्लिकेशन काम करता रहेगा.
कनेक्शन फिर से चालू होने के बाद, आपके ऐप्लिकेशन को इवेंट का सही सेट मिलता है, ताकि क्लाइंट किसी कस्टम कोड को लिखे बिना, सर्वर की मौजूदा स्थिति के साथ सिंक हो सके.
हम ऑनलाइन और ऑफ़लाइन सुविधाओं के बारे में ज़्यादा जानें लेख में, ऑफ़लाइन व्यवहार के बारे में ज़्यादा बात करेंगे.