Apple প্ল্যাটফর্মে ডেটা পড়া ও লেখা

(ঐচ্ছিক) Firebase Local Emulator Suite-এর সাথে প্রোটোটাইপ ও টেস্ট করুন

আপনার অ্যাপ কীভাবে Realtime Database থেকে ডেটা পড়ে এবং তাতে ডেটা লেখে সেই বিষয়ে কথা বলার আগে, আসুন এমন কিছু টুলের সাথে পরিচিত হই যেগুলি আপনি Realtime Database-এর কার্যকারিতা প্রোটোটাইপ এবং পরীক্ষা করার জন্য ব্যবহার করতে পারেন: Firebase Local Emulator Suite। আপনি যদি বিভিন্ন ডেটা মডেল ব্যবহার করে দেখেন, আপনার নিরাপত্তা সংক্রান্ত নিয়ম অপ্টিমাইজ করেন অথবা ব্যাক-এন্ডের সাথে ইন্টার‍্যাক্ট করার সবচেয়ে সাশ্রয়ী উপায় খুঁজে বের করার চেষ্টা করেন, তাহলে লাইভ পরিষেবা ডিপ্লয় না করে লোকালভাবে কাজ করতে পারাটা একটা দারুণ আইডিয়া হতে পারে।

Realtime Database এমুলেটর হল Local Emulator Suite-এর অংশ, যা আপনার অ্যাপকে আপনার এমুলেট করা ডেটাবেস কন্টেন্ট এবং কনফিগারেশনের সাথে ইন্টার‍্যাক্ট করতে দেয়, পাশাপাশি আপনার এমুলেট করা প্রোজেক্ট রিসোর্স (ফাংশন, অন্যান্য ডেটাবেস এবং নিরাপত্তা সংক্রান্ত নিয়ম) ঐচ্ছিকভাবে ব্যবহার করতে দেয়।

Realtime Database এমুলেটর ব্যবহার করার জন্য মাত্র কয়েকটি ধাপ অনুসরণ করতে হয়:

  1. এমুলেটরের সাথে কানেক্ট করার জন্য আপনার অ্যাপের টেস্ট কনফিগারেশনে কোডের একটি লাইন যোগ করা।
  2. আপনার লোকাল প্রোজেক্ট ডিরেক্টরির রুট থেকে, firebase emulators:start রান করা।
  3. যথারীতি Realtime Database প্ল্যাটফর্ম SDK ব্যবহার করে আপনার অ্যাপের প্রোটোটাইপ কোড থেকে কল করা, অথবা Realtime Database REST API ব্যবহার করা।

Realtime Database ও Cloud Functions-এর বিস্তারিত ওয়াকথ্রু উপলভ্য। এছাড়াও, আপনাকে Local Emulator Suite ভূমিকা দেখতে হবে।

FIRDatabaseReference পান

ডেটাবেস থেকে ডেটা পড়তে বা লিখতে, আপনার FIRDatabaseReference-এর ইনস্ট্যান্স প্রয়োজন:

Swift

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
var ref: DatabaseReference!

ref = Database.database().reference()

Objective-C

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
@property (strong, nonatomic) FIRDatabaseReference *ref;

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

ডেটা এডিট করা

এই ডকুমেন্টে Firebase ডেটা পড়া ও লেখার প্রাথমিক বিষয়গুলি কভার করা হয়েছে।

Firebase ডেটা Database রেফারেন্সে লেখা হয় এবং রেফারেন্সে অ্যাসিঙ্ক্রোনাস লিসেনার অ্যাটাচ করে তা ফিরিয়ে আনা হয়। ডেটার প্রাথমিক অবস্থার জন্য একবার এবং ডেটা পরিবর্তন হলে আবার যেকোনও সময় লিসেনার ট্রিগার করা হয়।

বেসিক রাইট অপারেশন

বেসিক রাইট অপারেশনের জন্য, আপনি setValue ব্যবহার করে নির্দিষ্ট রেফারেন্সে ডেটা সেভ করতে পারেন, যা সেই পাথে আগে থেকে থাকা যেকোনও ডেটা রিপ্লেস করে। আপনি এই পদ্ধতি ব্যবহার করে:

  • পাসের ধরন যা উপলভ্য JSON ধরনের সাথে মিলে যায়, যেমন:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

যেমন, আপনি এইভাবে setValue-এর সাথে একজন ব্যবহারকারীকে যোগ করতে পারেন:

Swift

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
self.ref.child("users").child(user.uid).setValue(["username": username])

Objective-C

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
[[[self.ref child:@"users"] child:authResult.user.uid]
    setValue:@{@"username": username}];

