(ঐচ্ছিক) 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 রেফারেন্সে লেখা হয় এবং রেফারেন্সে
অ্যাসিঙ্ক্রোনাস লিসনার অ্যাটাচ করে তা রিট্রিভ করা হয়। ডেটার প্রাথমিক অবস্থার জন্য লিসনার একবার ট্রিগার হয়
এবং ডেটা পরিবর্তন হলে আবার ট্রিগার হয়।
বেসিক লেখার অপারেশন
সাধারণ লেখার অপারেশনের জন্য, আপনি setValue ব্যবহার করে নির্দিষ্ট
রেফারেন্সে ডেটা সেভ করতে পারেন, যা সেই পাথে আগে থেকে থাকা ডেটা প্রতিস্থাপন করে। আপনি এগুলি করার জন্য এই পদ্ধতি ব্যবহার করতে পারবেন:
- উপলভ্য JSON ধরনের সাথে সম্পর্কিত পাসের ধরনগুলি নিচে দেওয়া হল:
NSStringNSNumberNSDictionaryNSArray
যেমন, আপনি 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];
ডেটা পড়া
ভ্যালু ইভেন্ট শুনে ডেটা পড়া
কোনও পাথে ডেটা পড়তে এবং পরিবর্তন সম্পর্কে শুনতে, observeEventType:withBlock of FIRDatabaseReference ব্যবহার করে
FIRDataEventTypeValue ইভেন্ট দেখুন।
| ইভেন্টের প্রকার | সাধারণ ব্যবহার |
|---|---|
FIRDataEventTypeValue |
পাথের সম্পূর্ণ কন্টেন্টে হওয়া পরিবর্তনগুলি পড়া ও শোনা। |
ইভেন্টের সময় ডেটা যেভাবে ছিল, সেইভাবে নির্দিষ্ট পাথে ডেটা রিড করার জন্য আপনি FIRDataEventTypeValue ইভেন্ট ব্যবহার করতে পারবেন।
লিসনার অ্যাটাচ করা হলে এই পদ্ধতি একবার ট্রিগার করা হয় এবং কোনও বাচ্চা সহ ডেটা
পরিবর্তিত হলে প্রতিবার ট্রিগার করা হয়।
ইভেন্ট কলব্যাকে 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() ব্যবহার করে একবার পড়ুন
আপনার অ্যাপ অনলাইন বা অফলাইন যাই হোক না কেন, SDK ডেটাবেস সার্ভারের সাথে ইন্টার্যাকশন ম্যানেজ করার জন্য ডিজাইন করা হয়েছে।
সাধারণত, ব্যাকএন্ড থেকে ডেটা আপডেট হলে সেই বিষয়ে বিজ্ঞপ্তি পেতে, আপনাকে উপরে বর্ণিত ভ্যালু ইভেন্ট টেকনিক ব্যবহার করে ডেটা পড়তে হবে। এইসব টেকনিক আপনার ব্যবহার ও বিলিং কমায় এবং আপনার ব্যবহারকারীদের অনলাইন ও অফলাইন অভিজ্ঞতা আরও ভাল করে তুলতে অপ্টিমাইজ করা হয়।
আপনার যদি ডেটা একবারই প্রয়োজন হয়, তাহলে আপনি 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()-এর অপ্রয়োজনীয় ব্যবহার ব্যান্ডউইথের ব্যবহার বাড়িয়ে দিতে পারে এবং পারফর্ম্যান্সের
ক্ষতি হতে পারে, যা উপরে দেখানো
মতো রিয়েলটাইম লিসনার ব্যবহার করে আটকানো যেতে পারে।
Observer-এর মাধ্যমে একবার ডেটা পড়া
কিছু ক্ষেত্রে, আপনি হয়ত চান যে সার্ভারে আপডেট করা ভ্যালু চেক করার পরিবর্তে
স্থানীয় ক্যাশে থেকে ভ্যালু অবিলম্বে রিটার্ন করা হোক। সেইসব
ক্ষেত্রে, লোকাল ডিস্ক ক্যাশে থেকে
অবিলম্বে ডেটা পেতে আপনি 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, দু'টিই ঐচ্ছিক
কমপ্লিশন ব্লক নেয়, যা ডেটাবেসে
রাইট কমিট করা হলে কল করা হয়। কোন ডেটা সেভ করা হয়েছে এবং কোন ডেটা এখনও সিঙ্ক করা হচ্ছে তা ট্র্যাক করার জন্য এই লিসনার কাজে লাগতে পারে।
কলটি সফল না হলে,
কেন কলটি সফল হয়নি তা উল্লেখ করে একটি সমস্যার অবজেক্ট লিসনারকে পাস করা হয়।
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-এর সাথে ব্যবহার করে একটি API কলের মাধ্যমে একাধিক বাচ্চাদের প্রোফাইল মুছে দিতে পারবেন।
লিসনার আলাদা করা
আপনি কোনও
ViewController ছেড়ে গেলে, পর্যবেক্ষকরা অটোমেটিক ডেটা সিঙ্ক করা বন্ধ করেন না। কোনও পর্যবেক্ষককে সঠিকভাবে সরানো না হলে, এটি স্থানীয় মেমোরিতে ডেটা সিঙ্ক
করা চালিয়ে যায়। পর্যবেক্ষকের আর প্রয়োজন না হলে, সংশ্লিষ্ট FIRDatabaseHandle-কে removeObserverWithHandle পদ্ধতিতে পাস করে
সেটি সরিয়ে দিন।
আপনি কোনও রেফারেন্সে কলব্যাক ব্লক যোগ করলে, একটি 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 ক্লায়েন্ট তারপরে সেই ডেটা রিমোট ডেটাবেস সার্ভার ও অন্যান্য ক্লায়েন্টের সাথে "সর্বোত্তম প্রচেষ্টা" ভিত্তিতে সিঙ্ক্রোনাইজ করে।
এর ফলে, ডেটাবেসে লেখা সব ডেটা, সার্ভারে কোনও ডেটা লেখার আগে অবিলম্বে লোকাল ইভেন্ট ট্রিগার করে। এর অর্থ হল, নেটওয়ার্ক লেটেন্সি বা কানেক্টিভিটি যাই হোক না কেন, আপনার অ্যাপ প্রতিক্রিয়াশীল থাকে।
কানেক্টিভিটি আবার চালু হয়ে গেলে, আপনার অ্যাপ ইভেন্টের উপযুক্ত সেট পায় যাতে ক্লায়েন্ট বর্তমান সার্ভার স্টেটের সাথে সিঙ্ক করতে পারে, এর জন্য কোনও কাস্টম কোড লিখতে হয় না।
আমরা অনলাইন ও অফলাইন ক্ষমতা সম্পর্কে আরও জানুন লিঙ্কে অফলাইন আচরণ সম্পর্কে আরও আলোচনা করব।