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

(ঐচ্ছিক) 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 পণ্যটি অ্যাপ ক্লিপ টার্গেটে উপলব্ধ নয়।
var ref: DatabaseReference!

ref = Database.database().reference()

অবজেক্টিভ-সি

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

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

তথ্য লিখুন

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

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

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

মৌলিক লেখার ক্রিয়াকলাপের জন্য, আপনি setValue ব্যবহার করে একটি নির্দিষ্ট রেফারেন্সে ডেটা সংরক্ষণ করতে পারেন, সেই পাথে বিদ্যমান যেকোনো ডেটা প্রতিস্থাপন করতে পারেন। আপনি এই পদ্ধতিটি ব্যবহার করতে পারেন:

  • নিম্নলিখিত ধরণের পাস যা উপলব্ধ JSON ধরণের সাথে সঙ্গতিপূর্ণ:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

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

সুইফট

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

অবজেক্টিভ-সি

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

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

সুইফট

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

অবজেক্টিভ-সি

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

তথ্য পড়ুন

মূল্যবোধের ঘটনা শুনে ডেটা পড়ুন

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

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

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

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

সুইফট

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

অবজেক্টিভ-সি

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

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

একবার তথ্য পড়ুন

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

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

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

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

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

সুইফট

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

অবজেক্টিভ-সি

দ্রষ্টব্য: এই 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 ব্যবহার করতে পারেন।

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

সুইফট

দ্রষ্টব্য: এই 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)
}

অবজেক্টিভ-সি

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

সুইফট

দ্রষ্টব্য: এই 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)

অবজেক্টিভ-সি

দ্রষ্টব্য: এই 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 এ ব্যবহারকারীর পোস্টে দ্বিতীয় এন্ট্রি তৈরি করতে কীটি ব্যবহার করা যেতে পারে।

এই পাথগুলি ব্যবহার করে, আপনি JSON ট্রিতে একাধিক অবস্থানে একযোগে আপডেট করতে পারেন updateChildValues ​​এ একটি একক কলের মাধ্যমে, যেমন এই উদাহরণটি কীভাবে উভয় অবস্থানে নতুন পোস্ট তৈরি করে। এইভাবে করা একযোগে আপডেটগুলি পারমাণবিক: হয় সমস্ত আপডেট সফল হয় অথবা সমস্ত আপডেট ব্যর্থ হয়।

একটি সমাপ্তি ব্লক যোগ করুন

আপনার ডেটা কখন কমিট করা হয়েছে তা জানতে চাইলে, আপনি একটি কমপ্লিশন ব্লক যোগ করতে পারেন। setValue এবং updateChildValues ​​উভয়ই একটি ঐচ্ছিক কমপ্লিশন ব্লক নেয় যা ডাটাবেসে লেখার সময় কল করা হয়। এই লিসেনার কোন ডেটা সংরক্ষণ করা হয়েছে এবং কোন ডেটা এখনও সিঙ্ক্রোনাইজ করা হচ্ছে তা ট্র্যাক করার জন্য কার্যকর হতে পারে। যদি কলটি ব্যর্থ হয়, তাহলে লিসেনারকে একটি ত্রুটি বস্তু পাঠানো হয় যা নির্দেশ করে যে কেন ব্যর্থতা ঘটেছে।

সুইফট

দ্রষ্টব্য: এই 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).")
}

অবজেক্টিভ-সি

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

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

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

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

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

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

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

সুইফট

দ্রষ্টব্য: এই 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)
  }
}

অবজেক্টিভ-সি

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

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

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

সুইফট

দ্রষ্টব্য: এই 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)

অবজেক্টিভ-সি

দ্রষ্টব্য: এই 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];

এই কোডটি কোনও লেনদেন অপারেশন ব্যবহার করে না, তাই কোনও বিরোধপূর্ণ আপডেট থাকলে এটি স্বয়ংক্রিয়ভাবে পুনরায় চালানো হয় না। তবে, যেহেতু ইনক্রিমেন্ট অপারেশনটি সরাসরি ডাটাবেস সার্ভারে ঘটে, তাই কোনও বিরোধের সম্ভাবনা নেই।

যদি আপনি অ্যাপ্লিকেশন-নির্দিষ্ট দ্বন্দ্ব সনাক্ত করতে এবং প্রত্যাখ্যান করতে চান, যেমন একজন ব্যবহারকারী এমন একটি পোস্টকে তারকাচিহ্নিত করেছেন যা তারা ইতিমধ্যেই তারকাচিহ্নিত করেছে, তাহলে আপনার সেই ব্যবহারের ক্ষেত্রে কাস্টম সুরক্ষা নিয়ম লিখতে হবে।

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

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

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

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

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

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

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