অ্যাপল প্ল্যাটফর্মগুলিতে ডেটা পড়ুন এবং লিখুন

(ঐচ্ছিক) 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 এর একটি ইনস্ট্যান্স প্রয়োজন।

সুইফট

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
var ref: DatabaseReference!

ref = Database.database().reference()

উদ্দেশ্য-সি

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
@property (strong, nonatomic) FIRDatabaseReference *ref;

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

ডেটা লিখুন

এই ডকুমেন্টে ফায়ারবেস ডেটা পড়া এবং লেখার প্রাথমিক বিষয়গুলো আলোচনা করা হয়েছে।

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

মৌলিক লেখার ক্রিয়াকলাপ

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

  • উপলব্ধ JSON টাইপগুলোর সাথে সঙ্গতিপূর্ণ টাইপগুলো নিম্নরূপে পাস করুন:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

উদাহরণস্বরূপ, আপনি setValue ব্যবহার করে নিম্নলিখিতভাবে একজন ব্যবহারকারী যোগ করতে পারেন:

সুইফট

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
self.ref.child("users").child(user.uid).setValue(["username": username])

উদ্দেশ্য-সি

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
[[[self.ref child:@"users"] child:authResult.user.uid]
    setValue:@{@"username": username}];

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

সুইফট

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
self.ref.child("users/\(user.uid)/username").setValue(username)

উদ্দেশ্য-সি

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

ডেটা পড়ুন

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

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

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

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

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

সুইফট

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
refHandle = postRef.observe(DataEventType.value, with: { snapshot in
  // ...
})

উদ্দেশ্য-সি

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

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

ডেটা একবার পড়ুন

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

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

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

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

নিম্নলিখিত উদাহরণটি ডাটাবেস থেকে একজন ব্যবহারকারীর সর্বজনীন ইউজারনেম একবার পুনরুদ্ধার করার পদ্ধতি প্রদর্শন করে:

সুইফট

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
do {
  let snapshot = try await ref.child("users/\(uid)/username").getData()
  let userName = snapshot.value as? String ?? "Unknown"
} catch {
  print(error)
}

উদ্দেশ্য-সি

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
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 ব্যবহার করতে পারেন।

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

সুইফট

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
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)
}

উদ্দেশ্য-সি

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
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 ​​কল করার সময়, আপনি কী-এর জন্য একটি পাথ নির্দিষ্ট করে নিম্ন-স্তরের চাইল্ড ভ্যালুগুলো আপডেট করতে পারেন। যদি আরও ভালোভাবে স্কেল করার জন্য ডেটা একাধিক স্থানে সংরক্ষিত থাকে, তাহলে আপনি ডেটা ফ্যান-আউট ব্যবহার করে সেই ডেটার সমস্ত ইনস্ট্যান্স আপডেট করতে পারেন। উদাহরণস্বরূপ, একটি সোশ্যাল ব্লগিং অ্যাপ একটি পোস্ট তৈরি করে একই সাথে সেটিকে রিসেন্ট অ্যাক্টিভিটি ফিড এবং পোস্টকারী ব্যবহারকারীর অ্যাক্টিভিটি ফিডে আপডেট করতে চাইতে পারে। এটি করার জন্য, ব্লগিং অ্যাপ্লিকেশনটি এই ধরনের কোড ব্যবহার করে:

সুইফট

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
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)

উদ্দেশ্য-সি

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

সুইফট

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
do {
  try await ref.child("users").child(user.uid).setValue(["username": username])
  print("Data saved successfully!")
} catch {
  print("Data could not be saved: \(error).")
}

উদ্দেশ্য-সি

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

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

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

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

লেনদেন হিসেবে ডেটা সংরক্ষণ করুন

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

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

সুইফট

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
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)
  }
}

উদ্দেশ্য-সি

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
[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 । সার্ভার প্রাথমিক মানটিকে তার বর্তমান মানের সাথে তুলনা করে এবং মান মিলে গেলে ট্রানজ্যাকশনটি গ্রহণ করে, অথবা প্রত্যাখ্যান করে। যদি ট্রানজ্যাকশনটি প্রত্যাখ্যান করা হয়, সার্ভার বর্তমান মানটি ক্লায়েন্টের কাছে ফেরত পাঠায়, যা আপডেট করা মান দিয়ে ট্রানজ্যাকশনটি আবার চালায়। এই প্রক্রিয়াটি ততক্ষণ চলতে থাকে যতক্ষণ না ট্রানজ্যাকশনটি গৃহীত হয় বা অনেক বেশিবার চেষ্টা করা হয়ে যায়।

পারমাণবিক সার্ভার-সাইড বৃদ্ধি

উপরের উদাহরণে আমরা ডাটাবেসে দুটি ভ্যালু লিখছি: যে ইউজার পোস্টটি স্টার বা আনস্টার করছেন তার আইডি, এবং স্টারের সংখ্যা বৃদ্ধির পরিমাণ। যদি আমরা আগে থেকেই জানি যে ইউজার পোস্টটি স্টার করছেন, তাহলে আমরা ট্রানজ্যাকশনের পরিবর্তে একটি অ্যাটমিক ইনক্রিমেন্ট অপারেশন ব্যবহার করতে পারি।

সুইফট

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
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)

উদ্দেশ্য-সি

দ্রষ্টব্য: এই Firebase পণ্যটি App Clip টার্গেটে উপলব্ধ নয়।
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];

এই কোডটি কোনো ট্রানজ্যাকশন অপারেশন ব্যবহার করে না, তাই কোনো সাংঘর্ষিক আপডেট ঘটলে এটি স্বয়ংক্রিয়ভাবে পুনরায় রান হয় না। তবে, যেহেতু ইনক্রিমেন্ট অপারেশনটি সরাসরি ডাটাবেস সার্ভারে সম্পন্ন হয়, তাই কোনো সংঘাতের সম্ভাবনা থাকে না।

যদি আপনি অ্যাপ্লিকেশন-নির্দিষ্ট দ্বন্দ্ব শনাক্ত ও বাতিল করতে চান, যেমন কোনো ব্যবহারকারী পূর্বে স্টার দেওয়া কোনো পোস্টে আবার স্টার দেওয়া, তাহলে সেই নির্দিষ্ট পরিস্থিতির জন্য আপনার নিজস্ব নিরাপত্তা নিয়ম লেখা উচিত।

অফলাইনে ডেটা নিয়ে কাজ করুন

যদি কোনো ক্লায়েন্ট তার নেটওয়ার্ক সংযোগ হারায়, আপনার অ্যাপটি সঠিকভাবে কাজ করতে থাকবে।

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

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

সংযোগ পুনঃপ্রতিষ্ঠিত হলে, আপনার অ্যাপটি প্রয়োজনীয় ইভেন্টগুলো পেয়ে যায়, যার ফলে কোনো কাস্টম কোড না লিখেই ক্লায়েন্ট বর্তমান সার্ভার অবস্থার সাথে সিঙ্ক হয়ে যায়।

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

পরবর্তী পদক্ষেপ