এইভাবে setValue ব্যবহার করলে নির্দিষ্ট লোকেশনে ডেটা ওভাররাইট হয়ে যায়, যার মধ্যে যেকোনও চাইল্ড নোডও অন্তর্ভুক্ত। তবে, আপনি এখনও সম্পূর্ণ অবজেক্ট আবার না লিখে চাইল্ড আপডেট করতে পারবেন। আপনি যদি ব্যবহারকারীদের তাদের প্রোফাইল আপডেট করার অনুমতি দিতে চান তাহলে আপনি নিম্নলিখিত উপায়ে ইউজারনেম আপডেট করতে পারেন:

Swift

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
self.ref.child("users/\(user.uid)/username").setValue(username)

Objective-C

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

ডেটা দেখা

ভ্যালু ইভেন্ট শুনে ডেটা পড়ুন

কোনও পাথে ডেটা পড়তে এবং পরিবর্তনের জন্য শুনতে, FIRDataEventTypeValue ইভেন্ট পর্যবেক্ষণ করতে FIRDatabaseReference-এর observeEventType:withBlock ব্যবহার করুন।

ইভেন্টের প্রকার সাধারণ ব্যবহার
FIRDataEventTypeValue কোনও পাথের সম্পূর্ণ কন্টেন্টে পরিবর্তন করা হলে তা পড়ে ও শোনে।

আপনি FIRDataEventTypeValue ইভেন্ট ব্যবহার করে নির্দিষ্ট পাথে ডেটা পড়তে পারবেন, যেহেতু ইভেন্টের সময় এটি থাকে। এই পদ্ধতিটি একবার ট্রিগার হয় যখন লিসেনার অ্যাটাচ করা হয় এবং আবার প্রতিবার ডেটা, যেকোনও চাইল্ড সহ, পরিবর্তন হয়। ইভেন্ট কলব্যাক একটি snapshot পাস করে যাতে সেই লোকেশনের সমস্ত ডেটা থাকে, যার মধ্যে চাইল্ড ডেটাও অন্তর্ভুক্ত। কোনও ডেটা না থাকলে, আপনি exists() কল করলে স্ন্যাপশট false রিটার্ন করবে এবং আপনি এর value প্রপার্টি পড়লে nil রিটার্ন করবে।

নিম্নলিখিত উদাহরণে দেখানো হয়েছে যে কীভাবে একটি সোশ্যাল ব্লগিং অ্যাপ্লিকেশন ডেটাবেস থেকে একটি পোস্টের বিবরণ খুঁজে বের করে:

Swift

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
refHandle = postRef.observe(DataEventType.value, with: { snapshot in
  // ...
})

Objective-C

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

লিসেনার একটি FIRDataSnapshot পায় যাতে ইভেন্টের সময় ডেটাবেসের নির্দিষ্ট লোকেশনে থাকা ডেটা এর value প্রপার্টিতে থাকে। আপনি উপযুক্ত নেটিভ টাইপে ভ্যালু অ্যাসাইন করতে পারেন, যেমন NSDictionary। লোকেশন সংক্রান্ত কোনও ডেটা না থাকলে, value হল nil।

একবার ডেটা দেখা

getData() ব্যবহার করে একবার পড়ুন

আপনার অ্যাপ অনলাইন বা অফলাইন যাই হোক না কেন, ডেটাবেস সার্ভারের সাথে ইন্টার‍্যাকশন ম্যানেজ করার জন্য SDK ডিজাইন করা হয়েছে।

সাধারণত, ব্যাকএন্ড থেকে ডেটার আপডেট সম্পর্কে বিজ্ঞপ্তি পেতে, ডেটা পড়ার জন্য উপরে বর্ণিত ভ্যালু ইভেন্ট টেকনিক ব্যবহার করা উচিত। এইসব টেকনিক আপনার ব্যবহার ও বিলিং কমায় এবং আপনার ব্যবহারকারীদের অনলাইন ও অফলাইন থাকার সময় সেরা অভিজ্ঞতা দেওয়ার জন্য অপ্টিমাইজ করা হয়েছে।

আপনার যদি ডেটা শুধুমাত্র একবার প্রয়োজন হয়, তাহলে আপনি getData() ব্যবহার করে ডেটাবেস থেকে ডেটার স্ন্যাপশট পেতে পারেন। কোনও কারণে getData() সার্ভার ভ্যালু ফেরত দিতে না পারলে, ক্লায়েন্ট লোকাল স্টোরেজ ক্যাশে পরীক্ষা করবে এবং ভ্যালু এখনও খুঁজে না পেলে একটি সমস্যা দেখাবে।

নিম্নলিখিত উদাহরণে দেখানো হয়েছে যে কীভাবে ডেটাবেস থেকে একবার ব্যবহারকারীর সর্বজনীন ইউজারনেম ফিরিয়ে আনা যায়:

