(ঐচ্ছিক) 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
এর একটি উদাহরণ প্রয়োজন:
সুইফট
var ref: DatabaseReference! ref = Database.database().reference()
উদ্দেশ্য-C
@property (strong, nonatomic) FIRDatabaseReference *ref; self.ref = [[FIRDatabase database] reference];
ডেটা লিখুন
এই দস্তাবেজটি Firebase ডেটা পড়ার এবং লেখার মৌলিক বিষয়গুলিকে কভার করে৷
ফায়ারবেস ডেটা একটি Database
রেফারেন্সে লেখা হয় এবং রেফারেন্সের সাথে একটি অ্যাসিঙ্ক্রোনাস লিসেনার সংযুক্ত করে পুনরুদ্ধার করা হয়। শ্রোতা ডেটার প্রাথমিক অবস্থার জন্য একবার ট্রিগার হয় এবং আবার যে কোনও সময় ডেটা পরিবর্তন হয়।
মৌলিক লেখার ক্রিয়াকলাপ
মৌলিক লেখার ক্রিয়াকলাপের জন্য, আপনি একটি নির্দিষ্ট রেফারেন্সে ডেটা সংরক্ষণ করতে setValue
ব্যবহার করতে পারেন, সেই পথে বিদ্যমান ডেটা প্রতিস্থাপন করতে পারেন। আপনি এই পদ্ধতিটি ব্যবহার করতে পারেন:
- নিম্নরূপ উপলব্ধ JSON প্রকারের সাথে সঙ্গতিপূর্ণ পাস প্রকার:
-
NSString
-
NSNumber
-
NSDictionary
-
NSArray
-
উদাহরণস্বরূপ, আপনি setValue
সহ একজন ব্যবহারকারীকে নিম্নরূপ যুক্ত করতে পারেন:
সুইফট
self.ref.child("users").child(user.uid).setValue(["username": username])
উদ্দেশ্য-C
[[[self.ref child:@"users"] child:authResult.user.uid] setValue:@{@"username": username}];
এইভাবে setValue
ব্যবহার করে যেকোন চাইল্ড নোড সহ নির্দিষ্ট স্থানে ডেটা ওভাররাইট করে। যাইহোক, আপনি এখনও সম্পূর্ণ বস্তুটি পুনরায় লেখা ছাড়াই একটি শিশুকে আপডেট করতে পারেন। আপনি যদি ব্যবহারকারীদের তাদের প্রোফাইল আপডেট করার অনুমতি দিতে চান তবে আপনি নিম্নরূপ ব্যবহারকারীর নাম আপডেট করতে পারেন:
সুইফট
self.ref.child("users/\(user.uid)/username").setValue(username)
উদ্দেশ্য-C
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];
ডেটা পড়ুন
মান ইভেন্টের জন্য শুনে ডেটা পড়ুন
একটি পাথে ডেটা পড়তে এবং পরিবর্তনের জন্য শুনতে, FIRDataEventTypeValue
ইভেন্টগুলি পর্যবেক্ষণ করতে FIRDatabaseReference
এর observeEventType:withBlock
ইভেন্টটাইপ ব্যবহার করুন।
ইভেন্টের ধরন | সাধারণ ব্যবহার |
---|---|
FIRDataEventTypeValue | একটি পথের সম্পূর্ণ বিষয়বস্তুর পরিবর্তনের জন্য পড়ুন এবং শুনুন। |
আপনি FIRDataEventTypeValue
ইভেন্টটি একটি প্রদত্ত পাথে ডেটা পড়ার জন্য ব্যবহার করতে পারেন, কারণ এটি ইভেন্টের সময় বিদ্যমান থাকে। এই পদ্ধতিটি একবার ট্রিগার করা হয় যখন শ্রোতা সংযুক্ত থাকে এবং আবার প্রতিবার ডেটা, যেকোন শিশু সহ, পরিবর্তন হয়। ইভেন্ট কলব্যাক একটি snapshot
পাস করা হয় যেখানে শিশু ডেটা সহ সেই অবস্থানের সমস্ত ডেটা রয়েছে৷ যদি কোন ডেটা না থাকে, আপনি যখন exists()
কল করবেন তখন স্ন্যাপশট false
হবে এবং যখন আপনি এর value
বৈশিষ্ট্যটি পড়বেন তখন nil
।
নিম্নলিখিত উদাহরণটি একটি সামাজিক ব্লগিং অ্যাপ্লিকেশন প্রদর্শন করে যা ডাটাবেস থেকে একটি পোস্টের বিবরণ পুনরুদ্ধার করে:
সুইফট
refHandle = postRef.observe(DataEventType.value, with: { snapshot in // ... })
উদ্দেশ্য-C
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { NSDictionary *postDict = snapshot.value; // ... }];
শ্রোতা একটি FIRDataSnapshot
পায় যাতে ডাটাবেসের নির্দিষ্ট স্থানে ইভেন্টের সময় তার value
সম্পত্তিতে ডেটা থাকে। আপনি মানগুলিকে যথাযথ নেটিভ টাইপের জন্য বরাদ্দ করতে পারেন, যেমন NSDictionary
। যদি অবস্থানে কোনো ডেটা বিদ্যমান না থাকে, তাহলে value
nil
।
একবার ডেটা পড়ুন
getData() ব্যবহার করে একবার পড়ুন
আপনার অ্যাপ অনলাইন হোক বা অফলাইন হোক ডাটাবেস সার্ভারের সাথে মিথস্ক্রিয়া পরিচালনা করার জন্য SDK ডিজাইন করা হয়েছে।
সাধারণত, ব্যাকএন্ড থেকে ডেটার আপডেটের বিজ্ঞপ্তি পেতে ডেটা পড়ার জন্য আপনার উপরে বর্ণিত মান ইভেন্ট কৌশলগুলি ব্যবহার করা উচিত। এই কৌশলগুলি আপনার ব্যবহার এবং বিলিং হ্রাস করে এবং আপনার ব্যবহারকারীদের অনলাইন এবং অফলাইনে যাওয়ার সাথে সাথে সেরা অভিজ্ঞতা দেওয়ার জন্য অপ্টিমাইজ করা হয়েছে৷
আপনার যদি শুধুমাত্র একবার ডেটার প্রয়োজন হয়, আপনি ডাটাবেস থেকে ডেটার একটি স্ন্যাপশট পেতে getData()
ব্যবহার করতে পারেন। কোনো কারণে getData()
সার্ভারের মান ফেরত দিতে অক্ষম হলে, ক্লায়েন্ট স্থানীয় স্টোরেজ ক্যাশে অনুসন্ধান করবে এবং মানটি এখনও খুঁজে না পাওয়া গেলে একটি ত্রুটি ফেরত দেবে।
নিম্নলিখিত উদাহরণটি দেখায় যে ডাটাবেস থেকে একবার ব্যবহারকারীর সর্বজনীন-মুখী ব্যবহারকারীর নাম পুনরুদ্ধার করা:
সুইফট
do { let snapshot = try await ref.child("users/\(uid)/username").getData() let userName = snapshot.value as? String ?? "Unknown" } catch { print(error) }
উদ্দেশ্য-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
ব্যবহার করতে পারেন।
এটি এমন ডেটার জন্য উপযোগী যেগুলি শুধুমাত্র একবার লোড করতে হবে এবং ঘন ঘন পরিবর্তন হবে বলে আশা করা হয় না বা সক্রিয় শোনার প্রয়োজন হয় না। উদাহরণস্বরূপ, পূর্ববর্তী উদাহরণগুলিতে ব্লগিং অ্যাপটি ব্যবহারকারীর প্রোফাইল লোড করতে এই পদ্ধতিটি ব্যবহার করে যখন তারা একটি নতুন পোস্ট লিখতে শুরু করে:
সুইফট
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) }
উদ্দেশ্য-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
কল করার সময়, আপনি কীটির জন্য একটি পাথ নির্দিষ্ট করে নিম্ন-স্তরের চাইল্ড মান আপডেট করতে পারেন। যদি আরও ভাল স্কেল করার জন্য একাধিক স্থানে ডেটা সংরক্ষণ করা হয়, আপনি ডেটা ফ্যান-আউট ব্যবহার করে সেই ডেটার সমস্ত দৃষ্টান্ত আপডেট করতে পারেন। উদাহরণস্বরূপ, একটি সামাজিক ব্লগিং অ্যাপ একটি পোস্ট তৈরি করতে এবং একই সাথে সাম্প্রতিক কার্যকলাপ ফিড এবং পোস্টিং ব্যবহারকারীর কার্যকলাপ ফিডে আপডেট করতে চাইতে পারে৷ এটি করার জন্য, ব্লগিং অ্যাপ্লিকেশন এই মত কোড ব্যবহার করে:
সুইফট
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)
উদ্দেশ্য-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];
এই উদাহরণটি /posts/$postid
এ সমস্ত ব্যবহারকারীর জন্য পোস্ট ধারণকারী নোডে একটি পোস্ট তৈরি করতে childByAutoId
ব্যবহার করে এবং একই সাথে getKey()
দিয়ে কী পুনরুদ্ধার করে। কীটি তারপর /user-posts/$userid/$postid
এ ব্যবহারকারীর পোস্টে একটি দ্বিতীয় এন্ট্রি তৈরি করতে ব্যবহার করা যেতে পারে।
এই পথগুলি ব্যবহার করে, আপনি updateChildValues
জন্য একক কলের মাধ্যমে JSON ট্রিতে একাধিক অবস্থানে একযোগে আপডেটগুলি সম্পাদন করতে পারেন, যেমন এই উদাহরণটি কীভাবে উভয় স্থানেই নতুন পোস্ট তৈরি করে৷ এইভাবে করা যুগপত আপডেটগুলি পরমাণু: হয় সমস্ত আপডেট সফল হয় বা সমস্ত আপডেট ব্যর্থ হয়।
একটি সমাপ্তি ব্লক যোগ করুন
আপনি যদি জানতে চান আপনার ডেটা কখন প্রতিশ্রুতিবদ্ধ হয়েছে, আপনি একটি সমাপ্তি ব্লক যোগ করতে পারেন। setValue
এবং updateChildValues
উভয়ই একটি ঐচ্ছিক সমাপ্তি ব্লক নেয় যা ডাটাবেসের সাথে লেখার প্রতিশ্রুতিবদ্ধ হলে বলা হয়। কোন ডেটা সংরক্ষণ করা হয়েছে এবং কোন ডেটা এখনও সিঙ্ক্রোনাইজ করা হচ্ছে তা ট্র্যাক রাখার জন্য এই শ্রোতা উপযোগী হতে পারে। যদি কলটি ব্যর্থ হয়, তাহলে শ্রোতাকে একটি ত্রুটি অবজেক্ট পাস করা হয় যা নির্দেশ করে যে কেন ব্যর্থতা ঘটেছে।
সুইফট
do { try await ref.child("users").child(user.uid).setValue(["username": username]) print("Data saved successfully!") } catch { print("Data could not be saved: \(error).") }
উদ্দেশ্য-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
উল্লেখ করেও মুছে ফেলতে পারেন। আপনি একটি একক API কলে একাধিক শিশু মুছে ফেলার জন্য updateChildValues
সাথে এই কৌশলটি ব্যবহার করতে পারেন।
শ্রোতাদের বিচ্ছিন্ন করুন
আপনি যখন ViewController
ছেড়ে যান তখন পর্যবেক্ষকরা স্বয়ংক্রিয়ভাবে ডেটা সিঙ্ক করা বন্ধ করে না। যদি একটি পর্যবেক্ষক সঠিকভাবে সরানো না হয়, তবে এটি স্থানীয় মেমরিতে ডেটা সিঙ্ক করতে থাকে। যখন একজন পর্যবেক্ষকের আর প্রয়োজন হয় না, তখন সংশ্লিষ্ট FIRDatabaseHandle
টিকে removeObserverWithHandle
পদ্ধতিতে পাস করে এটি সরিয়ে ফেলুন।
আপনি যখন একটি রেফারেন্সে একটি কলব্যাক ব্লক যোগ করেন, একটি FIRDatabaseHandle
ফেরত দেওয়া হয়। এই হ্যান্ডেলগুলি কলব্যাক ব্লক অপসারণ করতে ব্যবহার করা যেতে পারে।
যদি একটি ডাটাবেস রেফারেন্সে একাধিক শ্রোতা যোগ করা হয়, একটি ইভেন্ট উত্থাপিত হলে প্রতিটি শ্রোতাকে ডাকা হয়। সেই অবস্থানে ডেটা সিঙ্ক করা বন্ধ করার জন্য, আপনাকে removeAllObservers
পদ্ধতিতে কল করে একটি অবস্থানের সমস্ত পর্যবেক্ষককে সরিয়ে ফেলতে হবে।
removeObserverWithHandle
বা removeAllObservers
শ্রোতার উপর কল করা স্বয়ংক্রিয়ভাবে তার চাইল্ড নোডে নিবন্ধিত শ্রোতাদের সরিয়ে দেয় না; সেগুলি অপসারণ করার জন্য আপনাকে অবশ্যই সেই রেফারেন্স বা হ্যান্ডেলগুলির ট্র্যাক রাখতে হবে।
লেনদেন হিসাবে ডেটা সংরক্ষণ করুন
ক্রমবর্ধমান কাউন্টারগুলির মতো সমসাময়িক পরিবর্তন দ্বারা দূষিত হতে পারে এমন ডেটা নিয়ে কাজ করার সময়, আপনি একটি লেনদেন অপারেশন ব্যবহার করতে পারেন। আপনি এই অপারেশনটিকে দুটি আর্গুমেন্ট দেন: একটি আপডেট ফাংশন এবং একটি ঐচ্ছিক সমাপ্তি কলব্যাক৷ আপডেট ফাংশন একটি যুক্তি হিসাবে ডেটার বর্তমান অবস্থা নেয় এবং আপনি লিখতে চান এমন নতুন পছন্দসই অবস্থা প্রদান করে।
উদাহরণস্বরূপ, সোশ্যাল ব্লগিং অ্যাপের উদাহরণে, আপনি ব্যবহারকারীদের পোস্ট স্টার এবং আনস্টার করার অনুমতি দিতে পারেন এবং একটি পোস্ট কতগুলি স্টার পেয়েছে তার ট্র্যাক রাখতে পারেন:
সুইফট
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) } }
উদ্দেশ্য-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
। সার্ভার তার বর্তমান মানের সাথে প্রারম্ভিক মান তুলনা করে এবং মান মেলে বা প্রত্যাখ্যান করলে লেনদেন গ্রহণ করে। যদি লেনদেন প্রত্যাখ্যান করা হয়, সার্ভার ক্লায়েন্টকে বর্তমান মান ফেরত দেয়, যা আপডেট করা মান দিয়ে আবার লেনদেন চালায়। লেনদেন গৃহীত না হওয়া পর্যন্ত বা অনেক প্রচেষ্টা করা না হওয়া পর্যন্ত এটি পুনরাবৃত্তি হয়।
পারমাণবিক সার্ভার-সাইড বৃদ্ধি
উপরোক্ত ব্যবহারের ক্ষেত্রে আমরা ডাটাবেসে দুটি মান লিখছি: পোস্টে তারকাচিহ্নিত/তারামুক্তকারী ব্যবহারকারীর আইডি এবং বর্ধিত তারকা গণনা। যদি আমরা ইতিমধ্যেই জানি যে ব্যবহারকারী পোস্টটি তারকাচিহ্নিত করছে, আমরা একটি লেনদেনের পরিবর্তে একটি পারমাণবিক বৃদ্ধি অপারেশন ব্যবহার করতে পারি।
সুইফট
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)
উদ্দেশ্য-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 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
এর একটি উদাহরণ প্রয়োজন:
সুইফট
var ref: DatabaseReference! ref = Database.database().reference()
উদ্দেশ্য-C
@property (strong, nonatomic) FIRDatabaseReference *ref; self.ref = [[FIRDatabase database] reference];
ডেটা লিখুন
এই দস্তাবেজটি Firebase ডেটা পড়ার এবং লেখার মৌলিক বিষয়গুলিকে কভার করে৷
ফায়ারবেস ডেটা একটি Database
রেফারেন্সে লেখা হয় এবং রেফারেন্সের সাথে একটি অ্যাসিঙ্ক্রোনাস লিসেনার সংযুক্ত করে পুনরুদ্ধার করা হয়। শ্রোতা ডেটার প্রাথমিক অবস্থার জন্য একবার ট্রিগার হয় এবং আবার যে কোনও সময় ডেটা পরিবর্তন হয়।
মৌলিক লেখার ক্রিয়াকলাপ
মৌলিক লেখার ক্রিয়াকলাপের জন্য, আপনি একটি নির্দিষ্ট রেফারেন্সে ডেটা সংরক্ষণ করতে setValue
ব্যবহার করতে পারেন, সেই পথে বিদ্যমান ডেটা প্রতিস্থাপন করতে পারেন। আপনি এই পদ্ধতিটি ব্যবহার করতে পারেন:
- নিম্নরূপ উপলব্ধ JSON প্রকারের সাথে সঙ্গতিপূর্ণ পাস প্রকার:
-
NSString
-
NSNumber
-
NSDictionary
-
NSArray
-
উদাহরণস্বরূপ, আপনি setValue
সহ একজন ব্যবহারকারীকে নিম্নরূপ যুক্ত করতে পারেন:
সুইফট
self.ref.child("users").child(user.uid).setValue(["username": username])
উদ্দেশ্য-C
[[[self.ref child:@"users"] child:authResult.user.uid] setValue:@{@"username": username}];
এইভাবে setValue
ব্যবহার করে যেকোন চাইল্ড নোড সহ নির্দিষ্ট স্থানে ডেটা ওভাররাইট করে। যাইহোক, আপনি এখনও সম্পূর্ণ বস্তুটি পুনরায় লেখা ছাড়াই একটি শিশুকে আপডেট করতে পারেন। আপনি যদি ব্যবহারকারীদের তাদের প্রোফাইল আপডেট করার অনুমতি দিতে চান তবে আপনি নিম্নরূপ ব্যবহারকারীর নাম আপডেট করতে পারেন:
সুইফট
self.ref.child("users/\(user.uid)/username").setValue(username)
উদ্দেশ্য-C
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];
ডেটা পড়ুন
মান ইভেন্টের জন্য শুনে ডেটা পড়ুন
একটি পাথে ডেটা পড়তে এবং পরিবর্তনের জন্য শুনতে, FIRDataEventTypeValue
ইভেন্টগুলি পর্যবেক্ষণ করতে FIRDatabaseReference
এর observeEventType:withBlock
ইভেন্টটাইপ ব্যবহার করুন।
ইভেন্টের ধরন | সাধারণ ব্যবহার |
---|---|
FIRDataEventTypeValue | একটি পথের সম্পূর্ণ বিষয়বস্তুর পরিবর্তনের জন্য পড়ুন এবং শুনুন। |
আপনি FIRDataEventTypeValue
ইভেন্টটি একটি প্রদত্ত পাথে ডেটা পড়ার জন্য ব্যবহার করতে পারেন, কারণ এটি ইভেন্টের সময় বিদ্যমান থাকে। এই পদ্ধতিটি একবার ট্রিগার করা হয় যখন শ্রোতা সংযুক্ত থাকে এবং আবার প্রতিবার ডেটা, যেকোন শিশু সহ, পরিবর্তন হয়। ইভেন্ট কলব্যাক একটি snapshot
পাস করা হয় যেখানে শিশু ডেটা সহ সেই অবস্থানের সমস্ত ডেটা রয়েছে৷ যদি কোন ডেটা না থাকে, আপনি যখন exists()
কল করবেন তখন স্ন্যাপশট false
হবে এবং যখন আপনি এর value
বৈশিষ্ট্যটি পড়বেন তখন nil
।
নিম্নলিখিত উদাহরণটি একটি সামাজিক ব্লগিং অ্যাপ্লিকেশন প্রদর্শন করে যা ডাটাবেস থেকে একটি পোস্টের বিবরণ পুনরুদ্ধার করে:
সুইফট
refHandle = postRef.observe(DataEventType.value, with: { snapshot in // ... })
উদ্দেশ্য-C
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { NSDictionary *postDict = snapshot.value; // ... }];
শ্রোতা একটি FIRDataSnapshot
পায় যাতে ডাটাবেসের নির্দিষ্ট স্থানে ইভেন্টের সময় তার value
সম্পত্তিতে ডেটা থাকে। আপনি মানগুলিকে যথাযথ নেটিভ টাইপের জন্য বরাদ্দ করতে পারেন, যেমন NSDictionary
। যদি অবস্থানে কোনো ডেটা বিদ্যমান না থাকে, তাহলে value
nil
।
একবার ডেটা পড়ুন
getData() ব্যবহার করে একবার পড়ুন
আপনার অ্যাপ অনলাইন হোক বা অফলাইন হোক ডাটাবেস সার্ভারের সাথে মিথস্ক্রিয়া পরিচালনা করার জন্য SDK ডিজাইন করা হয়েছে।
সাধারণত, ব্যাকএন্ড থেকে ডেটার আপডেটের বিজ্ঞপ্তি পেতে ডেটা পড়ার জন্য আপনার উপরে বর্ণিত মান ইভেন্ট কৌশলগুলি ব্যবহার করা উচিত। এই কৌশলগুলি আপনার ব্যবহার এবং বিলিং হ্রাস করে এবং আপনার ব্যবহারকারীদের অনলাইন এবং অফলাইনে যাওয়ার সাথে সাথে সেরা অভিজ্ঞতা দেওয়ার জন্য অপ্টিমাইজ করা হয়েছে৷
আপনার যদি শুধুমাত্র একবার ডেটার প্রয়োজন হয়, আপনি ডাটাবেস থেকে ডেটার একটি স্ন্যাপশট পেতে getData()
ব্যবহার করতে পারেন। কোনো কারণে getData()
সার্ভারের মান ফেরত দিতে অক্ষম হলে, ক্লায়েন্ট স্থানীয় স্টোরেজ ক্যাশে অনুসন্ধান করবে এবং মানটি এখনও খুঁজে না পাওয়া গেলে একটি ত্রুটি ফেরত দেবে।
নিম্নলিখিত উদাহরণটি দেখায় যে ডাটাবেস থেকে একবার ব্যবহারকারীর সর্বজনীন-মুখী ব্যবহারকারীর নাম পুনরুদ্ধার করা:
সুইফট
do { let snapshot = try await ref.child("users/\(uid)/username").getData() let userName = snapshot.value as? String ?? "Unknown" } catch { print(error) }
উদ্দেশ্য-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
ব্যবহার করতে পারেন।
এটি এমন ডেটার জন্য উপযোগী যেগুলি শুধুমাত্র একবার লোড করতে হবে এবং ঘন ঘন পরিবর্তন হবে বলে আশা করা হয় না বা সক্রিয় শোনার প্রয়োজন হয় না। উদাহরণস্বরূপ, পূর্ববর্তী উদাহরণগুলিতে ব্লগিং অ্যাপটি ব্যবহারকারীর প্রোফাইল লোড করতে এই পদ্ধতিটি ব্যবহার করে যখন তারা একটি নতুন পোস্ট লিখতে শুরু করে:
সুইফট
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) }
উদ্দেশ্য-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
কল করার সময়, আপনি কীটির জন্য একটি পাথ নির্দিষ্ট করে নিম্ন-স্তরের চাইল্ড মান আপডেট করতে পারেন। যদি আরও ভাল স্কেল করার জন্য একাধিক স্থানে ডেটা সংরক্ষণ করা হয়, আপনি ডেটা ফ্যান-আউট ব্যবহার করে সেই ডেটার সমস্ত দৃষ্টান্ত আপডেট করতে পারেন। উদাহরণস্বরূপ, একটি সামাজিক ব্লগিং অ্যাপ একটি পোস্ট তৈরি করতে এবং একই সাথে সাম্প্রতিক কার্যকলাপ ফিড এবং পোস্টিং ব্যবহারকারীর কার্যকলাপ ফিডে আপডেট করতে চাইতে পারে৷ এটি করার জন্য, ব্লগিং অ্যাপ্লিকেশন এই মত কোড ব্যবহার করে:
সুইফট
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)
উদ্দেশ্য-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];
এই উদাহরণটি /posts/$postid
এ সমস্ত ব্যবহারকারীর জন্য পোস্ট ধারণকারী নোডে একটি পোস্ট তৈরি করতে childByAutoId
ব্যবহার করে এবং একই সাথে getKey()
দিয়ে কী পুনরুদ্ধার করে। কীটি তারপর /user-posts/$userid/$postid
এ ব্যবহারকারীর পোস্টে একটি দ্বিতীয় এন্ট্রি তৈরি করতে ব্যবহার করা যেতে পারে।
এই পথগুলি ব্যবহার করে, আপনি updateChildValues
জন্য একক কলের মাধ্যমে JSON ট্রিতে একাধিক অবস্থানে একযোগে আপডেটগুলি সম্পাদন করতে পারেন, যেমন এই উদাহরণটি কীভাবে উভয় স্থানেই নতুন পোস্ট তৈরি করে৷ এইভাবে করা যুগপত আপডেটগুলি পরমাণু: হয় সমস্ত আপডেট সফল হয় বা সমস্ত আপডেট ব্যর্থ হয়।
একটি সমাপ্তি ব্লক যোগ করুন
আপনি যদি জানতে চান আপনার ডেটা কখন প্রতিশ্রুতিবদ্ধ হয়েছে, আপনি একটি সমাপ্তি ব্লক যোগ করতে পারেন। setValue
এবং updateChildValues
উভয়ই একটি ঐচ্ছিক সমাপ্তি ব্লক নেয় যা ডাটাবেসের সাথে লেখার প্রতিশ্রুতিবদ্ধ হলে বলা হয়। কোন ডেটা সংরক্ষণ করা হয়েছে এবং কোন ডেটা এখনও সিঙ্ক্রোনাইজ করা হচ্ছে তা ট্র্যাক রাখার জন্য এই শ্রোতা উপযোগী হতে পারে। যদি কলটি ব্যর্থ হয়, তাহলে শ্রোতাকে একটি ত্রুটি অবজেক্ট পাস করা হয় যা নির্দেশ করে যে কেন ব্যর্থতা ঘটেছে।
সুইফট
do { try await ref.child("users").child(user.uid).setValue(["username": username]) print("Data saved successfully!") } catch { print("Data could not be saved: \(error).") }
উদ্দেশ্য-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
উল্লেখ করেও মুছে ফেলতে পারেন। আপনি একটি একক API কলে একাধিক শিশু মুছে ফেলার জন্য updateChildValues
সাথে এই কৌশলটি ব্যবহার করতে পারেন।
শ্রোতাদের বিচ্ছিন্ন করুন
আপনি যখন ViewController
ছেড়ে যান তখন পর্যবেক্ষকরা স্বয়ংক্রিয়ভাবে ডেটা সিঙ্ক করা বন্ধ করে না। যদি একটি পর্যবেক্ষক সঠিকভাবে সরানো না হয়, তবে এটি স্থানীয় মেমরিতে ডেটা সিঙ্ক করতে থাকে। যখন একজন পর্যবেক্ষকের আর প্রয়োজন হয় না, তখন সংশ্লিষ্ট FIRDatabaseHandle
টিকে removeObserverWithHandle
পদ্ধতিতে পাস করে এটি সরিয়ে ফেলুন।
আপনি যখন একটি রেফারেন্সে একটি কলব্যাক ব্লক যোগ করেন, একটি FIRDatabaseHandle
ফেরত দেওয়া হয়। এই হ্যান্ডেলগুলি কলব্যাক ব্লক অপসারণ করতে ব্যবহার করা যেতে পারে।
যদি একটি ডাটাবেস রেফারেন্সে একাধিক শ্রোতা যোগ করা হয়, একটি ইভেন্ট উত্থাপিত হলে প্রতিটি শ্রোতাকে ডাকা হয়। সেই অবস্থানে ডেটা সিঙ্ক করা বন্ধ করার জন্য, আপনাকে removeAllObservers
পদ্ধতিতে কল করে একটি অবস্থানের সমস্ত পর্যবেক্ষককে সরিয়ে ফেলতে হবে।
removeObserverWithHandle
বা removeAllObservers
শ্রোতার উপর কল করা স্বয়ংক্রিয়ভাবে তার চাইল্ড নোডে নিবন্ধিত শ্রোতাদের সরিয়ে দেয় না; সেগুলি অপসারণ করার জন্য আপনাকে অবশ্যই সেই রেফারেন্স বা হ্যান্ডেলগুলির ট্র্যাক রাখতে হবে।
লেনদেন হিসাবে ডেটা সংরক্ষণ করুন
ক্রমবর্ধমান কাউন্টারগুলির মতো সমসাময়িক পরিবর্তন দ্বারা দূষিত হতে পারে এমন ডেটা নিয়ে কাজ করার সময়, আপনি একটি লেনদেন অপারেশন ব্যবহার করতে পারেন। আপনি এই অপারেশনটিকে দুটি আর্গুমেন্ট দেন: একটি আপডেট ফাংশন এবং একটি ঐচ্ছিক সমাপ্তি কলব্যাক৷ আপডেট ফাংশন একটি যুক্তি হিসাবে ডেটার বর্তমান অবস্থা নেয় এবং আপনি লিখতে চান এমন নতুন পছন্দসই অবস্থা প্রদান করে।
উদাহরণস্বরূপ, সোশ্যাল ব্লগিং অ্যাপের উদাহরণে, আপনি ব্যবহারকারীদের পোস্ট স্টার এবং আনস্টার করার অনুমতি দিতে পারেন এবং একটি পোস্ট কতগুলি স্টার পেয়েছে তার ট্র্যাক রাখতে পারেন:
সুইফট
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) } }
উদ্দেশ্য-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
। সার্ভার তার বর্তমান মানের সাথে প্রারম্ভিক মান তুলনা করে এবং মান মেলে বা প্রত্যাখ্যান করলে লেনদেন গ্রহণ করে। যদি লেনদেন প্রত্যাখ্যান করা হয়, সার্ভার ক্লায়েন্টকে বর্তমান মান ফেরত দেয়, যা আপডেট করা মান দিয়ে আবার লেনদেন চালায়। লেনদেন গৃহীত না হওয়া পর্যন্ত বা অনেক প্রচেষ্টা করা না হওয়া পর্যন্ত এটি পুনরাবৃত্তি হয়।
পারমাণবিক সার্ভার-সাইড বৃদ্ধি
উপরোক্ত ব্যবহারের ক্ষেত্রে আমরা ডাটাবেসে দুটি মান লিখছি: পোস্টে তারকাচিহ্নিত/তারামুক্তকারী ব্যবহারকারীর আইডি এবং বর্ধিত তারকা গণনা। যদি আমরা ইতিমধ্যেই জানি যে ব্যবহারকারী পোস্টটি তারকাচিহ্নিত করছে, আমরা একটি লেনদেনের পরিবর্তে একটি পারমাণবিক বৃদ্ধি অপারেশন ব্যবহার করতে পারি।
সুইফট
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)
উদ্দেশ্য-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 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
এর একটি উদাহরণ প্রয়োজন:
সুইফট
var ref: DatabaseReference! ref = Database.database().reference()
উদ্দেশ্য-C
@property (strong, nonatomic) FIRDatabaseReference *ref; self.ref = [[FIRDatabase database] reference];
ডেটা লিখুন
এই দস্তাবেজটি Firebase ডেটা পড়ার এবং লেখার মৌলিক বিষয়গুলিকে কভার করে৷
ফায়ারবেস ডেটা একটি Database
রেফারেন্সে লেখা হয় এবং রেফারেন্সের সাথে একটি অ্যাসিঙ্ক্রোনাস লিসেনার সংযুক্ত করে পুনরুদ্ধার করা হয়। শ্রোতা ডেটার প্রাথমিক অবস্থার জন্য একবার ট্রিগার হয় এবং আবার যে কোনও সময় ডেটা পরিবর্তন হয়।
মৌলিক লেখার ক্রিয়াকলাপ
মৌলিক লেখার ক্রিয়াকলাপের জন্য, আপনি একটি নির্দিষ্ট রেফারেন্সে ডেটা সংরক্ষণ করতে setValue
ব্যবহার করতে পারেন, সেই পথে বিদ্যমান ডেটা প্রতিস্থাপন করতে পারেন। আপনি এই পদ্ধতিটি ব্যবহার করতে পারেন:
- নিম্নরূপ উপলব্ধ JSON প্রকারের সাথে সঙ্গতিপূর্ণ পাস প্রকার:
-
NSString
-
NSNumber
-
NSDictionary
-
NSArray
-
উদাহরণস্বরূপ, আপনি setValue
সহ একজন ব্যবহারকারীকে নিম্নরূপ যুক্ত করতে পারেন:
সুইফট
self.ref.child("users").child(user.uid).setValue(["username": username])
উদ্দেশ্য-C
[[[self.ref child:@"users"] child:authResult.user.uid] setValue:@{@"username": username}];
এইভাবে setValue
ব্যবহার করে যেকোন চাইল্ড নোড সহ নির্দিষ্ট স্থানে ডেটা ওভাররাইট করে। যাইহোক, আপনি এখনও সম্পূর্ণ বস্তুটি পুনরায় লেখা ছাড়াই একটি শিশুকে আপডেট করতে পারেন। আপনি যদি ব্যবহারকারীদের তাদের প্রোফাইল আপডেট করার অনুমতি দিতে চান তবে আপনি নিম্নরূপ ব্যবহারকারীর নাম আপডেট করতে পারেন:
সুইফট
self.ref.child("users/\(user.uid)/username").setValue(username)
উদ্দেশ্য-C
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];
ডেটা পড়ুন
মান ইভেন্টের জন্য শুনে ডেটা পড়ুন
একটি পাথে ডেটা পড়তে এবং পরিবর্তনের জন্য শুনতে, FIRDataEventTypeValue
ইভেন্টগুলি পর্যবেক্ষণ করতে FIRDatabaseReference
এর observeEventType:withBlock
ইভেন্টটাইপ ব্যবহার করুন।
ইভেন্টের ধরন | সাধারণ ব্যবহার |
---|---|
FIRDataEventTypeValue | একটি পথের সম্পূর্ণ বিষয়বস্তুর পরিবর্তনের জন্য পড়ুন এবং শুনুন। |
আপনি FIRDataEventTypeValue
ইভেন্টটি একটি প্রদত্ত পাথে ডেটা পড়ার জন্য ব্যবহার করতে পারেন, কারণ এটি ইভেন্টের সময় বিদ্যমান থাকে। এই পদ্ধতিটি একবার ট্রিগার করা হয় যখন শ্রোতা সংযুক্ত থাকে এবং আবার প্রতিবার ডেটা, যেকোন শিশু সহ, পরিবর্তন হয়। ইভেন্ট কলব্যাক একটি snapshot
পাস করা হয় যেখানে শিশু ডেটা সহ সেই অবস্থানের সমস্ত ডেটা রয়েছে৷ যদি কোন ডেটা না থাকে, আপনি যখন exists()
কল করবেন তখন স্ন্যাপশট false
হবে এবং যখন আপনি এর value
বৈশিষ্ট্যটি পড়বেন তখন nil
।
নিম্নলিখিত উদাহরণটি একটি সামাজিক ব্লগিং অ্যাপ্লিকেশন প্রদর্শন করে যা ডাটাবেস থেকে একটি পোস্টের বিবরণ পুনরুদ্ধার করে:
সুইফট
refHandle = postRef.observe(DataEventType.value, with: { snapshot in // ... })
উদ্দেশ্য-C
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { NSDictionary *postDict = snapshot.value; // ... }];
শ্রোতা একটি FIRDataSnapshot
পায় যাতে ডাটাবেসের নির্দিষ্ট স্থানে ইভেন্টের সময় তার value
সম্পত্তিতে ডেটা থাকে। আপনি মানগুলিকে যথাযথ নেটিভ টাইপের জন্য বরাদ্দ করতে পারেন, যেমন NSDictionary
। যদি অবস্থানে কোনো ডেটা বিদ্যমান না থাকে, তাহলে value
nil
।
একবার ডেটা পড়ুন
getData() ব্যবহার করে একবার পড়ুন
আপনার অ্যাপ অনলাইন হোক বা অফলাইন হোক ডাটাবেস সার্ভারের সাথে মিথস্ক্রিয়া পরিচালনা করার জন্য SDK ডিজাইন করা হয়েছে।
সাধারণত, ব্যাকএন্ড থেকে ডেটার আপডেটের বিজ্ঞপ্তি পেতে ডেটা পড়ার জন্য আপনার উপরে বর্ণিত মান ইভেন্ট কৌশলগুলি ব্যবহার করা উচিত। এই কৌশলগুলি আপনার ব্যবহার এবং বিলিং হ্রাস করে এবং আপনার ব্যবহারকারীদের অনলাইন এবং অফলাইনে যাওয়ার সাথে সাথে সেরা অভিজ্ঞতা দেওয়ার জন্য অপ্টিমাইজ করা হয়েছে৷
আপনার যদি শুধুমাত্র একবার ডেটার প্রয়োজন হয়, আপনি ডাটাবেস থেকে ডেটার একটি স্ন্যাপশট পেতে getData()
ব্যবহার করতে পারেন। কোনো কারণে getData()
সার্ভারের মান ফেরত দিতে অক্ষম হলে, ক্লায়েন্ট স্থানীয় স্টোরেজ ক্যাশে অনুসন্ধান করবে এবং মানটি এখনও খুঁজে না পাওয়া গেলে একটি ত্রুটি ফেরত দেবে।
নিম্নলিখিত উদাহরণটি দেখায় যে ডাটাবেস থেকে একবার ব্যবহারকারীর সর্বজনীন-মুখী ব্যবহারকারীর নাম পুনরুদ্ধার করা:
সুইফট
do { let snapshot = try await ref.child("users/\(uid)/username").getData() let userName = snapshot.value as? String ?? "Unknown" } catch { print(error) }
উদ্দেশ্য-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
ব্যবহার করতে পারেন।
এটি এমন ডেটার জন্য উপযোগী যেগুলি শুধুমাত্র একবার লোড করতে হবে এবং ঘন ঘন পরিবর্তন হবে বলে আশা করা হয় না বা সক্রিয় শোনার প্রয়োজন হয় না। উদাহরণস্বরূপ, পূর্ববর্তী উদাহরণগুলিতে ব্লগিং অ্যাপটি ব্যবহারকারীর প্রোফাইল লোড করতে এই পদ্ধতিটি ব্যবহার করে যখন তারা একটি নতুন পোস্ট লিখতে শুরু করে:
সুইফট
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) }
উদ্দেশ্য-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
কল করার সময়, আপনি কীটির জন্য একটি পাথ নির্দিষ্ট করে নিম্ন-স্তরের চাইল্ড মান আপডেট করতে পারেন। যদি আরও ভাল স্কেল করার জন্য একাধিক স্থানে ডেটা সংরক্ষণ করা হয়, আপনি ডেটা ফ্যান-আউট ব্যবহার করে সেই ডেটার সমস্ত দৃষ্টান্ত আপডেট করতে পারেন। উদাহরণস্বরূপ, একটি সামাজিক ব্লগিং অ্যাপ একটি পোস্ট তৈরি করতে এবং একই সাথে সাম্প্রতিক কার্যকলাপ ফিড এবং পোস্টিং ব্যবহারকারীর কার্যকলাপ ফিডে আপডেট করতে চাইতে পারে৷ এটি করার জন্য, ব্লগিং অ্যাপ্লিকেশন এই মত কোড ব্যবহার করে:
সুইফট
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)
উদ্দেশ্য-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];
এই উদাহরণটি /posts/$postid
এ সমস্ত ব্যবহারকারীর জন্য পোস্ট ধারণকারী নোডে একটি পোস্ট তৈরি করতে childByAutoId
ব্যবহার করে এবং একই সাথে getKey()
দিয়ে কী পুনরুদ্ধার করে। কীটি তারপর /user-posts/$userid/$postid
এ ব্যবহারকারীর পোস্টে একটি দ্বিতীয় এন্ট্রি তৈরি করতে ব্যবহার করা যেতে পারে।
এই পথগুলি ব্যবহার করে, আপনি updateChildValues
জন্য একক কলের মাধ্যমে JSON ট্রিতে একাধিক অবস্থানে একযোগে আপডেটগুলি সম্পাদন করতে পারেন, যেমন এই উদাহরণটি কীভাবে উভয় স্থানেই নতুন পোস্ট তৈরি করে৷ এইভাবে করা যুগপত আপডেটগুলি পরমাণু: হয় সমস্ত আপডেট সফল হয় বা সমস্ত আপডেট ব্যর্থ হয়।
একটি সমাপ্তি ব্লক যোগ করুন
আপনি যদি জানতে চান আপনার ডেটা কখন প্রতিশ্রুতিবদ্ধ হয়েছে, আপনি একটি সমাপ্তি ব্লক যোগ করতে পারেন। setValue
এবং updateChildValues
উভয়ই একটি ঐচ্ছিক সমাপ্তি ব্লক নেয় যা ডাটাবেসের সাথে লেখার প্রতিশ্রুতিবদ্ধ হলে বলা হয়। কোন ডেটা সংরক্ষণ করা হয়েছে এবং কোন ডেটা এখনও সিঙ্ক্রোনাইজ করা হচ্ছে তা ট্র্যাক রাখার জন্য এই শ্রোতা উপযোগী হতে পারে। কলটি ব্যর্থ হলে, শ্রোতাকে একটি ত্রুটি অবজেক্ট পাস করা হয় যা নির্দেশ করে যে কেন ব্যর্থতা ঘটেছে।
সুইফট
do { try await ref.child("users").child(user.uid).setValue(["username": username]) print("Data saved successfully!") } catch { print("Data could not be saved: \(error).") }
উদ্দেশ্য-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
উল্লেখ করেও মুছে ফেলতে পারেন। আপনি একটি একক API কলে একাধিক শিশু মুছে ফেলার জন্য updateChildValues
সাথে এই কৌশলটি ব্যবহার করতে পারেন।
শ্রোতাদের বিচ্ছিন্ন করুন
যখন আপনি একটি ViewController
ছেড়ে যান তখন পর্যবেক্ষকরা স্বয়ংক্রিয়ভাবে ডেটা সিঙ্ক করা বন্ধ করে না। যদি একটি পর্যবেক্ষক সঠিকভাবে সরানো না হয়, তবে এটি স্থানীয় মেমরিতে ডেটা সিঙ্ক করতে থাকে। যখন একজন পর্যবেক্ষকের আর প্রয়োজন হয় না, তখন সংশ্লিষ্ট FIRDatabaseHandle
টিকে removeObserverWithHandle
পদ্ধতিতে পাস করে এটি সরিয়ে ফেলুন।
আপনি যখন একটি রেফারেন্সে একটি কলব্যাক ব্লক যোগ করেন, তখন একটি FIRDatabaseHandle
ফেরত দেওয়া হয়। এই হ্যান্ডেলগুলি কলব্যাক ব্লক অপসারণ করতে ব্যবহার করা যেতে পারে।
যদি একাধিক শ্রোতা কোনও ডাটাবেস রেফারেন্সে যুক্ত করা হয় তবে প্রতিটি শ্রোতাকে যখন কোনও ইভেন্ট উত্থাপিত হয় তখন ডাকা হয়। সেই স্থানে ডেটা সিঙ্ক করা বন্ধ করার জন্য, আপনাকে অবশ্যই সমস্ত পর্যবেক্ষককে কোনও স্থানে অপসারণ করতে হবে removeAllObservers
পদ্ধতিতে কল করে।
শ্রোতার উপর removeObserverWithHandle
বা removeAllObservers
কল করা স্বয়ংক্রিয়ভাবে তার শিশু নোডগুলিতে নিবন্ধিত শ্রোতাদের অপসারণ করে না; এগুলি অপসারণের জন্য আপনাকে অবশ্যই সেই রেফারেন্স বা হ্যান্ডলগুলির উপর নজর রাখতে হবে।
লেনদেন হিসাবে ডেটা সংরক্ষণ করুন
ইনক্রিমেন্টাল কাউন্টারগুলির মতো সমবর্তী পরিবর্তনগুলি দ্বারা দূষিত হতে পারে এমন ডেটাগুলির সাথে কাজ করার সময়, আপনি একটি লেনদেন অপারেশন ব্যবহার করতে পারেন। আপনি এই অপারেশন দুটি যুক্তি দিন: একটি আপডেট ফাংশন এবং একটি al চ্ছিক সমাপ্তি কলব্যাক। আপডেট ফাংশনটি ডেটাটির বর্তমান অবস্থাটিকে যুক্তি হিসাবে গ্রহণ করে এবং আপনি লিখতে চান এমন নতুন কাঙ্ক্ষিত রাষ্ট্রকে ফিরিয়ে দেয়।
উদাহরণস্বরূপ, উদাহরণস্বরূপ সামাজিক ব্লগিং অ্যাপে, আপনি ব্যবহারকারীদের স্টার এবং আনস্টার পোস্টগুলি করতে এবং কোনও পোস্ট নিম্নলিখিত হিসাবে কতগুলি তারা পেয়েছেন তা ট্র্যাক রাখতে অনুমতি দিতে পারেন:
সুইফট
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) } }
উদ্দেশ্য-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
। সার্ভারটি তার বর্তমান মানের তুলনায় প্রাথমিক মানটির সাথে তুলনা করে এবং মানগুলি মেলে, বা এটি প্রত্যাখ্যান করলে লেনদেনটি গ্রহণ করে। যদি লেনদেনটি প্রত্যাখ্যান করা হয় তবে সার্ভারটি ক্লায়েন্টের কাছে বর্তমান মানটি ফেরত দেয়, যা আপডেট মান সহ আবার লেনদেন চালায়। লেনদেনটি গৃহীত না হওয়া বা অনেকগুলি প্রচেষ্টা না হওয়া পর্যন্ত এটি পুনরাবৃত্তি করে।
পারমাণবিক সার্ভার-সাইড ইনক্রিমেন্টস
উপরোক্ত ব্যবহারের ক্ষেত্রে আমরা ডাটাবেসে দুটি মান লিখছি: ব্যবহারকারী যিনি পোস্টটি স্টার/আনস্টারস এবং ইনক্রিমেন্টেড স্টার কাউন্টের আইডি। যদি আমরা ইতিমধ্যে জানি যে ব্যবহারকারী পোস্টটি অভিনয় করছেন, আমরা লেনদেনের পরিবর্তে একটি পারমাণবিক ইনক্রিমেন্ট অপারেশন ব্যবহার করতে পারি।
সুইফট
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)
উদ্দেশ্য-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];
এই কোডটি কোনও লেনদেন অপারেশন ব্যবহার করে না, সুতরাং কোনও বিরোধী আপডেট থাকলে এটি স্বয়ংক্রিয়ভাবে পুনরায় রান পায় না। তবে, যেহেতু ইনক্রিমেন্ট অপারেশনটি সরাসরি ডাটাবেস সার্ভারে ঘটে, তাই কোনও বিরোধের সম্ভাবনা নেই।
আপনি যদি অ্যাপ্লিকেশন-নির্দিষ্ট দ্বন্দ্বগুলি সনাক্ত এবং প্রত্যাখ্যান করতে চান, যেমন কোনও ব্যবহারকারী যেমন তারা ইতিমধ্যে অভিনয় করেছেন এমন একটি পোস্ট অভিনয় করেছেন, আপনার সেই ব্যবহারের ক্ষেত্রে কাস্টম সুরক্ষা বিধি লিখতে হবে।
অফলাইনে ডেটা নিয়ে কাজ করুন
যদি কোনও ক্লায়েন্ট তার নেটওয়ার্ক সংযোগটি হারিয়ে ফেলে তবে আপনার অ্যাপ্লিকেশনটি সঠিকভাবে কাজ করা চালিয়ে যাবে।
ফায়ারবেস ডাটাবেসের সাথে সংযুক্ত প্রতিটি ক্লায়েন্ট কোনও সক্রিয় ডেটার নিজস্ব অভ্যন্তরীণ সংস্করণ বজায় রাখে। যখন ডেটা লেখা হয়, এটি প্রথমে এই স্থানীয় সংস্করণে লেখা হয়। ফায়ারবেস ক্লায়েন্ট তারপরে সেই ডেটা দূরবর্তী ডাটাবেস সার্ভারগুলির সাথে এবং অন্যান্য ক্লায়েন্টদের সাথে "সেরা-প্রচেষ্টা" ভিত্তিতে সিঙ্ক্রোনাইজ করে।
ফলস্বরূপ, সার্ভারে কোনও ডেটা লেখার আগে সমস্ত ডাটাবেস স্থানীয় ইভেন্টগুলি ট্রিগার করে। এর অর্থ আপনার অ্যাপ্লিকেশনটি নেটওয়ার্কের বিলম্ব বা সংযোগ নির্বিশেষে প্রতিক্রিয়াশীল থেকে যায়।
সংযোগটি পুনরায় প্রতিষ্ঠিত হয়ে গেলে, আপনার অ্যাপ্লিকেশনটি ইভেন্টগুলির উপযুক্ত সেটটি গ্রহণ করে যাতে ক্লায়েন্ট কোনও কাস্টম কোড না লিখে বর্তমান সার্ভার স্টেটের সাথে সিঙ্ক করে।
আমরা অনলাইন এবং অফলাইন ক্ষমতা সম্পর্কে আরও শিখতে অফলাইন আচরণ সম্পর্কে আরও কথা বলব।