Swift

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
do {
  let snapshot = try await ref.child("users/\(uid)/username").getData()
  let userName = snapshot.value as? String ?? "Unknown"
} catch {
  print(error)
}

Objective-C

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
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

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
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

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
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

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
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

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
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

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
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

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
[[[_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 ছেড়ে চলে গেলে, পর্যবেক্ষকরা অটোমেটিক ডেটা সিঙ্ক করা বন্ধ করে না। কোনও পর্যবেক্ষককে সঠিকভাবে সরানো না হলে, এটি লোকাল মেমরিতে ডেটা সিঙ্ক করা চালিয়ে যায়। পর্যবেক্ষকের আর প্রয়োজন না থাকলে, removeObserverWithHandle পদ্ধতিতে সংশ্লিষ্ট FIRDatabaseHandle পাস করে এটি সরিয়ে দিন।

আপনি কোনও রেফারেন্সে কলব্যাক ব্লক যোগ করলে, একটি FIRDatabaseHandle রিটার্ন করা হয়। কলব্যাক ব্লক সরিয়ে দেওয়ার জন্য এইসব হ্যান্ডেল ব্যবহার করা যেতে পারে।

কোনও ডেটাবেস রেফারেন্সে একাধিক লিসেনার যোগ করা হলে, কোনও ইভেন্ট ঘটলে প্রতিটি লিসেনারকে কল করা হয়। সেই লোকেশনে ডেটা সিঙ্ক করা বন্ধ করতে, আপনাকে removeAllObservers মেথড কল করে লোকেশনের সব অবজারভার সরাতে হবে।

কোনও লিসেনারে removeObserverWithHandle বা removeAllObservers কল করলে তার চাইল্ড নোডে রেজিস্টার করা লিসেনার অটোমেটিক মুছে যায় না; আপনাকেও সেগুলি মুছে ফেলার জন্য সেই রেফারেন্স বা হ্যান্ডেল ট্র্যাক করতে হবে।

ট্রানজ্যাকশন হিসেবে ডেটা সেভ করুন

ইনক্রিমেন্টাল কাউন্টারের মতো একই সাথে করা পরিবর্তনের ফলে খারাপ হয়ে যেতে পারে এমন ডেটা নিয়ে কাজ করার সময়, আপনি ট্রানজ্যাকশন অপারেশন ব্যবহার করতে পারেন। এই অপারেশনে আপনি দুটি আর্গুমেন্ট দেন: একটি আপডেট ফাংশন এবং একটি ঐচ্ছিক সম্পূর্ণতা কলব্যাক। আপডেট ফাংশন ডেটার বর্তমান অবস্থাকে একটি আর্গুমেন্ট হিসেবে গ্রহণ করে এবং আপনি যে নতুন কাঙ্ক্ষিত অবস্থা লিখতে চান তা প্রদান করে।

যেমন, সোশ্যাল ব্লগিং অ্যাপের উদাহরণে, আপনি ব্যবহারকারীদের পোস্ট স্টার ও আনস্টার করার অনুমতি দিতে পারেন এবং একটি পোস্ট কতগুলি স্টার পেয়েছে তা ট্র্যাক করতে পারেন নিম্নলিখিত উপায়ে:

Swift

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
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

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
[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

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
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

মনে রাখবেন: এই Firebase প্রোডাক্ট অ্যাপ ক্লিপ টার্গেটে উপলভ্য নয়।
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 ক্লায়েন্ট তখন রিমোট ডেটাবেস সার্ভার এবং অন্যান্য ক্লায়েন্টের সাথে "সর্বোচ্চ-চেষ্টা"র ভিত্তিতে সেই ডেটা সিঙ্ক করে।

এর ফলে, ডেটাবেসে কিছু লেখা হলে, সার্ভারে কোনও ডেটা লেখার আগেই, তা সাথে সাথে লোকাল ইভেন্ট ট্রিগার করে। এর অর্থ হল, নেটওয়ার্ক লেটেন্সি বা কানেক্টিভিটি যাই হোক না কেন, আপনার অ্যাপ রেসপন্সিভ থাকে।

কানেক্টিভিটি আবার চালু হয়ে গেলে, আপনার অ্যাপ উপযুক্ত ইভেন্ট সেট পায় যাতে ক্লায়েন্ট বর্তমান সার্ভার স্ট্যাটাসের সাথে সিঙ্ক করে, কোনও কাস্টম কোড লিখতে হয় না।

আমরা অফলাইন আচরণ সম্পর্কে আরও কথা বলব অনলাইন এবং অফলাইন ক্ষমতা সম্পর্কে আরও জানুন।

পরবর্তী ধাপ