সুইফটের কোডেবল এপিআই, সুইফট 4-এ প্রবর্তন করা হয়েছে, আমাদেরকে কম্পাইলারের শক্তিকে কাজে লাগাতে সাহায্য করে যাতে করে ক্রমিক বিন্যাস থেকে সুইফট প্রকারে ডেটা ম্যাপ করা সহজ হয়।
আপনি একটি ওয়েব API থেকে আপনার অ্যাপের ডেটা মডেলে ডেটা ম্যাপ করতে কোডেবল ব্যবহার করতে পারেন (এবং এর বিপরীতে), কিন্তু এটি তার চেয়ে অনেক বেশি নমনীয়।
এই নির্দেশিকায়, আমরা Cloud Firestore থেকে সুইফট প্রকারে ডেটা ম্যাপ করতে কোডেবল কীভাবে ব্যবহার করা যেতে পারে তা দেখতে যাচ্ছি এবং এর বিপরীতে।
Cloud Firestore থেকে একটি নথি আনার সময়, আপনার অ্যাপটি কী/মান জোড়ার একটি অভিধান পাবে (অথবা অভিধানের একটি অ্যারে, যদি আপনি একাধিক নথি ফিরিয়ে দেওয়ার ক্রিয়াকলাপগুলির একটি ব্যবহার করেন)।
এখন, আপনি অবশ্যই সুইফটে সরাসরি অভিধান ব্যবহার করা চালিয়ে যেতে পারেন, এবং তারা কিছু দুর্দান্ত নমনীয়তা অফার করে যা আপনার ব্যবহারের ক্ষেত্রে যা প্রয়োজন তা হতে পারে। যাইহোক, এই পদ্ধতিটি টাইপ নিরাপদ নয় এবং বৈশিষ্ট্যের নামের ভুল বানান করে বা আপনার দল গত সপ্তাহে সেই উত্তেজনাপূর্ণ নতুন বৈশিষ্ট্যটি পাঠানোর সময় যোগ করা নতুন বৈশিষ্ট্য ম্যাপ করতে ভুলে গিয়ে হার্ড-টু-ট্র্যাক-ডাউন বাগগুলি প্রবর্তন করা সহজ।
অতীতে, অনেক বিকাশকারী একটি সাধারণ ম্যাপিং স্তর প্রয়োগ করে এই ত্রুটিগুলিকে ঘিরে কাজ করেছে যা তাদের সুইফ্ট প্রকারের অভিধানগুলিকে ম্যাপ করতে দেয়৷ কিন্তু আবার, এই বাস্তবায়নের বেশিরভাগই Cloud Firestore ডকুমেন্ট এবং আপনার অ্যাপের ডেটা মডেলের সংশ্লিষ্ট প্রকারের মধ্যে ম্যাপিং ম্যানুয়ালি নির্দিষ্ট করার উপর ভিত্তি করে।
Cloud Firestore সাহায্যে সুইফ্টের কোডেবল API-এর জন্য এটি অনেক সহজ হয়ে যায়:
- আপনাকে আর ম্যানুয়ালি কোনো ম্যাপিং কোড বাস্তবায়ন করতে হবে না।
- বিভিন্ন নামের সাথে বৈশিষ্ট্যগুলি কীভাবে ম্যাপ করা যায় তা সংজ্ঞায়িত করা সহজ।
- এটিতে সুইফটের অনেক ধরণের জন্য অন্তর্নির্মিত সমর্থন রয়েছে।
- এবং কাস্টম ধরনের ম্যাপিংয়ের জন্য সমর্থন যোগ করা সহজ।
- সর্বোত্তম: সাধারণ ডেটা মডেলগুলির জন্য, আপনাকে কোনও ম্যাপিং কোড লিখতে হবে না।
ম্যাপিং ডেটা
Cloud Firestore নথিতে ডেটা সঞ্চয় করে যা মানগুলির কী ম্যাপ করে। একটি স্বতন্ত্র নথি থেকে ডেটা আনার জন্য, আমরা DocumentSnapshot.data()
কল করতে পারি, যা একটি অভিধান প্রদান করে যা ফিল্ডের নাম ম্যাপিং করে Any
: func data() -> [String : Any]?
.
এর মানে আমরা প্রতিটি পৃথক ক্ষেত্রে অ্যাক্সেস করতে সুইফটের সাবস্ক্রিপ্ট সিনট্যাক্স ব্যবহার করতে পারি।
import FirebaseFirestore
#warning("DO NOT MAP YOUR DOCUMENTS MANUALLY. USE CODABLE INSTEAD.")
func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument { document, error in
if let error = error as NSError? {
self.errorMessage = "Error getting document: \(error.localizedDescription)"
}
else {
if let document = document {
let id = document.documentID
let data = document.data()
let title = data?["title"] as? String ?? ""
let numberOfPages = data?["numberOfPages"] as? Int ?? 0
let author = data?["author"] as? String ?? ""
self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author)
}
}
}
}
যদিও এটি সহজবোধ্য এবং প্রয়োগ করা সহজ বলে মনে হতে পারে, এই কোডটি ভঙ্গুর, বজায় রাখা কঠিন এবং ত্রুটি-প্রবণ।
আপনি দেখতে পাচ্ছেন, আমরা ডকুমেন্ট ফিল্ডের ডেটা প্রকার সম্পর্কে অনুমান করছি। এইগুলি সঠিক হতে পারে বা নাও হতে পারে।
মনে রাখবেন, যেহেতু কোনও স্কিমা নেই, আপনি সহজেই সংগ্রহে একটি নতুন নথি যোগ করতে পারেন এবং একটি ক্ষেত্রের জন্য একটি ভিন্ন প্রকার চয়ন করতে পারেন৷ আপনি ঘটনাক্রমে numberOfPages
ক্ষেত্রের জন্য স্ট্রিং বেছে নিতে পারেন, যার ফলে ম্যাপিং-এর সমস্যা খুঁজে পাওয়া কঠিন হবে। এছাড়াও, যখনই একটি নতুন ক্ষেত্র যোগ করা হবে তখনই আপনাকে আপনার ম্যাপিং কোড আপডেট করতে হবে, যা বরং কষ্টকর।
এবং আসুন আমরা ভুলে যাই না যে আমরা সুইফটের শক্তিশালী টাইপ সিস্টেমের সুবিধা নিচ্ছি না, যা Book
প্রতিটি বৈশিষ্ট্যের জন্য সঠিক টাইপ জানে।
কোডেবল কি, যাইহোক?
অ্যাপলের ডকুমেন্টেশন অনুসারে, কোডেবল হল "একটি প্রকার যা নিজেকে একটি বাহ্যিক উপস্থাপনায় এবং এর বাইরে রূপান্তর করতে পারে।" আসলে, কোডেবল হল এনকোডেবল এবং ডিকোডেবল প্রোটোকলের জন্য একটি টাইপ উপনাম। এই প্রোটোকলের সাথে একটি সুইফ্ট টাইপ মেনে চলার মাধ্যমে, কম্পাইলার এই ধরণের একটি উদাহরণকে এনকোড/ডিকোড করার জন্য প্রয়োজনীয় কোড সংশ্লেষিত করবে, যেমন JSON-এর মতো সিরিয়াল ফর্ম্যাট থেকে।
একটি বই সম্পর্কে ডেটা সঞ্চয় করার জন্য একটি সাধারণ ধরন এইরকম দেখতে পারে:
struct Book: Codable {
var title: String
var numberOfPages: Int
var author: String
}
আপনি দেখতে পাচ্ছেন, কোডেবলের সাথে টাইপ মেনে চলা ন্যূনতম আক্রমণাত্মক। আমাদের শুধুমাত্র প্রোটোকলের সাথে সামঞ্জস্য যোগ করতে হয়েছিল; অন্য কোন পরিবর্তন প্রয়োজন ছিল না.
এটির সাথে, আমরা এখন সহজেই একটি JSON অবজেক্টে একটি বই এনকোড করতে পারি:
do {
let book = Book(title: "The Hitchhiker's Guide to the Galaxy",
numberOfPages: 816,
author: "Douglas Adams")
let encoder = JSONEncoder()
let data = try encoder.encode(book)
}
catch {
print("Error when trying to encode book: \(error)")
}
একটি Book
উদাহরণে একটি JSON অবজেক্ট ডিকোড করা নিম্নরূপ কাজ করে:
let decoder = JSONDecoder()
let data = /* fetch data from the network */
let decodedBook = try decoder.decode(Book.self, from: data)
Cloud Firestore নথিতে সহজ প্রকারে এবং থেকে ম্যাপিং
কোডেবল ব্যবহার করে
Cloud Firestore সাধারণ স্ট্রিং থেকে নেস্টেড ম্যাপ পর্যন্ত ডেটা প্রকারের বিস্তৃত সেট সমর্থন করে। এর মধ্যে বেশিরভাগই সরাসরি সুইফটের বিল্ট-ইন প্রকারের সাথে মিলে যায়। আরও জটিল তথ্যের মধ্যে ডুব দেওয়ার আগে আসুন প্রথমে কিছু সাধারণ ডেটা প্রকারের ম্যাপিংয়ের দিকে একবার নজর দিই।
Cloud Firestore নথিগুলিকে সুইফ্ট প্রকারে ম্যাপ করতে, এই পদক্ষেপগুলি অনুসরণ করুন:
- নিশ্চিত করুন যে আপনি আপনার প্রকল্পে
FirebaseFirestore
ফ্রেমওয়ার্ক যোগ করেছেন। আপনি এটি করতে সুইফট প্যাকেজ ম্যানেজার বা কোকোপডস ব্যবহার করতে পারেন। - আপনার সুইফট ফাইলে
FirebaseFirestore
আমদানি করুন। -
Codable
সাথে আপনার টাইপ কনফর্ম করুন। - (ঐচ্ছিক, যদি আপনি একটি
List
ভিউতে টাইপটি ব্যবহার করতে চান) আপনার টাইপে একটিid
প্রপার্টি যোগ করুন এবং Cloud Firestore ডকুমেন্ট আইডিতে ম্যাপ করতে বলার জন্য@DocumentID
ব্যবহার করুন। আমরা নীচে আরো বিস্তারিত আলোচনা করব. - একটি সুইফট টাইপের নথির রেফারেন্স ম্যাপ করতে
documentReference.data(as: )
ব্যবহার করুন। - একটি Cloud Firestore নথিতে Swift প্রকার থেকে ডেটা ম্যাপ করতে
documentReference.setData(from: )
ব্যবহার করুন৷ - (ঐচ্ছিক, কিন্তু অত্যন্ত প্রস্তাবিত) সঠিক ত্রুটি হ্যান্ডলিং বাস্তবায়ন করুন।
আসুন সেই অনুযায়ী আমাদের Book
ধরন আপডেট করি:
struct Book: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
}
যেহেতু এই প্রকারটি ইতিমধ্যেই কোডযোগ্য ছিল, তাই আমাদের শুধুমাত্র id
প্রপার্টি যোগ করতে হবে এবং @DocumentID
প্রপার্টি র্যাপার দিয়ে টীকা করতে হবে।
একটি নথি আনা এবং ম্যাপ করার জন্য পূর্ববর্তী কোড স্নিপেট গ্রহণ করে, আমরা সমস্ত ম্যানুয়াল ম্যাপিং কোডকে একটি লাইন দিয়ে প্রতিস্থাপন করতে পারি:
func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument { document, error in
if let error = error as NSError? {
self.errorMessage = "Error getting document: \(error.localizedDescription)"
}
else {
if let document = document {
do {
self.book = try document.data(as: Book.self)
}
catch {
print(error)
}
}
}
}
}
getDocument(as:)
কল করার সময় নথির ধরন উল্লেখ করে আপনি এটি আরও সংক্ষিপ্তভাবে লিখতে পারেন। এটি আপনার জন্য ম্যাপিং সঞ্চালন করবে, এবং ম্যাপ করা নথি সম্বলিত একটি Result
ধরণ প্রদান করবে, অথবা ডিকোডিং ব্যর্থ হলে একটি ত্রুটি:
private func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument(as: Book.self) { result in
switch result {
case .success(let book):
// A Book value was successfully initialized from the DocumentSnapshot.
self.book = book
self.errorMessage = nil
case .failure(let error):
// A Book value could not be initialized from the DocumentSnapshot.
self.errorMessage = "Error decoding document: \(error.localizedDescription)"
}
}
}
একটি বিদ্যমান নথি আপডেট করা documentReference.setData(from: )
কল করার মতোই সহজ। কিছু মৌলিক ত্রুটি পরিচালনা সহ, এখানে একটি Book
উদাহরণ সংরক্ষণ করার কোড রয়েছে:
func updateBook(book: Book) {
if let id = book.id {
let docRef = db.collection("books").document(id)
do {
try docRef.setData(from: book)
}
catch {
print(error)
}
}
}
একটি নতুন নথি যোগ করার সময়, Cloud Firestore স্বয়ংক্রিয়ভাবে নথিতে একটি নতুন নথি আইডি বরাদ্দ করার যত্ন নেবে৷ অ্যাপটি বর্তমানে অফলাইনে থাকলেও এটি কাজ করে।
func addBook(book: Book) {
let collectionRef = db.collection("books")
do {
let newDocReference = try collectionRef.addDocument(from: self.book)
print("Book stored with new document reference: \(newDocReference)")
}
catch {
print(error)
}
}
সাধারণ ডেটা টাইপ ম্যাপ করার পাশাপাশি, Cloud Firestore অনেকগুলি অন্যান্য ডেটাটাইপ সমর্থন করে, যার মধ্যে কিছু কাঠামোগত প্রকার যা আপনি একটি নথির ভিতরে নেস্টেড অবজেক্ট তৈরি করতে ব্যবহার করতে পারেন।
নেস্টেড কাস্টম প্রকার
আমরা আমাদের নথিতে ম্যাপ করতে চাই বেশিরভাগ গুণাবলী হল সাধারণ মান, যেমন বইয়ের শিরোনাম বা লেখকের নাম। কিন্তু সেই ক্ষেত্রে কী হবে যখন আমাদের আরও জটিল বস্তু সঞ্চয় করতে হবে? উদাহরণস্বরূপ, আমরা বিভিন্ন রেজোলিউশনে বইয়ের কভারে URLগুলি সংরক্ষণ করতে চাই।
Cloud Firestore এটি করার সবচেয়ে সহজ উপায় হল একটি মানচিত্র ব্যবহার করা:
সংশ্লিষ্ট সুইফ্ট স্ট্রাকট লেখার সময়, Cloud Firestore ইউআরএল-কে সমর্থন করে তা আমরা ব্যবহার করতে পারি — যখন একটি ইউআরএল রয়েছে এমন একটি ফিল্ড সংরক্ষণ করার সময়, এটি একটি স্ট্রিং-এ রূপান্তরিত হবে এবং এর বিপরীতে:
struct CoverImages: Codable {
var small: URL
var medium: URL
var large: URL
}
struct BookWithCoverImages: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var cover: CoverImages?
}
Cloud Firestore নথিতে কভার মানচিত্রের জন্য আমরা কীভাবে একটি স্ট্রাকট, CoverImages
সংজ্ঞায়িত করেছি তা লক্ষ্য করুন। BookWithCoverImages
এ কভার প্রপার্টিটিকে ঐচ্ছিক হিসেবে চিহ্নিত করার মাধ্যমে, আমরা কিছু নথিতে একটি কভার অ্যাট্রিবিউট নাও থাকতে পারে তা সামলে নিতে পারি।
আপনি যদি কৌতূহলী হন কেন ডেটা আনার বা আপডেট করার জন্য কোনও কোড স্নিপেট নেই, তাহলে আপনি এটি শুনে খুশি হবেন যে Cloud Firestore থেকে/এ পড়ার বা লেখার জন্য কোড সামঞ্জস্য করার প্রয়োজন নেই: এই সমস্ত কিছু আমরা কোডের সাথে কাজ করি প্রাথমিক বিভাগে লিখেছি।
অ্যারে
কখনও কখনও, আমরা একটি নথিতে মান সংগ্রহ করতে চাই। একটি বইয়ের জেনারগুলি একটি ভাল উদাহরণ: The Hitchhiker's Guide to the Galaxy- এর মতো একটি বই বিভিন্ন বিভাগে পড়তে পারে — এই ক্ষেত্রে "Sci-Fi" এবং "কমেডি":
Cloud Firestore , আমরা মানগুলির একটি অ্যারে ব্যবহার করে এটিকে মডেল করতে পারি। এটি যেকোন কোডেবল প্রকারের জন্য সমর্থিত (যেমন String
, Int
, ইত্যাদি)। আমাদের Book
মডেলে জেনারের একটি অ্যারে কীভাবে যুক্ত করা যায় তা নিম্নলিখিতটি দেখায়:
public struct BookWithGenre: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var genres: [String]
}
যেহেতু এটি যেকোনো কোডেবল টাইপের জন্য কাজ করে, তাই আমরা কাস্টম প্রকারগুলিও ব্যবহার করতে পারি। কল্পনা করুন আমরা প্রতিটি বইয়ের জন্য ট্যাগের একটি তালিকা সংরক্ষণ করতে চাই। ট্যাগের নামের সাথে, আমরা ট্যাগের রঙও সংরক্ষণ করতে চাই, যেমন:
এইভাবে ট্যাগগুলি সংরক্ষণ করার জন্য, আমাদের যা করতে হবে তা হল একটি ট্যাগ প্রতিনিধিত্ব করার জন্য একটি Tag
স্ট্রাকট প্রয়োগ করা এবং এটিকে কোডেবল করা:
struct Tag: Codable, Hashable {
var title: String
var color: String
}
এবং ঠিক তেমনই, আমরা আমাদের Book
নথিতে Tags
একটি অ্যারে সংরক্ষণ করতে পারি!
struct BookWithTags: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var tags: [Tag]
}
ডকুমেন্ট আইডি ম্যাপিং সম্পর্কে একটি দ্রুত শব্দ
আরও টাইপ ম্যাপ করার আগে, আসুন কিছুক্ষণের জন্য ডকুমেন্ট আইডি ম্যাপিং সম্পর্কে কথা বলি।
আমাদের Cloud Firestore ডকুমেন্টের ডকুমেন্ট আইডিকে আমাদের সুইফট ধরনের id
প্রপার্টিতে ম্যাপ করতে আমরা পূর্ববর্তী কিছু উদাহরণে @DocumentID
প্রপার্টি র্যাপার ব্যবহার করেছি। এটি বেশ কয়েকটি কারণে গুরুত্বপূর্ণ:
- ব্যবহারকারী স্থানীয় পরিবর্তন করলে কোন নথি আপডেট করতে হবে তা জানতে এটি আমাদের সাহায্য করে।
- সুইফটইউআই-এর
List
উপাদানগুলিকেIdentifiable
হতে হবে যাতে উপাদানগুলি ঢোকানোর সময় চারপাশে লাফাতে না পারে।
এটি উল্লেখ করার মতো যে @DocumentID
হিসাবে চিহ্নিত একটি বৈশিষ্ট্য Cloud Firestore এনকোডার দ্বারা নথিটি লেখার সময় এনকোড করা হবে না। কারণ ডকুমেন্ট আইডি ডকুমেন্টেরই কোনো অ্যাট্রিবিউট নয়—তাই ডকুমেন্টে লেখাটা ভুল হবে।
নেস্টেড প্রকারের সাথে কাজ করার সময় (যেমন এই নির্দেশিকায় আগের উদাহরণে Book
ট্যাগগুলির অ্যারে), এটি একটি @DocumentID
বৈশিষ্ট্য যোগ করার প্রয়োজন নেই: নেস্টেড বৈশিষ্ট্যগুলি Cloud Firestore নথির একটি অংশ, এবং গঠন করে না একটি পৃথক নথি। অতএব, তাদের একটি নথি আইডির প্রয়োজন নেই।
তারিখ এবং সময়
তারিখ এবং সময় পরিচালনা করার জন্য Cloud Firestore একটি অন্তর্নির্মিত ডেটা টাইপ রয়েছে এবং কোডেবলের জন্য Cloud Firestore সমর্থনের জন্য ধন্যবাদ, সেগুলি ব্যবহার করা সহজ।
আসুন এই নথিটি একবার দেখে নেওয়া যাক যা 1843 সালে উদ্ভাবিত সমস্ত প্রোগ্রামিং ভাষার জননী অ্যাডাকে প্রতিনিধিত্ব করে:
এই নথির ম্যাপিংয়ের জন্য একটি সুইফ্ট টাইপ এইরকম দেখতে পারে:
struct ProgrammingLanguage: Codable {
@DocumentID var id: String?
var name: String
var year: Date
}
@ServerTimestamp
সম্পর্কে কথোপকথন না করে আমরা তারিখ এবং সময় সম্পর্কে এই বিভাগটি ছেড়ে যেতে পারি না। আপনার অ্যাপে টাইমস্ট্যাম্প নিয়ে কাজ করার ক্ষেত্রে এই সম্পত্তির মোড়ক একটি পাওয়ার হাউস।
যেকোন ডিস্ট্রিবিউটেড সিস্টেমে, আলাদা আলাদা সিস্টেমের ঘড়িগুলো সব সময় সম্পূর্ণ সিঙ্ক না হওয়ার সম্ভাবনা থাকে। আপনি ভাবতে পারেন এটি একটি বড় বিষয় নয়, তবে একটি স্টক ট্রেড সিস্টেমের জন্য একটি ঘড়ির কিছুটা সিঙ্কের বাইরে চলার প্রভাব কল্পনা করুন: এমনকি একটি মিলিসেকেন্ডের বিচ্যুতিও একটি ট্রেড নির্বাহ করার সময় মিলিয়ন ডলারের পার্থক্য হতে পারে৷
Cloud Firestore @ServerTimestamp
দ্বারা চিহ্নিত বৈশিষ্ট্যগুলিকে নিম্নরূপ পরিচালনা করে: যদি আপনি এটি সংরক্ষণ করার সময় বৈশিষ্ট্যটি nil
হয় (উদাহরণস্বরূপ addDocument()
ব্যবহার করে), Cloud Firestore ডাটাবেসে লেখার সময় বর্তমান সার্ভার টাইমস্ট্যাম্প সহ ক্ষেত্রটি পূরণ করবে . আপনি addDocument()
বা updateData()
কল করার সময় ক্ষেত্রটি nil
না হলে, Cloud Firestore অ্যাট্রিবিউটের মানটিকে স্পর্শ না করে রেখে দেবে। এইভাবে, createdAt
এবং lastUpdatedAt
মতো ক্ষেত্রগুলি বাস্তবায়ন করা সহজ।
জিওপয়েন্ট
জিওলোকেশন আমাদের অ্যাপে সর্বব্যাপী। অনেক উত্তেজনাপূর্ণ বৈশিষ্ট্য তাদের সংরক্ষণ করে সম্ভব হয়ে ওঠে. উদাহরণস্বরূপ, একটি কাজের জন্য একটি অবস্থান সংরক্ষণ করা দরকারী হতে পারে যাতে আপনি একটি গন্তব্যে পৌঁছালে আপনার অ্যাপটি আপনাকে একটি টাস্ক সম্পর্কে মনে করিয়ে দিতে পারে।
Cloud Firestore একটি অন্তর্নির্মিত ডেটা টাইপ রয়েছে, GeoPoint
, যা যেকোনো অবস্থানের দ্রাঘিমাংশ এবং অক্ষাংশ সংরক্ষণ করতে পারে। একটি Cloud Firestore ডকুমেন্ট থেকে/তে অবস্থান ম্যাপ করতে, আমরা GeoPoint
টাইপ ব্যবহার করতে পারি:
struct Office: Codable {
@DocumentID var id: String?
var name: String
var location: GeoPoint
}
সুইফ্টের সংশ্লিষ্ট ধরনটি হল CLLocationCoordinate2D
, এবং আমরা নিম্নলিখিত ক্রিয়াকলাপের মাধ্যমে এই দুটি প্রকারের মধ্যে মানচিত্র করতে পারি:
CLLocationCoordinate2D(latitude: office.location.latitude,
longitude: office.location.longitude)
ভৌত অবস্থান দ্বারা নথি অনুসন্ধান সম্পর্কে আরও জানতে, এই সমাধান নির্দেশিকাটি দেখুন।
Enums
Enums সম্ভবত সুইফটের সবচেয়ে আন্ডাররেটেড ভাষার বৈশিষ্ট্যগুলির মধ্যে একটি; চোখের দেখা ছাড়া তাদের কাছে আরও অনেক কিছু আছে। enums-এর জন্য একটি সাধারণ ব্যবহারের ক্ষেত্রে কিছুর বিচ্ছিন্ন অবস্থার মডেল করা। উদাহরণস্বরূপ, আমরা নিবন্ধগুলি পরিচালনা করার জন্য একটি অ্যাপ লিখতে পারি। একটি নিবন্ধের স্থিতি ট্র্যাক করতে, আমরা একটি enum Status
ব্যবহার করতে চাই:
enum Status: String, Codable {
case draft
case inReview
case approved
case published
}
Cloud Firestore স্থানীয়ভাবে enums সমর্থন করে না (অর্থাৎ, এটি মানগুলির সেট প্রয়োগ করতে পারে না), তবে আমরা এখনও এই সত্যটি ব্যবহার করতে পারি যে enums টাইপ করা যেতে পারে এবং একটি কোডেবল টাইপ চয়ন করতে পারি। এই উদাহরণে, আমরা String
বেছে নিয়েছি, যার মানে Cloud Firestore ডকুমেন্টে সঞ্চয় করার সময় সমস্ত enum মান স্ট্রিং-এ/থেকে ম্যাপ করা হবে।
এবং, যেহেতু সুইফ্ট কাস্টম কাঁচা মান সমর্থন করে, আমরা এমনকি কাস্টমাইজ করতে পারি কোন মানগুলি কোন enum কেসকে নির্দেশ করে। সুতরাং উদাহরণস্বরূপ, যদি আমরা Status.inReview
কেসকে "পর্যালোচনায়" হিসাবে সংরক্ষণ করার সিদ্ধান্ত নিই, তাহলে আমরা উপরের enumটিকে নিম্নরূপ আপডেট করতে পারি:
enum Status: String, Codable {
case draft
case inReview = "in review"
case approved
case published
}
ম্যাপিং কাস্টমাইজ করা
কখনও কখনও, আমরা যে Cloud Firestore ডকুমেন্টগুলি ম্যাপ করতে চাই তার অ্যাট্রিবিউটের নামগুলি আমাদের সুইফটের ডেটা মডেলের বৈশিষ্ট্যগুলির নামের সাথে মেলে না৷ উদাহরণস্বরূপ, আমাদের সহকর্মীদের মধ্যে একজন পাইথন বিকাশকারী হতে পারে এবং তাদের সমস্ত বৈশিষ্ট্যের নামের জন্য snake_case বেছে নেওয়ার সিদ্ধান্ত নিয়েছে৷
চিন্তা করবেন না: কোডেবল আমাদের কভার করেছে!
এই ধরনের ক্ষেত্রে, আমরা CodingKeys
ব্যবহার করতে পারি। এটি একটি enum যা আমরা নির্দিষ্ট বৈশিষ্ট্যগুলি কীভাবে ম্যাপ করা হবে তা নির্দিষ্ট করতে একটি কোডেবল স্ট্রাকটে যোগ করতে পারি।
এই নথি বিবেচনা করুন:
এই ডকুমেন্টটিকে এমন একটি স্ট্রাকটে ম্যাপ করতে যার একটি নাম বৈশিষ্ট্য রয়েছে String
টাইপ, আমাদের ProgrammingLanguage
স্ট্রাকটে একটি CodingKeys
enum যোগ করতে হবে এবং নথিতে বৈশিষ্ট্যের নাম উল্লেখ করতে হবে:
struct ProgrammingLanguage: Codable {
@DocumentID var id: String?
var name: String
var year: Date
enum CodingKeys: String, CodingKey {
case id
case name = "language_name"
case year
}
}
ডিফল্টরূপে, Cloud Firestore নথিতে আমরা ম্যাপ করার চেষ্টা করছি এমন বৈশিষ্ট্যের নাম নির্ধারণ করতে Codable API আমাদের সুইফ্ট প্রকারের সম্পত্তির নাম ব্যবহার করবে। তাই যতক্ষণ পর্যন্ত অ্যাট্রিবিউটের নাম মেলে, ততক্ষণ আমাদের কোডেবল প্রকারে CodingKeys
যোগ করার দরকার নেই। যাইহোক, একবার আমরা একটি নির্দিষ্ট ধরণের জন্য CodingKeys
ব্যবহার করলে, আমরা ম্যাপ করতে চাই এমন সমস্ত সম্পত্তির নাম যোগ করতে হবে।
উপরের কোড স্নিপেটে, আমরা একটি id
প্রপার্টি সংজ্ঞায়িত করেছি যা আমরা একটি SwiftUI List
ভিউতে সনাক্তকারী হিসাবে ব্যবহার করতে চাই। যদি আমরা এটি CodingKeys
এ নির্দিষ্ট না করে থাকি, তাহলে ডেটা আনার সময় এটি ম্যাপ করা হবে না এবং এইভাবে nil
হয়ে যাবে। এর ফলে List
ভিউ প্রথম নথিতে পূর্ণ হবে।
সংশ্লিষ্ট CodingKeys
enum-এ কেস হিসেবে তালিকাভুক্ত নয় এমন কোনো সম্পত্তি ম্যাপিং প্রক্রিয়া চলাকালীন উপেক্ষা করা হবে। এটি আসলে সুবিধাজনক হতে পারে যদি আমরা বিশেষভাবে কিছু বৈশিষ্ট্য ম্যাপ করা থেকে বাদ দিতে চাই।
সুতরাং উদাহরণস্বরূপ, যদি আমরা এই বৈশিষ্ট্যটিকে ম্যাপ করা থেকে reasonWhyILoveThis
বাদ দিতে চাই, তবে আমাদের যা করতে হবে তা হল CodingKeys
এনাম থেকে এটি অপসারণ করা:
struct ProgrammingLanguage: Identifiable, Codable {
@DocumentID var id: String?
var name: String
var year: Date
var reasonWhyILoveThis: String = ""
enum CodingKeys: String, CodingKey {
case id
case name = "language_name"
case year
}
}
মাঝে মাঝে আমরা Cloud Firestore নথিতে একটি খালি বৈশিষ্ট্য লিখতে চাই। সুইফ্টের একটি মানের অনুপস্থিতি বোঝাতে ঐচ্ছিক ধারণা রয়েছে এবং Cloud Firestore null
মানগুলিকেও সমর্থন করে। যাইহোক, এনকোডিং ঐচ্ছিকগুলির জন্য ডিফল্ট আচরণ যেগুলির একটি nil
মান রয়েছে কেবল সেগুলিকে বাদ দেওয়া। @ExplicitNull
আমাদেরকে সুইফ্ট বিকল্পগুলিকে এনকোড করার সময় কীভাবে পরিচালনা করা হয় তার উপর কিছু নিয়ন্ত্রণ দেয়: @ExplicitNull
হিসাবে একটি ঐচ্ছিক প্রপার্টি ফ্ল্যাগ করে, আমরা Cloud Firestore বলতে পারি এই প্রপার্টিটিকে শূন্য মান সহ নথিতে লিখতে যদি এতে nil
এর মান থাকে।
রঙ ম্যাপ করার জন্য একটি কাস্টম এনকোডার এবং ডিকোডার ব্যবহার করা
কোডেবলের সাথে ম্যাপিং ডেটার আমাদের কভারেজের একটি শেষ বিষয় হিসাবে, আসুন কাস্টম এনকোডার এবং ডিকোডারগুলি প্রবর্তন করি। এই বিভাগটি একটি নেটিভ Cloud Firestore ডেটাটাইপকে কভার করে না, তবে কাস্টম এনকোডার এবং ডিকোডারগুলি আপনার Cloud Firestore অ্যাপগুলিতে ব্যাপকভাবে কার্যকর।
"কিভাবে আমি রঙের ম্যাপ করতে পারি" ডেভেলপারের সবচেয়ে বেশি জিজ্ঞাসিত প্রশ্নগুলির মধ্যে একটি, শুধুমাত্র Cloud Firestore জন্য নয়, সুইফট এবং JSON-এর মধ্যে ম্যাপিংয়ের জন্যও৷ সেখানে প্রচুর সমাধান রয়েছে, তবে তাদের বেশিরভাগই JSON-এ ফোকাস করে এবং প্রায় সবকটিই এর আরজিবি উপাদানগুলির সমন্বয়ে গঠিত একটি নেস্টেড অভিধান হিসাবে রঙগুলিকে ম্যাপ করে।
মনে হচ্ছে একটি ভাল, সহজ সমাধান হওয়া উচিত। কেন আমরা ওয়েব রঙ ব্যবহার করি না (অথবা, আরও নির্দিষ্ট হতে, CSS হেক্স রঙের স্বরলিপি) — এগুলি ব্যবহার করা সহজ (মূলত কেবল একটি স্ট্রিং), এবং এমনকি তারা স্বচ্ছতা সমর্থন করে!
একটি সুইফ্ট Color
তার হেক্স মানের সাথে ম্যাপ করতে সক্ষম হওয়ার জন্য, আমাদের একটি সুইফট এক্সটেনশন তৈরি করতে হবে যা Codable কে Color
এ যোগ করে।
extension Color {
init(hex: String) {
let rgba = hex.toRGBA()
self.init(.sRGB,
red: Double(rgba.r),
green: Double(rgba.g),
blue: Double(rgba.b),
opacity: Double(rgba.alpha))
}
//... (code for translating between hex and RGBA omitted for brevity)
}
extension Color: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let hex = try container.decode(String.self)
self.init(hex: hex)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(toHex)
}
}
decoder.singleValueContainer()
ব্যবহার করে, আমরা RGBA উপাদানগুলিকে নেস্ট না করেই একটি String
এর Color
সমতুল্য ডিকোড করতে পারি। এছাড়াও, আপনি এই মানগুলিকে প্রথমে রূপান্তর না করেই আপনার অ্যাপের ওয়েব UI-তে ব্যবহার করতে পারেন!
এটির সাহায্যে, আমরা ম্যাপিং ট্যাগের জন্য কোড আপডেট করতে পারি, আমাদের অ্যাপের UI কোডে ম্যানুয়ালি ম্যাপ করার পরিবর্তে ট্যাগের রঙগুলিকে সরাসরি পরিচালনা করা সহজ করে তোলে:
struct Tag: Codable, Hashable {
var title: String
var color: Color
}
struct BookWithTags: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var tags: [Tag]
}
হ্যান্ডলিং ত্রুটি
উপরের কোড স্নিপেটগুলিতে আমরা ইচ্ছাকৃতভাবে ত্রুটি হ্যান্ডলিংকে ন্যূনতম রেখেছি, তবে একটি প্রোডাকশন অ্যাপে, আপনি নিশ্চিত করতে চাইবেন যে কোনও ত্রুটি সুন্দরভাবে পরিচালনা করুন।
এখানে একটি কোড স্নিপেট রয়েছে যা দেখায় যে কীভাবে আপনি যে কোনও ত্রুটির পরিস্থিতি পরিচালনা করতে পারেন তা দেখায়:
class MappingSimpleTypesViewModel: ObservableObject {
@Published var book: Book = .empty
@Published var errorMessage: String?
private var db = Firestore.firestore()
func fetchAndMap() {
fetchBook(documentId: "hitchhiker")
}
func fetchAndMapNonExisting() {
fetchBook(documentId: "does-not-exist")
}
func fetchAndTryMappingInvalidData() {
fetchBook(documentId: "invalid-data")
}
private func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument(as: Book.self) { result in
switch result {
case .success(let book):
// A Book value was successfully initialized from the DocumentSnapshot.
self.book = book
self.errorMessage = nil
case .failure(let error):
// A Book value could not be initialized from the DocumentSnapshot.
switch error {
case DecodingError.typeMismatch(_, let context):
self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.valueNotFound(_, let context):
self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.keyNotFound(_, let context):
self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.dataCorrupted(let key):
self.errorMessage = "\(error.localizedDescription): \(key)"
default:
self.errorMessage = "Error decoding document: \(error.localizedDescription)"
}
}
}
}
}
লাইভ আপডেটে ত্রুটি পরিচালনা করা
পূর্ববর্তী কোড স্নিপেট দেখায় কিভাবে একটি একক নথি আনার সময় ত্রুটিগুলি পরিচালনা করতে হয়। একবার ডেটা আনার পাশাপাশি, Cloud Firestore তথাকথিত স্ন্যাপশট শ্রোতাদের ব্যবহার করে আপনার অ্যাপে আপডেটগুলি সরবরাহ করতেও সমর্থন করে: আমরা একটি সংগ্রহে (বা প্রশ্ন) একটি স্ন্যাপশট শ্রোতা নিবন্ধন করতে পারি এবং Cloud Firestore যখনই সেখানে আমাদের শ্রোতাকে কল করবে একটি আপডেট হয়।
এখানে একটি কোড স্নিপেট রয়েছে যা দেখায় কিভাবে একটি স্ন্যাপশট শ্রোতা নিবন্ধন করতে হয়, কোডেবল ব্যবহার করে ডেটা ম্যাপ করতে হয় এবং যে কোনো ত্রুটি ঘটতে পারে তা পরিচালনা করতে হয়। এটি সংগ্রহে একটি নতুন নথি কীভাবে যুক্ত করতে হয় তাও দেখায়। আপনি দেখতে পাবেন, ম্যাপ করা নথিগুলিকে ধারণ করে স্থানীয় অ্যারে আপডেট করার দরকার নেই, কারণ এটি স্ন্যাপশট শ্রোতার কোড দ্বারা যত্ন নেওয়া হয়।
class MappingColorsViewModel: ObservableObject {
@Published var colorEntries = [ColorEntry]()
@Published var newColor = ColorEntry.empty
@Published var errorMessage: String?
private var db = Firestore.firestore()
private var listenerRegistration: ListenerRegistration?
public func unsubscribe() {
if listenerRegistration != nil {
listenerRegistration?.remove()
listenerRegistration = nil
}
}
func subscribe() {
if listenerRegistration == nil {
listenerRegistration = db.collection("colors")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
self?.errorMessage = "No documents in 'colors' collection"
return
}
self?.colorEntries = documents.compactMap { queryDocumentSnapshot in
let result = Result { try queryDocumentSnapshot.data(as: ColorEntry.self) }
switch result {
case .success(let colorEntry):
if let colorEntry = colorEntry {
// A ColorEntry value was successfully initialized from the DocumentSnapshot.
self?.errorMessage = nil
return colorEntry
}
else {
// A nil value was successfully initialized from the DocumentSnapshot,
// or the DocumentSnapshot was nil.
self?.errorMessage = "Document doesn't exist."
return nil
}
case .failure(let error):
// A ColorEntry value could not be initialized from the DocumentSnapshot.
switch error {
case DecodingError.typeMismatch(_, let context):
self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.valueNotFound(_, let context):
self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.keyNotFound(_, let context):
self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.dataCorrupted(let key):
self?.errorMessage = "\(error.localizedDescription): \(key)"
default:
self?.errorMessage = "Error decoding document: \(error.localizedDescription)"
}
return nil
}
}
}
}
}
func addColorEntry() {
let collectionRef = db.collection("colors")
do {
let newDocReference = try collectionRef.addDocument(from: newColor)
print("ColorEntry stored with new document reference: \(newDocReference)")
}
catch {
print(error)
}
}
}
এই পোস্টে ব্যবহৃত সমস্ত কোড স্নিপেট একটি নমুনা অ্যাপ্লিকেশনের অংশ যা আপনি এই GitHub সংগ্রহস্থল থেকে ডাউনলোড করতে পারেন।
এগিয়ে যান এবং কোডেবল ব্যবহার করুন!
Swift's Codable API আপনার অ্যাপ্লিকেশন ডেটা মডেল থেকে সিরিয়ালাইজড ফরম্যাট থেকে ডেটা ম্যাপ করার একটি শক্তিশালী এবং নমনীয় উপায় প্রদান করে। এই নির্দেশিকাটিতে, আপনি দেখেছেন যে অ্যাপগুলিকে তাদের ডেটাস্টোর হিসাবে Cloud Firestore ব্যবহার করা কতটা সহজ৷
সাধারণ ডেটা টাইপ সহ একটি মৌলিক উদাহরণ থেকে শুরু করে, আমরা ধীরে ধীরে ডেটা মডেলের জটিলতা বাড়িয়েছি, আমাদের জন্য ম্যাপিং সম্পাদন করার জন্য কোডেবল এবং ফায়ারবেসের বাস্তবায়নের উপর নির্ভর করতে সক্ষম হয়েছি।
কোডেবল সম্পর্কে আরও বিশদ বিবরণের জন্য, আমি নিম্নলিখিত সংস্থানগুলির সুপারিশ করছি:
- কোডেবলের বেসিক সম্পর্কে জন সান্ডেলের একটি চমৎকার নিবন্ধ রয়েছে।
- যদি বইগুলি আপনার জিনিস বেশি হয় তবে সুইফট কোডেবলের জন্য ম্যাটের ফ্লাইট স্কুল গাইড দেখুন।
- এবং অবশেষে, ডনি ওয়ালসের কোডেবল সম্পর্কে একটি সম্পূর্ণ সিরিজ রয়েছে।
যদিও আমরা Cloud Firestore ডকুমেন্ট ম্যাপ করার জন্য একটি বিস্তৃত নির্দেশিকা কম্পাইল করার জন্য যথাসাধ্য চেষ্টা করেছি, এটি সম্পূর্ণ নয় এবং আপনি আপনার প্রকারগুলিকে ম্যাপ করার জন্য অন্যান্য কৌশলগুলি ব্যবহার করতে পারেন৷ নীচের প্রতিক্রিয়া পাঠান বোতামটি ব্যবহার করে, আপনি অন্য ধরনের Cloud Firestore ডেটা ম্যাপ করার জন্য বা Swift-এ ডেটা উপস্থাপন করার জন্য কী কৌশলগুলি ব্যবহার করেন তা আমাদের জানান৷
Cloud Firestore কোডেবল সাপোর্ট ব্যবহার না করার কোন কারণ নেই।
,সুইফটের কোডেবল এপিআই, সুইফট 4-এ প্রবর্তন করা হয়েছে, আমাদেরকে কম্পাইলারের শক্তিকে কাজে লাগাতে সাহায্য করে যাতে করে ক্রমিক বিন্যাস থেকে সুইফট প্রকারে ডেটা ম্যাপ করা সহজ হয়।
আপনি একটি ওয়েব API থেকে আপনার অ্যাপের ডেটা মডেলে ডেটা ম্যাপ করতে কোডেবল ব্যবহার করতে পারেন (এবং এর বিপরীতে), কিন্তু এটি তার চেয়ে অনেক বেশি নমনীয়।
এই নির্দেশিকায়, আমরা Cloud Firestore থেকে সুইফট প্রকারে ডেটা ম্যাপ করতে কোডেবল কীভাবে ব্যবহার করা যেতে পারে তা দেখতে যাচ্ছি এবং এর বিপরীতে।
Cloud Firestore থেকে একটি নথি আনার সময়, আপনার অ্যাপটি কী/মান জোড়ার একটি অভিধান পাবে (অথবা অভিধানের একটি অ্যারে, যদি আপনি একাধিক নথি ফিরিয়ে দেওয়ার ক্রিয়াকলাপগুলির একটি ব্যবহার করেন)।
এখন, আপনি অবশ্যই সুইফটে সরাসরি অভিধান ব্যবহার করা চালিয়ে যেতে পারেন, এবং তারা কিছু দুর্দান্ত নমনীয়তা অফার করে যা আপনার ব্যবহারের ক্ষেত্রে যা প্রয়োজন তা হতে পারে। যাইহোক, এই পদ্ধতিটি টাইপ নিরাপদ নয় এবং বৈশিষ্ট্যের নামের ভুল বানান করে বা আপনার দল গত সপ্তাহে সেই উত্তেজনাপূর্ণ নতুন বৈশিষ্ট্যটি পাঠানোর সময় যোগ করা নতুন বৈশিষ্ট্য ম্যাপ করতে ভুলে গিয়ে হার্ড-টু-ট্র্যাক-ডাউন বাগগুলি প্রবর্তন করা সহজ।
অতীতে, অনেক বিকাশকারী একটি সাধারণ ম্যাপিং স্তর প্রয়োগ করে এই ত্রুটিগুলিকে ঘিরে কাজ করেছে যা তাদের সুইফ্ট প্রকারের অভিধানগুলিকে ম্যাপ করতে দেয়৷ কিন্তু আবার, এই বাস্তবায়নের বেশিরভাগই Cloud Firestore ডকুমেন্ট এবং আপনার অ্যাপের ডেটা মডেলের সংশ্লিষ্ট প্রকারের মধ্যে ম্যাপিং ম্যানুয়ালি নির্দিষ্ট করার উপর ভিত্তি করে।
Cloud Firestore সাহায্যে সুইফ্টের কোডেবল API-এর জন্য এটি অনেক সহজ হয়ে যায়:
- আপনাকে আর ম্যানুয়ালি কোনো ম্যাপিং কোড বাস্তবায়ন করতে হবে না।
- বিভিন্ন নামের সাথে বৈশিষ্ট্যগুলি কীভাবে ম্যাপ করা যায় তা সংজ্ঞায়িত করা সহজ।
- এটিতে সুইফটের অনেক ধরণের জন্য অন্তর্নির্মিত সমর্থন রয়েছে।
- এবং কাস্টম ধরনের ম্যাপিংয়ের জন্য সমর্থন যোগ করা সহজ।
- সর্বোত্তম: সাধারণ ডেটা মডেলগুলির জন্য, আপনাকে কোনও ম্যাপিং কোড লিখতে হবে না।
ম্যাপিং ডেটা
Cloud Firestore নথিতে ডেটা সঞ্চয় করে যা মানগুলির কী ম্যাপ করে। একটি স্বতন্ত্র নথি থেকে ডেটা আনার জন্য, আমরা DocumentSnapshot.data()
কল করতে পারি, যা একটি অভিধান প্রদান করে যা ফিল্ডের নাম ম্যাপিং করে Any
: func data() -> [String : Any]?
.
এর মানে আমরা প্রতিটি পৃথক ক্ষেত্রে অ্যাক্সেস করতে সুইফটের সাবস্ক্রিপ্ট সিনট্যাক্স ব্যবহার করতে পারি।
import FirebaseFirestore
#warning("DO NOT MAP YOUR DOCUMENTS MANUALLY. USE CODABLE INSTEAD.")
func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument { document, error in
if let error = error as NSError? {
self.errorMessage = "Error getting document: \(error.localizedDescription)"
}
else {
if let document = document {
let id = document.documentID
let data = document.data()
let title = data?["title"] as? String ?? ""
let numberOfPages = data?["numberOfPages"] as? Int ?? 0
let author = data?["author"] as? String ?? ""
self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author)
}
}
}
}
যদিও এটি সহজবোধ্য এবং প্রয়োগ করা সহজ বলে মনে হতে পারে, এই কোডটি ভঙ্গুর, বজায় রাখা কঠিন এবং ত্রুটি-প্রবণ।
আপনি দেখতে পাচ্ছেন, আমরা ডকুমেন্ট ফিল্ডের ডেটা প্রকার সম্পর্কে অনুমান করছি। এইগুলি সঠিক হতে পারে বা নাও হতে পারে।
মনে রাখবেন, যেহেতু কোনও স্কিমা নেই, আপনি সহজেই সংগ্রহে একটি নতুন নথি যোগ করতে পারেন এবং একটি ক্ষেত্রের জন্য একটি ভিন্ন প্রকার চয়ন করতে পারেন৷ আপনি ঘটনাক্রমে numberOfPages
ক্ষেত্রের জন্য স্ট্রিং বেছে নিতে পারেন, যার ফলে ম্যাপিং-এর সমস্যা খুঁজে পাওয়া কঠিন হবে। এছাড়াও, যখনই একটি নতুন ক্ষেত্র যোগ করা হবে তখনই আপনাকে আপনার ম্যাপিং কোড আপডেট করতে হবে, যা বরং কষ্টকর।
এবং আসুন আমরা ভুলে যাই না যে আমরা সুইফটের শক্তিশালী টাইপ সিস্টেমের সুবিধা নিচ্ছি না, যা Book
প্রতিটি বৈশিষ্ট্যের জন্য সঠিক টাইপ জানে।
কোডেবল কি, যাইহোক?
অ্যাপলের ডকুমেন্টেশন অনুসারে, কোডেবল হল "একটি প্রকার যা নিজেকে একটি বাহ্যিক উপস্থাপনায় এবং এর বাইরে রূপান্তর করতে পারে।" আসলে, কোডেবল হল এনকোডেবল এবং ডিকোডেবল প্রোটোকলের জন্য একটি টাইপ উপনাম। এই প্রোটোকলের সাথে একটি সুইফ্ট টাইপ মেনে চলার মাধ্যমে, কম্পাইলার এই ধরণের একটি উদাহরণকে এনকোড/ডিকোড করার জন্য প্রয়োজনীয় কোড সংশ্লেষিত করবে, যেমন JSON-এর মতো সিরিয়াল ফর্ম্যাট থেকে।
একটি বই সম্পর্কে ডেটা সঞ্চয় করার জন্য একটি সাধারণ ধরন এইরকম দেখতে পারে:
struct Book: Codable {
var title: String
var numberOfPages: Int
var author: String
}
আপনি দেখতে পাচ্ছেন, কোডেবলের সাথে টাইপ মেনে চলা ন্যূনতম আক্রমণাত্মক। আমাদের শুধুমাত্র প্রোটোকলের সাথে সামঞ্জস্য যোগ করতে হয়েছিল; অন্য কোন পরিবর্তন প্রয়োজন ছিল না.
এটির সাথে, আমরা এখন সহজেই একটি JSON অবজেক্টে একটি বই এনকোড করতে পারি:
do {
let book = Book(title: "The Hitchhiker's Guide to the Galaxy",
numberOfPages: 816,
author: "Douglas Adams")
let encoder = JSONEncoder()
let data = try encoder.encode(book)
}
catch {
print("Error when trying to encode book: \(error)")
}
একটি Book
উদাহরণে একটি JSON অবজেক্ট ডিকোড করা নিম্নরূপ কাজ করে:
let decoder = JSONDecoder()
let data = /* fetch data from the network */
let decodedBook = try decoder.decode(Book.self, from: data)
Cloud Firestore নথিতে সহজ প্রকারে এবং থেকে ম্যাপিং
কোডেবল ব্যবহার করে
Cloud Firestore সাধারণ স্ট্রিং থেকে নেস্টেড ম্যাপ পর্যন্ত ডেটা প্রকারের বিস্তৃত সেট সমর্থন করে। এর মধ্যে বেশিরভাগই সরাসরি সুইফটের বিল্ট-ইন প্রকারের সাথে মিলে যায়। আরও জটিল তথ্যের মধ্যে ডুব দেওয়ার আগে আসুন প্রথমে কিছু সাধারণ ডেটা প্রকারের ম্যাপিংয়ের দিকে একবার নজর দিই।
Cloud Firestore নথিগুলিকে সুইফ্ট প্রকারে ম্যাপ করতে, এই পদক্ষেপগুলি অনুসরণ করুন:
- নিশ্চিত করুন যে আপনি আপনার প্রকল্পে
FirebaseFirestore
ফ্রেমওয়ার্ক যোগ করেছেন। আপনি এটি করতে সুইফট প্যাকেজ ম্যানেজার বা কোকোপডস ব্যবহার করতে পারেন। - আপনার সুইফট ফাইলে
FirebaseFirestore
আমদানি করুন। -
Codable
সাথে আপনার টাইপ কনফর্ম করুন। - (ঐচ্ছিক, যদি আপনি একটি
List
ভিউতে টাইপটি ব্যবহার করতে চান) আপনার টাইপে একটিid
প্রপার্টি যোগ করুন এবং Cloud Firestore ডকুমেন্ট আইডিতে ম্যাপ করতে বলার জন্য@DocumentID
ব্যবহার করুন। আমরা নীচে আরো বিস্তারিত আলোচনা করব. - একটি সুইফট টাইপের নথির রেফারেন্স ম্যাপ করতে
documentReference.data(as: )
ব্যবহার করুন। - একটি Cloud Firestore নথিতে Swift প্রকার থেকে ডেটা ম্যাপ করতে
documentReference.setData(from: )
ব্যবহার করুন৷ - (ঐচ্ছিক, কিন্তু অত্যন্ত প্রস্তাবিত) সঠিক ত্রুটি হ্যান্ডলিং বাস্তবায়ন.
আসুন সেই অনুযায়ী আমাদের Book
ধরন আপডেট করি:
struct Book: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
}
যেহেতু এই প্রকারটি ইতিমধ্যেই কোডযোগ্য ছিল, তাই আমাদের শুধুমাত্র id
প্রপার্টি যোগ করতে হবে এবং @DocumentID
প্রপার্টি র্যাপার দিয়ে টীকা করতে হবে।
একটি নথি আনা এবং ম্যাপ করার জন্য পূর্ববর্তী কোড স্নিপেট গ্রহণ করে, আমরা সমস্ত ম্যানুয়াল ম্যাপিং কোডকে একটি লাইন দিয়ে প্রতিস্থাপন করতে পারি:
func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument { document, error in
if let error = error as NSError? {
self.errorMessage = "Error getting document: \(error.localizedDescription)"
}
else {
if let document = document {
do {
self.book = try document.data(as: Book.self)
}
catch {
print(error)
}
}
}
}
}
getDocument(as:)
কল করার সময় নথির ধরন উল্লেখ করে আপনি এটি আরও সংক্ষিপ্তভাবে লিখতে পারেন। এটি আপনার জন্য ম্যাপিং সঞ্চালন করবে, এবং ম্যাপ করা নথি সম্বলিত একটি Result
ধরণ প্রদান করবে, অথবা ডিকোডিং ব্যর্থ হলে একটি ত্রুটি:
private func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument(as: Book.self) { result in
switch result {
case .success(let book):
// A Book value was successfully initialized from the DocumentSnapshot.
self.book = book
self.errorMessage = nil
case .failure(let error):
// A Book value could not be initialized from the DocumentSnapshot.
self.errorMessage = "Error decoding document: \(error.localizedDescription)"
}
}
}
একটি বিদ্যমান নথি আপডেট করা documentReference.setData(from: )
কল করার মতোই সহজ। কিছু মৌলিক ত্রুটি পরিচালনা সহ, এখানে একটি Book
উদাহরণ সংরক্ষণ করার কোড রয়েছে:
func updateBook(book: Book) {
if let id = book.id {
let docRef = db.collection("books").document(id)
do {
try docRef.setData(from: book)
}
catch {
print(error)
}
}
}
একটি নতুন নথি যোগ করার সময়, Cloud Firestore স্বয়ংক্রিয়ভাবে নথিতে একটি নতুন নথি আইডি বরাদ্দ করার যত্ন নেবে৷ অ্যাপটি বর্তমানে অফলাইনে থাকলেও এটি কাজ করে।
func addBook(book: Book) {
let collectionRef = db.collection("books")
do {
let newDocReference = try collectionRef.addDocument(from: self.book)
print("Book stored with new document reference: \(newDocReference)")
}
catch {
print(error)
}
}
সাধারণ ডেটা টাইপ ম্যাপ করার পাশাপাশি, Cloud Firestore অনেকগুলি অন্যান্য ডেটাটাইপ সমর্থন করে, যার মধ্যে কিছু কাঠামোগত প্রকার যা আপনি একটি নথির ভিতরে নেস্টেড অবজেক্ট তৈরি করতে ব্যবহার করতে পারেন।
নেস্টেড কাস্টম প্রকার
আমরা আমাদের নথিতে ম্যাপ করতে চাই বেশিরভাগ গুণাবলী হল সাধারণ মান, যেমন বইয়ের শিরোনাম বা লেখকের নাম। কিন্তু সেই ক্ষেত্রে কী হবে যখন আমাদের আরও জটিল বস্তু সঞ্চয় করতে হবে? উদাহরণস্বরূপ, আমরা বিভিন্ন রেজোলিউশনে বইয়ের কভারে URLগুলি সংরক্ষণ করতে চাই।
Cloud Firestore এটি করার সবচেয়ে সহজ উপায় হল একটি মানচিত্র ব্যবহার করা:
সংশ্লিষ্ট সুইফ্ট স্ট্রাকট লেখার সময়, Cloud Firestore ইউআরএল-কে সমর্থন করে তা আমরা ব্যবহার করতে পারি — যখন একটি ইউআরএল রয়েছে এমন একটি ফিল্ড সংরক্ষণ করার সময়, এটি একটি স্ট্রিং-এ রূপান্তরিত হবে এবং এর বিপরীতে:
struct CoverImages: Codable {
var small: URL
var medium: URL
var large: URL
}
struct BookWithCoverImages: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var cover: CoverImages?
}
Cloud Firestore নথিতে কভার মানচিত্রের জন্য আমরা কীভাবে একটি স্ট্রাকট, CoverImages
সংজ্ঞায়িত করেছি তা লক্ষ্য করুন। BookWithCoverImages
এ কভার প্রপার্টিটিকে ঐচ্ছিক হিসেবে চিহ্নিত করার মাধ্যমে, আমরা কিছু নথিতে একটি কভার অ্যাট্রিবিউট নাও থাকতে পারে তা সামলে নিতে পারি।
আপনি যদি কৌতূহলী হন কেন ডেটা আনার বা আপডেট করার জন্য কোনও কোড স্নিপেট নেই, তাহলে আপনি এটি শুনে খুশি হবেন যে Cloud Firestore থেকে/এ পড়ার বা লেখার জন্য কোড সামঞ্জস্য করার প্রয়োজন নেই: এই সমস্ত কিছু আমরা কোডের সাথে কাজ করি প্রাথমিক বিভাগে লিখেছি।
অ্যারে
কখনও কখনও, আমরা একটি নথিতে মান সংগ্রহ করতে চাই। একটি বইয়ের জেনারগুলি একটি ভাল উদাহরণ: The Hitchhiker's Guide to the Galaxy- এর মতো একটি বই বিভিন্ন বিভাগে পড়তে পারে — এই ক্ষেত্রে "Sci-Fi" এবং "কমেডি":
Cloud Firestore , আমরা মানগুলির একটি অ্যারে ব্যবহার করে এটিকে মডেল করতে পারি। এটি যেকোন কোডেবল প্রকারের জন্য সমর্থিত (যেমন String
, Int
, ইত্যাদি)। আমাদের Book
মডেলে জেনারের একটি অ্যারে কীভাবে যুক্ত করা যায় তা নিম্নলিখিতটি দেখায়:
public struct BookWithGenre: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var genres: [String]
}
যেহেতু এটি যেকোনো কোডেবল টাইপের জন্য কাজ করে, তাই আমরা কাস্টম প্রকারগুলিও ব্যবহার করতে পারি। কল্পনা করুন আমরা প্রতিটি বইয়ের জন্য ট্যাগের একটি তালিকা সংরক্ষণ করতে চাই। ট্যাগের নামের সাথে, আমরা ট্যাগের রঙও সংরক্ষণ করতে চাই, যেমন:
এইভাবে ট্যাগগুলি সংরক্ষণ করার জন্য, আমাদের যা করতে হবে তা হল একটি ট্যাগ প্রতিনিধিত্ব করার জন্য একটি Tag
স্ট্রাকট প্রয়োগ করা এবং এটিকে কোডেবল করা:
struct Tag: Codable, Hashable {
var title: String
var color: String
}
এবং ঠিক তেমনই, আমরা আমাদের Book
নথিতে Tags
একটি অ্যারে সংরক্ষণ করতে পারি!
struct BookWithTags: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var tags: [Tag]
}
ডকুমেন্ট আইডি ম্যাপিং সম্পর্কে একটি দ্রুত শব্দ
আরও টাইপ ম্যাপ করার আগে, আসুন কিছুক্ষণের জন্য ডকুমেন্ট আইডি ম্যাপিং সম্পর্কে কথা বলি।
আমাদের Cloud Firestore ডকুমেন্টের ডকুমেন্ট আইডিকে আমাদের সুইফট ধরনের id
প্রপার্টিতে ম্যাপ করতে আমরা পূর্ববর্তী কিছু উদাহরণে @DocumentID
প্রপার্টি র্যাপার ব্যবহার করেছি। এটি বেশ কয়েকটি কারণে গুরুত্বপূর্ণ:
- ব্যবহারকারী স্থানীয় পরিবর্তন করলে কোন নথি আপডেট করতে হবে তা জানতে এটি আমাদের সাহায্য করে।
- সুইফটইউআই-এর
List
উপাদানগুলিকেIdentifiable
হতে হবে যাতে উপাদানগুলি ঢোকানোর সময় চারপাশে লাফাতে না পারে।
এটি উল্লেখ করার মতো যে @DocumentID
হিসাবে চিহ্নিত একটি বৈশিষ্ট্য Cloud Firestore এনকোডার দ্বারা নথিটি লেখার সময় এনকোড করা হবে না। কারণ ডকুমেন্ট আইডি ডকুমেন্টেরই কোনো অ্যাট্রিবিউট নয়—তাই ডকুমেন্টে লেখাটা ভুল হবে।
নেস্টেড প্রকারের সাথে কাজ করার সময় (যেমন এই নির্দেশিকায় আগের উদাহরণে Book
ট্যাগগুলির অ্যারে), এটি একটি @DocumentID
বৈশিষ্ট্য যোগ করার প্রয়োজন নেই: নেস্টেড বৈশিষ্ট্যগুলি Cloud Firestore নথির একটি অংশ, এবং গঠন করে না একটি পৃথক নথি। অতএব, তাদের একটি নথি আইডির প্রয়োজন নেই।
তারিখ এবং সময়
তারিখ এবং সময় পরিচালনা করার জন্য Cloud Firestore একটি অন্তর্নির্মিত ডেটা টাইপ রয়েছে এবং কোডেবলের জন্য Cloud Firestore সমর্থনের জন্য ধন্যবাদ, সেগুলি ব্যবহার করা সহজ।
আসুন এই নথিটি একবার দেখে নেওয়া যাক যা 1843 সালে উদ্ভাবিত সমস্ত প্রোগ্রামিং ভাষার জননী অ্যাডাকে প্রতিনিধিত্ব করে:
এই নথির ম্যাপিংয়ের জন্য একটি সুইফ্ট টাইপ এইরকম দেখতে পারে:
struct ProgrammingLanguage: Codable {
@DocumentID var id: String?
var name: String
var year: Date
}
@ServerTimestamp
সম্পর্কে কথোপকথন না করে আমরা তারিখ এবং সময় সম্পর্কে এই বিভাগটি ছেড়ে যেতে পারি না। আপনার অ্যাপে টাইমস্ট্যাম্প নিয়ে কাজ করার ক্ষেত্রে এই সম্পত্তির মোড়ক একটি পাওয়ার হাউস।
যেকোন ডিস্ট্রিবিউটেড সিস্টেমে, আলাদা আলাদা সিস্টেমের ঘড়িগুলো সব সময় সম্পূর্ণ সিঙ্ক না হওয়ার সম্ভাবনা থাকে। আপনি ভাবতে পারেন এটি একটি বড় বিষয় নয়, তবে একটি স্টক ট্রেড সিস্টেমের জন্য একটি ঘড়ির কিছুটা সিঙ্কের বাইরে চলার প্রভাব কল্পনা করুন: এমনকি একটি মিলিসেকেন্ডের বিচ্যুতিও একটি ট্রেড নির্বাহ করার সময় মিলিয়ন ডলারের পার্থক্য হতে পারে৷
Cloud Firestore @ServerTimestamp
দ্বারা চিহ্নিত বৈশিষ্ট্যগুলিকে নিম্নরূপ পরিচালনা করে: যদি আপনি এটি সংরক্ষণ করার সময় বৈশিষ্ট্যটি nil
হয় (উদাহরণস্বরূপ addDocument()
ব্যবহার করে), Cloud Firestore ডাটাবেসে লেখার সময় বর্তমান সার্ভার টাইমস্ট্যাম্প সহ ক্ষেত্রটি পূরণ করবে . আপনি addDocument()
বা updateData()
কল করার সময় ক্ষেত্রটি nil
না হলে, Cloud Firestore অ্যাট্রিবিউটের মানটিকে স্পর্শ না করে রেখে দেবে। এইভাবে, createdAt
এবং lastUpdatedAt
মতো ক্ষেত্রগুলি বাস্তবায়ন করা সহজ।
জিওপয়েন্ট
জিওলোকেশন আমাদের অ্যাপে সর্বব্যাপী। অনেক উত্তেজনাপূর্ণ বৈশিষ্ট্য তাদের সংরক্ষণ করে সম্ভব হয়ে ওঠে. উদাহরণস্বরূপ, একটি কাজের জন্য একটি অবস্থান সংরক্ষণ করা দরকারী হতে পারে যাতে আপনি একটি গন্তব্যে পৌঁছালে আপনার অ্যাপটি আপনাকে একটি টাস্ক সম্পর্কে মনে করিয়ে দিতে পারে।
Cloud Firestore একটি অন্তর্নির্মিত ডেটা টাইপ রয়েছে, GeoPoint
, যা যেকোনো অবস্থানের দ্রাঘিমাংশ এবং অক্ষাংশ সংরক্ষণ করতে পারে। একটি Cloud Firestore ডকুমেন্ট থেকে/তে অবস্থান ম্যাপ করতে, আমরা GeoPoint
টাইপ ব্যবহার করতে পারি:
struct Office: Codable {
@DocumentID var id: String?
var name: String
var location: GeoPoint
}
সুইফ্টের সংশ্লিষ্ট ধরনটি হল CLLocationCoordinate2D
, এবং আমরা নিম্নলিখিত ক্রিয়াকলাপের মাধ্যমে এই দুটি প্রকারের মধ্যে মানচিত্র করতে পারি:
CLLocationCoordinate2D(latitude: office.location.latitude,
longitude: office.location.longitude)
ভৌত অবস্থান দ্বারা নথি অনুসন্ধান সম্পর্কে আরও জানতে, এই সমাধান নির্দেশিকাটি দেখুন।
Enums
Enums সম্ভবত সুইফটের সবচেয়ে আন্ডাররেটেড ভাষার বৈশিষ্ট্যগুলির মধ্যে একটি; চোখের দেখা ছাড়া তাদের কাছে আরও অনেক কিছু আছে। enums-এর জন্য একটি সাধারণ ব্যবহারের ক্ষেত্রে কিছুর বিচ্ছিন্ন অবস্থার মডেল করা। উদাহরণস্বরূপ, আমরা নিবন্ধগুলি পরিচালনা করার জন্য একটি অ্যাপ লিখতে পারি। একটি নিবন্ধের স্থিতি ট্র্যাক করতে, আমরা একটি enum Status
ব্যবহার করতে চাই:
enum Status: String, Codable {
case draft
case inReview
case approved
case published
}
Cloud Firestore স্থানীয়ভাবে enums সমর্থন করে না (অর্থাৎ, এটি মানগুলির সেট প্রয়োগ করতে পারে না), তবে আমরা এখনও এই সত্যটি ব্যবহার করতে পারি যে enums টাইপ করা যেতে পারে এবং একটি কোডেবল টাইপ চয়ন করতে পারি। এই উদাহরণে, আমরা String
বেছে নিয়েছি, যার মানে Cloud Firestore ডকুমেন্টে সঞ্চয় করার সময় সমস্ত enum মান স্ট্রিং-এ/থেকে ম্যাপ করা হবে।
এবং, যেহেতু সুইফ্ট কাস্টম কাঁচা মান সমর্থন করে, আমরা এমনকি কাস্টমাইজ করতে পারি কোন মানগুলি কোন enum কেসকে নির্দেশ করে। সুতরাং উদাহরণস্বরূপ, যদি আমরা Status.inReview
কেসকে "পর্যালোচনায়" হিসাবে সংরক্ষণ করার সিদ্ধান্ত নিই, তাহলে আমরা উপরের enumটিকে নিম্নরূপ আপডেট করতে পারি:
enum Status: String, Codable {
case draft
case inReview = "in review"
case approved
case published
}
ম্যাপিং কাস্টমাইজ করা
কখনও কখনও, আমরা যে Cloud Firestore ডকুমেন্টগুলি ম্যাপ করতে চাই তার অ্যাট্রিবিউটের নামগুলি আমাদের সুইফটের ডেটা মডেলের বৈশিষ্ট্যগুলির নামের সাথে মেলে না৷ উদাহরণস্বরূপ, আমাদের সহকর্মীদের মধ্যে একজন পাইথন বিকাশকারী হতে পারে এবং তাদের সমস্ত বৈশিষ্ট্যের নামের জন্য snake_case বেছে নেওয়ার সিদ্ধান্ত নিয়েছে৷
চিন্তা করবেন না: কোডেবল আমাদের কভার করেছে!
এই ধরনের ক্ষেত্রে, আমরা CodingKeys
ব্যবহার করতে পারি। এটি একটি enum যা আমরা নির্দিষ্ট বৈশিষ্ট্যগুলি কীভাবে ম্যাপ করা হবে তা নির্দিষ্ট করতে একটি কোডেবল স্ট্রাকটে যোগ করতে পারি।
এই নথি বিবেচনা করুন:
টাইপ String
একটি নাম সম্পত্তি রয়েছে এমন একটি স্ট্রাক্টে এই দস্তাবেজটি মানচিত্র করতে, আমাদের ProgrammingLanguage
স্ট্রাক্টে একটি CodingKeys
এনাম যুক্ত করতে হবে এবং নথিতে বৈশিষ্ট্যের নাম নির্দিষ্ট করতে হবে:
struct ProgrammingLanguage: Codable {
@DocumentID var id: String?
var name: String
var year: Date
enum CodingKeys: String, CodingKey {
case id
case name = "language_name"
case year
}
}
ডিফল্টরূপে, কোডেবল এপিআই আমাদের সুইফট প্রকারের সম্পত্তির নামগুলি Cloud Firestore ডকুমেন্টগুলিতে আমরা মানচিত্রের জন্য চেষ্টা করছি এমন বৈশিষ্ট্যের নামগুলি নির্ধারণ করতে ব্যবহার করবে। সুতরাং যতক্ষণ না বৈশিষ্ট্যের নামগুলি মেলে ততক্ষণ আমাদের কোডিং প্রকারগুলিতে CodingKeys
যুক্ত করার দরকার নেই। যাইহোক, একবার আমরা নির্দিষ্ট ধরণের জন্য CodingKeys
ব্যবহার করি, আমাদের মানচিত্র করতে চাই এমন সমস্ত সম্পত্তির নাম যুক্ত করতে হবে।
উপরের কোড স্নিপেটে, আমরা একটি id
সম্পত্তি সংজ্ঞায়িত করেছি যা আমরা সুইফটুই List
ভিউতে সনাক্তকারী হিসাবে ব্যবহার করতে চাই। যদি আমরা এটি CodingKeys
নির্দিষ্ট না করে থাকি তবে ডেটা আনার সময় এটি ম্যাপ করা হবে না এবং এইভাবে nil
হয়ে যায়। এর ফলে List
ভিউটি প্রথম নথিতে ভরাট হবে।
ম্যাপিং প্রক্রিয়া চলাকালীন সংশ্লিষ্ট CodingKeys
এনামে কেস হিসাবে তালিকাভুক্ত নয় এমন কোনও সম্পত্তি উপেক্ষা করা হবে। এটি আসলে সুবিধাজনক হতে পারে যদি আমরা বিশেষভাবে কিছু সম্পত্তি ম্যাপ করা থেকে বাদ দিতে চাই।
সুতরাং উদাহরণস্বরূপ, যদি আমরা কারণটিকে ম্যাপ করা থেকে reasonWhyILoveThis
বাদ দিতে চাই তবে আমাদের যা করতে হবে তা হ'ল CodingKeys
এনাম থেকে এটি সরিয়ে ফেলা:
struct ProgrammingLanguage: Identifiable, Codable {
@DocumentID var id: String?
var name: String
var year: Date
var reasonWhyILoveThis: String = ""
enum CodingKeys: String, CodingKey {
case id
case name = "language_name"
case year
}
}
মাঝেমধ্যে আমরা Cloud Firestore নথিতে ফিরে একটি খালি বৈশিষ্ট্য লিখতে চাই। কোনও মানের অনুপস্থিতি বোঝাতে সুইফ্টের বিকল্পগুলির ধারণা রয়েছে এবং Cloud Firestore null
মানগুলিকে সমর্থন করে। যাইহোক, এনকোডিং বিকল্পগুলির জন্য ডিফল্ট আচরণ যা একটি nil
মান রয়েছে তা কেবল সেগুলি বাদ দেওয়া। @ExplicitNull
আমাদের এনকোড করার সময় কীভাবে সুইফট বিকল্পগুলি পরিচালনা করা হয় তার উপর কিছুটা নিয়ন্ত্রণ দেয়: @ExplicitNull
হিসাবে একটি al চ্ছিক সম্পত্তি পতাকাঙ্কিত করে আমরা Cloud Firestore এই সম্পত্তিটি নাল মান দিয়ে নথির সাথে লিখতে বলতে পারি যদি এতে nil
মান থাকে।
ম্যাপিং রঙের জন্য একটি কাস্টম এনকোডার এবং ডিকোডার ব্যবহার করে
কোডেবলের সাথে ম্যাপিং ডেটা আমাদের কভারেজের শেষ বিষয় হিসাবে, আসুন কাস্টম এনকোডার এবং ডিকোডারগুলি প্রবর্তন করি। এই বিভাগটি কোনও নেটিভ Cloud Firestore ডেটাটাইপটি কভার করে না, তবে কাস্টম এনকোডার এবং ডিকোডারগুলি আপনার Cloud Firestore অ্যাপ্লিকেশনগুলিতে ব্যাপকভাবে কার্যকর।
"আমি কীভাবে ম্যাপ করতে পারি রঙগুলি" কেবল Cloud Firestore জন্যই নয়, সুইফট এবং জসনের মধ্যে ম্যাপিংয়ের জন্যও সর্বাধিক ঘন ঘন জিজ্ঞাসিত বিকাশকারী প্রশ্নগুলির মধ্যে একটি। এখানে প্রচুর সমাধান রয়েছে তবে তাদের বেশিরভাগই জেএসএন -তে মনোনিবেশ করে এবং প্রায় সকলেই তার আরজিবি উপাদানগুলির সমন্বয়ে গঠিত নেস্টেড অভিধান হিসাবে রঙগুলিকে ম্যাপ করে।
দেখে মনে হচ্ছে আরও ভাল, সহজ সমাধান হওয়া উচিত। কেন আমরা ওয়েব রঙগুলি ব্যবহার করি না (বা আরও সুনির্দিষ্ট হতে, সিএসএস হেক্স রঙের স্বরলিপি) - এগুলি ব্যবহার করা সহজ (মূলত কেবল একটি স্ট্রিং), এবং তারা এমনকি স্বচ্ছতা সমর্থন করে!
তার হেক্স মানটিতে একটি সুইফট Color
মানচিত্র করতে সক্ষম হতে, আমাদের একটি সুইফট এক্সটেনশন তৈরি করতে হবে যা Color
কোডেবল যুক্ত করে।
extension Color {
init(hex: String) {
let rgba = hex.toRGBA()
self.init(.sRGB,
red: Double(rgba.r),
green: Double(rgba.g),
blue: Double(rgba.b),
opacity: Double(rgba.alpha))
}
//... (code for translating between hex and RGBA omitted for brevity)
}
extension Color: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let hex = try container.decode(String.self)
self.init(hex: hex)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(toHex)
}
}
decoder.singleValueContainer()
ব্যবহার করে, আমরা আরজিবিএ উপাদানগুলিকে বাসা বাঁধতে না পেরে এর Color
সমতুলে একটি String
ডিকোড করতে পারি। এছাড়াও, আপনি প্রথমে রূপান্তর না করে আপনি এই মানগুলি আপনার অ্যাপ্লিকেশনটির ওয়েব ইউআইতে ব্যবহার করতে পারেন!
এটির সাহায্যে আমরা ম্যাপিং ট্যাগগুলির জন্য কোড আপডেট করতে পারি, আমাদের অ্যাপের ইউআই কোডে ম্যানুয়ালি মানচিত্রের পরিবর্তে সরাসরি ট্যাগ রঙগুলি পরিচালনা করা সহজ করে তোলে:
struct Tag: Codable, Hashable {
var title: String
var color: Color
}
struct BookWithTags: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var tags: [Tag]
}
হ্যান্ডলিং ত্রুটি
উপরের কোড স্নিপেটগুলিতে আমরা ইচ্ছাকৃতভাবে ন্যূনতম সময়ে ত্রুটি পরিচালনা করে রেখেছি, তবে একটি উত্পাদন অ্যাপ্লিকেশনটিতে আপনি কোনও ত্রুটিগুলি করুণভাবে পরিচালনা করতে নিশ্চিত করতে চাইবেন।
এখানে একটি কোড স্নিপেট রয়েছে যা দেখায় যে কীভাবে আপনি যে কোনও ত্রুটি পরিস্থিতিগুলি পরিচালনা করতে পারেন তা পরিচালনা করতে হয়:
class MappingSimpleTypesViewModel: ObservableObject {
@Published var book: Book = .empty
@Published var errorMessage: String?
private var db = Firestore.firestore()
func fetchAndMap() {
fetchBook(documentId: "hitchhiker")
}
func fetchAndMapNonExisting() {
fetchBook(documentId: "does-not-exist")
}
func fetchAndTryMappingInvalidData() {
fetchBook(documentId: "invalid-data")
}
private func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument(as: Book.self) { result in
switch result {
case .success(let book):
// A Book value was successfully initialized from the DocumentSnapshot.
self.book = book
self.errorMessage = nil
case .failure(let error):
// A Book value could not be initialized from the DocumentSnapshot.
switch error {
case DecodingError.typeMismatch(_, let context):
self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.valueNotFound(_, let context):
self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.keyNotFound(_, let context):
self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.dataCorrupted(let key):
self.errorMessage = "\(error.localizedDescription): \(key)"
default:
self.errorMessage = "Error decoding document: \(error.localizedDescription)"
}
}
}
}
}
লাইভ আপডেটে ত্রুটিগুলি পরিচালনা করা
পূর্ববর্তী কোড স্নিপেট কোনও একক নথি আনার সময় ত্রুটিগুলি কীভাবে পরিচালনা করতে হয় তা প্রদর্শন করে। একবার ডেটা আনার পাশাপাশি Cloud Firestore তথাকথিত স্ন্যাপশট শ্রোতা ব্যবহার করে আপনার অ্যাপ্লিকেশনগুলিতে আপডেটগুলি সরবরাহ করার পক্ষে সমর্থন করে: আমরা একটি সংগ্রহ (বা ক্যোয়ারী) এ স্ন্যাপশট শ্রোতা নিবন্ধন করতে পারি এবং Cloud Firestore যখনই সেখানে আমাদের শ্রোতাকে কল করবে একটি আপডেট।
এখানে একটি কোড স্নিপেট রয়েছে যা দেখায় যে কীভাবে স্ন্যাপশট শ্রোতা নিবন্ধন করা যায়, কোডযোগ্য ব্যবহার করে মানচিত্রের ডেটা কীভাবে নিবন্ধন করা যায় এবং যে কোনও ত্রুটি ঘটতে পারে তা পরিচালনা করতে পারে। এটি কীভাবে সংগ্রহে একটি নতুন ডকুমেন্ট যুক্ত করতে হয় তাও দেখায়। আপনি যেমন দেখতে পাবেন, ম্যাপযুক্ত নথিগুলি নিজেরাই ধারণ করে স্থানীয় অ্যারে আপডেট করার দরকার নেই, কারণ এটি স্ন্যাপশট শ্রোতার কোড দ্বারা যত্ন নেওয়া হয়।
class MappingColorsViewModel: ObservableObject {
@Published var colorEntries = [ColorEntry]()
@Published var newColor = ColorEntry.empty
@Published var errorMessage: String?
private var db = Firestore.firestore()
private var listenerRegistration: ListenerRegistration?
public func unsubscribe() {
if listenerRegistration != nil {
listenerRegistration?.remove()
listenerRegistration = nil
}
}
func subscribe() {
if listenerRegistration == nil {
listenerRegistration = db.collection("colors")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
self?.errorMessage = "No documents in 'colors' collection"
return
}
self?.colorEntries = documents.compactMap { queryDocumentSnapshot in
let result = Result { try queryDocumentSnapshot.data(as: ColorEntry.self) }
switch result {
case .success(let colorEntry):
if let colorEntry = colorEntry {
// A ColorEntry value was successfully initialized from the DocumentSnapshot.
self?.errorMessage = nil
return colorEntry
}
else {
// A nil value was successfully initialized from the DocumentSnapshot,
// or the DocumentSnapshot was nil.
self?.errorMessage = "Document doesn't exist."
return nil
}
case .failure(let error):
// A ColorEntry value could not be initialized from the DocumentSnapshot.
switch error {
case DecodingError.typeMismatch(_, let context):
self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.valueNotFound(_, let context):
self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.keyNotFound(_, let context):
self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.dataCorrupted(let key):
self?.errorMessage = "\(error.localizedDescription): \(key)"
default:
self?.errorMessage = "Error decoding document: \(error.localizedDescription)"
}
return nil
}
}
}
}
}
func addColorEntry() {
let collectionRef = db.collection("colors")
do {
let newDocReference = try collectionRef.addDocument(from: newColor)
print("ColorEntry stored with new document reference: \(newDocReference)")
}
catch {
print(error)
}
}
}
এই পোস্টে ব্যবহৃত সমস্ত কোড স্নিপেটগুলি এমন একটি নমুনা অ্যাপ্লিকেশনটির অংশ যা আপনি এই গিটহাব সংগ্রহস্থল থেকে ডাউনলোড করতে পারেন।
এগিয়ে যান এবং কোডেবল ব্যবহার করুন!
সুইফটের কোডেবল এপিআই আপনার অ্যাপ্লিকেশন ডেটা মডেল থেকে এবং সিরিয়ালাইজড ফর্ম্যাটগুলি থেকে ডেটা মানচিত্রের জন্য একটি শক্তিশালী এবং নমনীয় উপায় সরবরাহ করে। এই গাইডে, আপনি দেখেছেন যে অ্যাপ্লিকেশনগুলিতে Cloud Firestore তাদের ডেটাস্টোর হিসাবে ব্যবহার করে এটি ব্যবহার করা কতটা সহজ।
সাধারণ ডেটা প্রকারের সাথে একটি প্রাথমিক উদাহরণ থেকে শুরু করে, আমরা ক্রমান্বয়ে ডেটা মডেলের জটিলতা বাড়িয়েছি, সমস্ত সময় আমাদের জন্য ম্যাপিং সম্পাদনের জন্য কোডেবল এবং ফায়ারবেসের বাস্তবায়নের উপর নির্ভর করতে সক্ষম হয়েছি।
কোডেবল সম্পর্কে আরও তথ্যের জন্য, আমি নিম্নলিখিত সংস্থানগুলি সুপারিশ করি:
- জন সানডেলের কোডেবলের বুনিয়াদি সম্পর্কে একটি দুর্দান্ত নিবন্ধ রয়েছে।
- বইগুলি যদি আপনার জিনিস বেশি হয় তবে সুইফট কোডেবলের জন্য ম্যাট এর ফ্লাইট স্কুল গাইড দেখুন।
- এবং অবশেষে, ডনি ওয়ালসের কোডযোগ্য সম্পর্কে একটি সম্পূর্ণ সিরিজ রয়েছে।
যদিও আমরা Cloud Firestore ডকুমেন্টগুলি ম্যাপিংয়ের জন্য একটি বিস্তৃত গাইড সংকলন করার জন্য যথাসাধ্য চেষ্টা করেছি, এটি সম্পূর্ণ নয় এবং আপনি আপনার ধরণের মানচিত্রের জন্য অন্যান্য কৌশলগুলি ব্যবহার করছেন। নীচে প্রেরণ করুন প্রতিক্রিয়া বোতামটি ব্যবহার করে, আপনি অন্যান্য ধরণের Cloud Firestore ডেটা ম্যাপিংয়ের জন্য বা সুইফটে ডেটা উপস্থাপনের জন্য কী কৌশলগুলি ব্যবহার করেন তা আমাদের জানান।
Cloud Firestore কোডেবল সমর্থন ব্যবহার না করার সত্যিই কোনও কারণ নেই।
,সুইফটের কোডেবল এপিআই, সুইফট 4 -এ প্রবর্তিত, আমাদের সংকলকটির শক্তিটি সিরিয়ালাইজড ফর্ম্যাটগুলি থেকে সুইফট প্রকারের ডেটা মানচিত্র করা আরও সহজ করার জন্য আমাদের সক্ষম করে তোলে।
আপনি আপনার অ্যাপ্লিকেশনটির ডেটা মডেল (এবং তদ্বিপরীত) এ ওয়েব এপিআই থেকে ডেটা মানচিত্রের জন্য কোডযোগ্য ব্যবহার করে থাকতে পারেন তবে এটি এর চেয়ে অনেক বেশি নমনীয়।
এই গাইডে, আমরা Cloud Firestore থেকে সুইফট প্রকার এবং তদ্বিপরীত পর্যন্ত ডেটা মানচিত্রের জন্য কীভাবে কোডেবল ব্যবহার করা যেতে পারে তা দেখতে যাচ্ছি।
Cloud Firestore থেকে কোনও দস্তাবেজ আনার সময়, আপনার অ্যাপ্লিকেশনটি কী/মান জোড়াগুলির একটি অভিধান (বা আপনি যদি একাধিক নথি ফিরিয়ে দেওয়ার অপারেশনগুলির মধ্যে একটি ব্যবহার করেন) ব্যবহার করেন) একটি অভিধান পাবেন।
এখন, আপনি অবশ্যই সুইফটে সরাসরি অভিধানগুলি ব্যবহার করা চালিয়ে যেতে পারেন এবং তারা কিছু দুর্দান্ত নমনীয়তা সরবরাহ করে যা আপনার ব্যবহারের ক্ষেত্রে ঠিক তাই হতে পারে। যাইহোক, এই পদ্ধতির সুরক্ষিত টাইপ নয় এবং ভুল বানান বৈশিষ্ট্যযুক্ত নামগুলি দ্বারা হার্ড-টু-ট্র্যাক-ডাউন বাগগুলি প্রবর্তন করা সহজ, বা আপনার দলটি যখন গত সপ্তাহে সেই উত্তেজনাপূর্ণ নতুন বৈশিষ্ট্যটি প্রেরণ করেছে তখন আপনার দলটি যুক্ত নতুন বৈশিষ্ট্যটি মানচিত্র করতে ভুলে যাওয়া ভুলে যাওয়া।
অতীতে, অনেক বিকাশকারীরা একটি সাধারণ ম্যাপিং স্তর প্রয়োগ করে এই ত্রুটিগুলি ঘিরে কাজ করেছেন যা তাদেরকে দ্রুত ধরণের অভিধানের মানচিত্রের জন্য ম্যাপ করার অনুমতি দেয়। তবে আবারও, এই বাস্তবায়নগুলির বেশিরভাগই Cloud Firestore ডকুমেন্ট এবং আপনার অ্যাপের ডেটা মডেলের সংশ্লিষ্ট প্রকারের মধ্যে ম্যাপিংটি ম্যানুয়ালি নির্দিষ্ট করার উপর ভিত্তি করে।
সুইফটের কোডেবল এপিআইয়ের জন্য Cloud Firestore সমর্থন সহ, এটি অনেক সহজ হয়ে যায়:
- আপনাকে আর কোনও ম্যাপিং কোড ম্যানুয়ালি প্রয়োগ করতে হবে না।
- বিভিন্ন নাম সহ বৈশিষ্ট্যগুলি কীভাবে মানচিত্র করা যায় তা নির্ধারণ করা সহজ।
- এটি সুইফটের অনেক ধরণের জন্য অন্তর্নির্মিত সমর্থন রয়েছে।
- এবং কাস্টম প্রকারের ম্যাপিংয়ের জন্য সমর্থন যুক্ত করা সহজ।
- সর্বোপরি: সাধারণ ডেটা মডেলের জন্য, আপনাকে কোনও ম্যাপিং কোড লিখতে হবে না।
ম্যাপিং ডেটা
Cloud Firestore ডকুমেন্টগুলিতে ডেটা সঞ্চয় করে যা মানগুলির কীগুলি মানচিত্র করে। একটি পৃথক নথি থেকে ডেটা আনতে, আমরা DocumentSnapshot.data()
কল করতে পারি, যা মাঠের নামগুলিকে Any
ম্যাপিং করে একটি অভিধান দেয়: func data() -> [String : Any]?
.
এর অর্থ আমরা প্রতিটি পৃথক ক্ষেত্র অ্যাক্সেস করতে সুইফটের সাবস্ক্রিপ্ট সিনট্যাক্স ব্যবহার করতে পারি।
import FirebaseFirestore
#warning("DO NOT MAP YOUR DOCUMENTS MANUALLY. USE CODABLE INSTEAD.")
func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument { document, error in
if let error = error as NSError? {
self.errorMessage = "Error getting document: \(error.localizedDescription)"
}
else {
if let document = document {
let id = document.documentID
let data = document.data()
let title = data?["title"] as? String ?? ""
let numberOfPages = data?["numberOfPages"] as? Int ?? 0
let author = data?["author"] as? String ?? ""
self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author)
}
}
}
}
যদিও এটি সহজবোধ্য এবং বাস্তবায়ন করা সহজ বলে মনে হতে পারে তবে এই কোডটি ভঙ্গুর, বজায় রাখা শক্ত এবং ত্রুটি-প্রবণ।
আপনি দেখতে পাচ্ছেন, আমরা ডকুমেন্ট ক্ষেত্রগুলির ডেটা ধরণের সম্পর্কে অনুমান করছি। এগুলি সঠিক হতে পারে বা নাও হতে পারে।
মনে রাখবেন, যেহেতু কোনও স্কিমা নেই, আপনি সহজেই সংগ্রহে একটি নতুন ডকুমেন্ট যুক্ত করতে পারেন এবং ক্ষেত্রের জন্য একটি আলাদা ধরণের চয়ন করতে পারেন। আপনি দুর্ঘটনাক্রমে numberOfPages
ক্ষেত্রের জন্য স্ট্রিং চয়ন করতে পারেন, যার ফলে ম্যাপিং ইস্যু একটি কঠিন থেকে তৈরি হতে পারে। এছাড়াও, যখনই কোনও নতুন ক্ষেত্র যুক্ত করা হয় তখন আপনাকে আপনার ম্যাপিং কোডটি আপডেট করতে হবে, যা বরং জটিল।
এবং আসুন আমরা ভুলে যাবেন না যে আমরা সুইফটের শক্তিশালী টাইপ সিস্টেমের সুবিধা নিচ্ছি না, যা Book
প্রতিটি বৈশিষ্ট্যের জন্য ঠিক সঠিক ধরণের জানে।
কোডেবল কী, যাইহোক?
অ্যাপলের ডকুমেন্টেশন অনুসারে, কোডেবল হ'ল "এমন একটি প্রকার যা নিজেকে বাহ্যিক উপস্থাপনায় এবং বাইরে রূপান্তর করতে পারে।" প্রকৃতপক্ষে, কোডেবল হ'ল এনকোডেবল এবং ডিকোডেবল প্রোটোকলের জন্য একটি টাইপ ওরফ। এই প্রোটোকলটিতে একটি সুইফট টাইপের সাথে সামঞ্জস্য করে, সংকলকটি জেএসওএন -এর মতো সিরিয়ালাইজড ফর্ম্যাট থেকে এই ধরণের একটি উদাহরণ এনকোড/ডিকোড করার জন্য প্রয়োজনীয় কোডটি সংশ্লেষিত করবে।
কোনও বই সম্পর্কে ডেটা সংরক্ষণের জন্য একটি সাধারণ প্রকারটি দেখতে এরকম দেখতে পারে:
struct Book: Codable {
var title: String
var numberOfPages: Int
var author: String
}
আপনি দেখতে পাচ্ছেন, কোডযোগ্য টাইপটি মেনে চলাই ন্যূনতম আক্রমণাত্মক। আমাদের কেবল প্রোটোকলের সাথে সামঞ্জস্যতা যুক্ত করতে হয়েছিল; অন্য কোনও পরিবর্তনের প্রয়োজন ছিল না।
এটি জায়গায়, আমরা এখন সহজেই একটি জসন অবজেক্টে একটি বই এনকোড করতে পারি:
do {
let book = Book(title: "The Hitchhiker's Guide to the Galaxy",
numberOfPages: 816,
author: "Douglas Adams")
let encoder = JSONEncoder()
let data = try encoder.encode(book)
}
catch {
print("Error when trying to encode book: \(error)")
}
কোনও Book
উদাহরণে একটি জসন অবজেক্ট ডিকোডিং নিম্নলিখিত হিসাবে কাজ করে:
let decoder = JSONDecoder()
let data = /* fetch data from the network */
let decodedBook = try decoder.decode(Book.self, from: data)
Cloud Firestore ডকুমেন্টগুলিতে সাধারণ ধরণের ম্যাপিং
কোডেবল ব্যবহার করে
Cloud Firestore সাধারণ স্ট্রিং থেকে শুরু করে নেস্টেড মানচিত্র পর্যন্ত ডেটা ধরণের একটি বিস্তৃত সেটকে সমর্থন করে। এগুলির বেশিরভাগই সরাসরি সুইফটের অন্তর্নির্মিত প্রকারের সাথে মিলে যায়। আসুন আমরা আরও জটিলগুলিতে ডুব দেওয়ার আগে প্রথমে কিছু সাধারণ ডেটা প্রকারের ম্যাপিংয়ের দিকে একবার নজর রাখি।
Cloud Firestore ডকুমেন্টগুলিকে দ্রুত ধরণের ম্যাপ করতে, এই পদক্ষেপগুলি অনুসরণ করুন:
- নিশ্চিত হয়ে নিন যে আপনি আপনার প্রকল্পে
FirebaseFirestore
ফ্রেমওয়ার্ক যুক্ত করেছেন। আপনি এটি করতে সুইফট প্যাকেজ ম্যানেজার বা কোকোপডগুলি ব্যবহার করতে পারেন। - আপনার সুইফট ফাইলে
FirebaseFirestore
আমদানি করুন। - আপনার প্রকারটি
Codable
মেনে চলুন। - (Al চ্ছিক, আপনি যদি কোনও
List
ভিউতে টাইপটি ব্যবহার করতে চান) আপনার প্রকারে একটিid
সম্পত্তি যুক্ত করুন এবং ডকুমেন্ট আইডিতে এটি মানচিত্রের জন্য Cloud Firestore বলতে@DocumentID
ব্যবহার করুন। আমরা নীচে আরো বিস্তারিত আলোচনা করব. - একটি সুইফট প্রকারের একটি ডকুমেন্ট রেফারেন্স মানচিত্র করতে
documentReference.data(as: )
ব্যবহার করুন। - সুইফট প্রকার থেকে Cloud Firestore ডকুমেন্টে ডেটা মানচিত্র করতে
documentReference.setData(from: )
ব্যবহার করুন। - (Al চ্ছিক, তবে উচ্চ প্রস্তাবিত) যথাযথ ত্রুটি হ্যান্ডলিং প্রয়োগ করুন।
আসুন সেই অনুযায়ী আমাদের Book
ধরণ আপডেট করুন:
struct Book: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
}
যেহেতু এই ধরণের ইতিমধ্যে কোডযোগ্য ছিল, তাই আমাদের কেবল id
সম্পত্তি যুক্ত করতে হয়েছিল এবং এটি @DocumentID
সম্পত্তি মোড়কের সাথে টীকা দিতে হয়েছিল।
কোনও দস্তাবেজ আনতে এবং ম্যাপিংয়ের জন্য পূর্ববর্তী কোড স্নিপেট গ্রহণ করে, আমরা সমস্ত ম্যানুয়াল ম্যাপিং কোডকে একটি একক লাইনের সাথে প্রতিস্থাপন করতে পারি:
func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument { document, error in
if let error = error as NSError? {
self.errorMessage = "Error getting document: \(error.localizedDescription)"
}
else {
if let document = document {
do {
self.book = try document.data(as: Book.self)
}
catch {
print(error)
}
}
}
}
}
getDocument(as:)
কল করার সময় আপনি নথির ধরণটি নির্দিষ্ট করে আরও সংক্ষিপ্তভাবে লিখতে পারেন। এটি আপনার জন্য ম্যাপিং সম্পাদন করবে এবং ম্যাপযুক্ত নথিটিযুক্ত Result
ধরণটি ফিরিয়ে দেবে, বা ডিকোডিং ব্যর্থ ক্ষেত্রে একটি ত্রুটি:
private func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument(as: Book.self) { result in
switch result {
case .success(let book):
// A Book value was successfully initialized from the DocumentSnapshot.
self.book = book
self.errorMessage = nil
case .failure(let error):
// A Book value could not be initialized from the DocumentSnapshot.
self.errorMessage = "Error decoding document: \(error.localizedDescription)"
}
}
}
একটি বিদ্যমান ডকুমেন্ট আপডেট করা documentReference.setData(from: )
কল করার মতো সহজ। কিছু প্রাথমিক ত্রুটি পরিচালনা সহ, একটি Book
উদাহরণ সংরক্ষণ করার কোডটি এখানে:
func updateBook(book: Book) {
if let id = book.id {
let docRef = db.collection("books").document(id)
do {
try docRef.setData(from: book)
}
catch {
print(error)
}
}
}
একটি নতুন ডকুমেন্ট যুক্ত করার সময়, Cloud Firestore স্বয়ংক্রিয়ভাবে নথিতে একটি নতুন ডকুমেন্ট আইডি বরাদ্দ করার যত্ন নেবে। এমনকি অ্যাপটি বর্তমানে অফলাইনে থাকলেও এটি কাজ করে।
func addBook(book: Book) {
let collectionRef = db.collection("books")
do {
let newDocReference = try collectionRef.addDocument(from: self.book)
print("Book stored with new document reference: \(newDocReference)")
}
catch {
print(error)
}
}
সাধারণ ডেটা প্রকারের ম্যাপিং ছাড়াও, Cloud Firestore বেশ কয়েকটি অন্যান্য ডেটাটাইপগুলিকে সমর্থন করে, যার মধ্যে কয়েকটি কাঠামোগত প্রকার যা আপনি কোনও নথির ভিতরে নেস্টেড অবজেক্ট তৈরি করতে ব্যবহার করতে পারেন।
নেস্টেড কাস্টম প্রকার
আমাদের নথিতে আমরা মানচিত্র করতে চাই এমন বেশিরভাগ বৈশিষ্ট্য হ'ল বইয়ের শিরোনাম বা লেখকের নাম হিসাবে সহজ মান। কিন্তু যখন আমাদের আরও জটিল বস্তু সংরক্ষণ করা দরকার তখন সেই মামলাগুলি সম্পর্কে কী হবে? উদাহরণস্বরূপ, আমরা বিভিন্ন রেজোলিউশনে বইয়ের কভারে ইউআরএলগুলি সংরক্ষণ করতে চাই।
Cloud Firestore এটি করার সবচেয়ে সহজ উপায় হ'ল একটি মানচিত্র ব্যবহার করা:
সংশ্লিষ্ট সুইফট স্ট্রাক্টটি লেখার সময়, আমরা Cloud Firestore ইউআরএলগুলিকে সমর্থন করে এমনটি ব্যবহার করতে পারি - যখন একটি ইউআরএল রয়েছে এমন একটি ক্ষেত্র সংরক্ষণ করার সময় এটি একটি স্ট্রিং এবং তার বিপরীতে রূপান্তরিত হবে:
struct CoverImages: Codable {
var small: URL
var medium: URL
var large: URL
}
struct BookWithCoverImages: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var cover: CoverImages?
}
Cloud Firestore নথিতে কভার মানচিত্রের জন্য আমরা কীভাবে একটি স্ট্রাক্ট, CoverImages
সংজ্ঞায়িত করেছি তা লক্ষ্য করুন। Booket চ্ছিক হিসাবে BookWithCoverImages
কভার সম্পত্তি চিহ্নিত করে আমরা এই সত্যটি পরিচালনা করতে সক্ষম হয়েছি যে কিছু নথিতে কোনও কভার অ্যাট্রিবিউট না থাকতে পারে।
আপনি যদি কৌতূহলী হন তবে কেন ডেটা আনার বা আপডেট করার জন্য কোনও কোড স্নিপেট নেই তবে আপনি শুনে সন্তুষ্ট হবেন যে/ Cloud Firestore থেকে/থেকে লেখার জন্য কোডটি সামঞ্জস্য করার দরকার নেই: এই সমস্তই কোডের সাথে কাজ করে প্রাথমিক বিভাগে লিখেছেন।
অ্যারে
কখনও কখনও, আমরা একটি নথিতে মানগুলির সংগ্রহ সংরক্ষণ করতে চাই। একটি বইয়ের জেনারগুলি একটি ভাল উদাহরণ: গ্যালাক্সির জন্য হিচিকার গাইডের মতো একটি বই বেশ কয়েকটি বিভাগে পড়তে পারে-এই ক্ষেত্রে "সাই-ফাই" এবং "কমেডি":
Cloud Firestore , আমরা মানগুলির একটি অ্যারে ব্যবহার করে এটি মডেল করতে পারি। এটি কোনও কোডেবল ধরণের (যেমন String
, Int
ইত্যাদি) জন্য সমর্থিত। নিম্নলিখিতগুলি কীভাবে আমাদের Book
মডেলটিতে জেনারগুলির একটি অ্যারে যুক্ত করবেন তা দেখায়:
public struct BookWithGenre: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var genres: [String]
}
যেহেতু এটি কোনও কোডেবল ধরণের জন্য কাজ করে, তাই আমরা কাস্টম প্রকারগুলিও ব্যবহার করতে পারি। কল্পনা করুন যে আমরা প্রতিটি বইয়ের জন্য ট্যাগগুলির একটি তালিকা সংরক্ষণ করতে চাই। ট্যাগের নাম সহ, আমরা ট্যাগের রঙটিও সংরক্ষণ করতে চাই, এর মতো:
এইভাবে ট্যাগগুলি সঞ্চয় করতে, আমাদের যা করতে হবে তা হ'ল একটি ট্যাগ উপস্থাপন করতে এবং এটি কোডযোগ্য করে তুলতে একটি Tag
স্ট্রাক্ট প্রয়োগ করতে হবে:
struct Tag: Codable, Hashable {
var title: String
var color: String
}
এবং ঠিক এর মতোই, আমরা আমাদের Book
নথিতে Tags
একটি অ্যারে সঞ্চয় করতে পারি!
struct BookWithTags: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var tags: [Tag]
}
ডকুমেন্ট আইডি ম্যাপিং সম্পর্কে একটি দ্রুত শব্দ
আমরা আরও ধরণের ম্যাপিংয়ে যাওয়ার আগে, আসুন এক মুহুর্তের জন্য ডকুমেন্ট আইডি ম্যাপিং সম্পর্কে কথা বলি।
আমরা আমাদের Cloud Firestore ডকুমেন্টগুলির ডকুমেন্ট আইডিটি আমাদের সুইফট প্রকারের id
সম্পত্তিতে মানচিত্রের জন্য পূর্ববর্তী কয়েকটি উদাহরণে @DocumentID
সম্পত্তি মোড়ক ব্যবহার করেছি। এটি বেশ কয়েকটি কারণে গুরুত্বপূর্ণ:
- ব্যবহারকারী স্থানীয় পরিবর্তন আনলে কোন দস্তাবেজ আপডেট করতে হবে তা আমাদের জানতে সহায়তা করে।
- সুইফটুইয়ের
List
উপাদানগুলি প্রবেশ করানো হলে চারপাশে ঝাঁপিয়ে পড়া থেকে রোধ করার জন্য এর উপাদানগুলিIdentifiable
হওয়া প্রয়োজন।
এটি উল্লেখ করার মতো বিষয় যে @DocumentID
হিসাবে চিহ্নিত একটি বৈশিষ্ট্যটি ডকুমেন্টটি আবার লেখার সময় Cloud Firestore এনকোডার দ্বারা এনকোড করা হবে না। এটি কারণ ডকুমেন্ট আইডি নিজেই ডকুমেন্টের কোনও বৈশিষ্ট্য নয় - সুতরাং এটি নথিতে লেখা ভুল হবে।
নেস্টেড প্রকারের সাথে কাজ করার সময় (যেমন এই গাইডের পূর্ববর্তী উদাহরণে Book
ট্যাগের অ্যারে), এটি একটি @DocumentID
সম্পত্তি যুক্ত করার প্রয়োজন হয় না: নেস্টেড বৈশিষ্ট্যগুলি Cloud Firestore ডকুমেন্টের একটি অংশ, এবং গঠন করে না একটি পৃথক দলিল। সুতরাং, তাদের কোনও ডকুমেন্ট আইডি দরকার নেই।
তারিখ এবং সময়
Cloud Firestore তারিখগুলি এবং সময়গুলি পরিচালনা করার জন্য একটি অন্তর্নির্মিত ডেটা টাইপ রয়েছে এবং Cloud Firestore কোডেবলের জন্য সমর্থনকে ধন্যবাদ, এগুলি ব্যবহার করা সহজ।
আসুন এই দস্তাবেজটি একবার দেখে নিই যা 1843 সালে উদ্ভাবিত সমস্ত প্রোগ্রামিং ভাষার মায়ের প্রতিনিধিত্ব করে:
এই দস্তাবেজটি ম্যাপিংয়ের জন্য একটি সুইফট টাইপ এর মতো দেখতে পারে:
struct ProgrammingLanguage: Codable {
@DocumentID var id: String?
var name: String
var year: Date
}
আমরা এই বিভাগটি @ServerTimestamp
সম্পর্কে কথোপকথন না করে তারিখ এবং সময় সম্পর্কে ছেড়ে যেতে পারি না। আপনার অ্যাপ্লিকেশনটিতে টাইমস্ট্যাম্পগুলি নিয়ে কাজ করার ক্ষেত্রে এই সম্পত্তি মোড়ক একটি পাওয়ার হাউস।
যে কোনও বিতরণ সিস্টেমে, সম্ভাবনাগুলি হ'ল পৃথক সিস্টেমের ঘড়িগুলি পুরোপুরি সিঙ্কে সম্পূর্ণ হয় না। আপনি মনে করতে পারেন এটি কোনও বড় বিষয় নয়, তবে স্টক ট্রেড সিস্টেমের জন্য সিঙ্কের বাইরে কিছুটা চলমান ঘড়ির প্রভাবগুলি কল্পনা করুন: এমনকি মিলিসেকেন্ড বিচ্যুতির ফলে কোনও বাণিজ্য কার্যকর করার সময় কয়েক মিলিয়ন ডলারের পার্থক্য হতে পারে।
Cloud Firestore @ServerTimestamp
সাথে চিহ্নিত বৈশিষ্ট্যগুলি হ্যান্ডল করে নিম্নরূপ: আপনি যখন এটি সঞ্চয় করেন (উদাহরণস্বরূপ addDocument()
ব্যবহার করে) বৈশিষ্ট্যটি যদি nil
হয় তবে Cloud Firestore ডাটাবেসে লেখার সময় বর্তমান সার্ভার টাইমস্ট্যাম্পের সাথে ক্ষেত্রটি জনপ্রিয় করবে . আপনি যখন addDocument()
বা updateData()
কল করবেন তখন ক্ষেত্রটি যদি nil
না হয় তবে Cloud Firestore অ্যাটাক্ট মানটি ছোঁয়া দেবে। এইভাবে, createdAt
এবং lastUpdatedAt
মতো ক্ষেত্রগুলি প্রয়োগ করা সহজ।
জিওপয়েন্টস
জিওলোকেশনগুলি আমাদের অ্যাপ্লিকেশনগুলিতে সর্বব্যাপী। অনেকগুলি উত্তেজনাপূর্ণ বৈশিষ্ট্যগুলি সংরক্ষণ করে সম্ভব হয়। উদাহরণস্বরূপ, কোনও টাস্কের জন্য কোনও অবস্থান সংরক্ষণ করা কার্যকর হতে পারে যাতে আপনার অ্যাপ্লিকেশন আপনাকে কোনও গন্তব্যে পৌঁছানোর সময় কোনও কার্য সম্পর্কে আপনাকে স্মরণ করিয়ে দিতে পারে।
Cloud Firestore একটি অন্তর্নির্মিত ডেটা টাইপ রয়েছে, GeoPoint
, যা কোনও অবস্থানের দ্রাঘিমাংশ এবং অক্ষাংশ সঞ্চয় করতে পারে। Cloud Firestore ডকুমেন্ট থেকে/থেকে অবস্থানগুলি মানচিত্র করতে, আমরা GeoPoint
প্রকারটি ব্যবহার করতে পারি:
struct Office: Codable {
@DocumentID var id: String?
var name: String
var location: GeoPoint
}
সুইফটে সংশ্লিষ্ট প্রকারটি হ'ল CLLocationCoordinate2D
, এবং আমরা নিম্নলিখিত অপারেশন সহ এই দুটি ধরণের মধ্যে মানচিত্র করতে পারি:
CLLocationCoordinate2D(latitude: office.location.latitude,
longitude: office.location.longitude)
শারীরিক অবস্থান অনুসারে নথিগুলি অনুসন্ধান করার বিষয়ে আরও জানতে, এই সমাধান গাইডটি দেখুন।
Enums
এনামগুলি সম্ভবত সুইফ্টের অন্যতম আন্ডাররেটেড ভাষার বৈশিষ্ট্য; তাদের চোখের সাথে দেখা করার চেয়ে আরও অনেক কিছুই রয়েছে। এনামগুলির জন্য একটি সাধারণ ব্যবহারের কেস হ'ল কোনও কিছুর বিচ্ছিন্ন রাষ্ট্রগুলিকে মডেল করা। উদাহরণস্বরূপ, আমরা নিবন্ধগুলি পরিচালনার জন্য একটি অ্যাপ্লিকেশন লিখতে পারি। একটি নিবন্ধের স্থিতি ট্র্যাক করতে, আমরা একটি এনাম Status
ব্যবহার করতে চাই:
enum Status: String, Codable {
case draft
case inReview
case approved
case published
}
Cloud Firestore স্থানীয়ভাবে এনামগুলিকে সমর্থন করে না (অর্থাত্ এটি মানগুলির সেট প্রয়োগ করতে পারে না) তবে আমরা এখনও এনামগুলি টাইপ করা যায় এবং একটি কোডেবল টাইপ চয়ন করতে পারি তা ব্যবহার করতে পারি। এই উদাহরণে, আমরা String
বেছে নিয়েছি, যার অর্থ Cloud Firestore ডকুমেন্টে সংরক্ষণ করা হলে সমস্ত এনাম মানগুলি স্ট্রিং থেকে/থেকে ম্যাপ করা হবে।
এবং, যেহেতু সুইফট কাস্টম কাঁচা মানগুলিকে সমর্থন করে, তাই আমরা এমনকি কাস্টমাইজ করতে পারি যে কোন মানগুলি এনাম কেসকে বোঝায়। সুতরাং উদাহরণস্বরূপ, যদি আমরা Status.inReview
সংরক্ষণ করার সিদ্ধান্ত নিয়েছি reviewie
enum Status: String, Codable {
case draft
case inReview = "in review"
case approved
case published
}
ম্যাপিং কাস্টমাইজিং
কখনও কখনও, আমরা মানচিত্র করতে চাই Cloud Firestore ডকুমেন্টগুলির বৈশিষ্ট্যযুক্ত নামগুলি সুইফটে আমাদের ডেটা মডেলের বৈশিষ্ট্যগুলির নামগুলির সাথে মেলে না। উদাহরণস্বরূপ, আমাদের সহকর্মীদের মধ্যে একজন পাইথন বিকাশকারী হতে পারেন এবং তাদের সমস্ত বৈশিষ্ট্যের নামের জন্য স্নেক_কেস চয়ন করার সিদ্ধান্ত নিয়েছেন।
চিন্তা করার দরকার নেই: কোডেবল আমাদের covered েকে রেখেছে!
এই জাতীয় ক্ষেত্রে, আমরা CodingKeys
ব্যবহার করতে পারি। এটি একটি এনাম যা আমরা নির্দিষ্ট বৈশিষ্ট্যগুলি কীভাবে ম্যাপ করা হবে তা নির্দিষ্ট করার জন্য একটি কোডেবল স্ট্রাক্টে যুক্ত করতে পারি।
এই দস্তাবেজটি বিবেচনা করুন:
টাইপ String
একটি নাম সম্পত্তি রয়েছে এমন একটি স্ট্রাক্টে এই দস্তাবেজটি মানচিত্র করতে, আমাদের ProgrammingLanguage
স্ট্রাক্টে একটি CodingKeys
এনাম যুক্ত করতে হবে এবং নথিতে বৈশিষ্ট্যের নাম নির্দিষ্ট করতে হবে:
struct ProgrammingLanguage: Codable {
@DocumentID var id: String?
var name: String
var year: Date
enum CodingKeys: String, CodingKey {
case id
case name = "language_name"
case year
}
}
ডিফল্টরূপে, কোডেবল এপিআই আমাদের সুইফট প্রকারের সম্পত্তির নামগুলি Cloud Firestore ডকুমেন্টগুলিতে আমরা মানচিত্রের জন্য চেষ্টা করছি এমন বৈশিষ্ট্যের নামগুলি নির্ধারণ করতে ব্যবহার করবে। সুতরাং যতক্ষণ না বৈশিষ্ট্যের নামগুলি মেলে ততক্ষণ আমাদের কোডিং প্রকারগুলিতে CodingKeys
যুক্ত করার দরকার নেই। যাইহোক, একবার আমরা নির্দিষ্ট ধরণের জন্য CodingKeys
ব্যবহার করি, আমাদের মানচিত্র করতে চাই এমন সমস্ত সম্পত্তির নাম যুক্ত করতে হবে।
উপরের কোড স্নিপেটে, আমরা একটি id
সম্পত্তি সংজ্ঞায়িত করেছি যা আমরা সুইফটুই List
ভিউতে সনাক্তকারী হিসাবে ব্যবহার করতে চাই। যদি আমরা এটি CodingKeys
নির্দিষ্ট না করে থাকি তবে ডেটা আনার সময় এটি ম্যাপ করা হবে না এবং এইভাবে nil
হয়ে যায়। এর ফলে List
ভিউটি প্রথম নথিতে ভরাট হবে।
ম্যাপিং প্রক্রিয়া চলাকালীন সংশ্লিষ্ট CodingKeys
এনামে কেস হিসাবে তালিকাভুক্ত নয় এমন কোনও সম্পত্তি উপেক্ষা করা হবে। এটি আসলে সুবিধাজনক হতে পারে যদি আমরা বিশেষভাবে কিছু সম্পত্তি ম্যাপ করা থেকে বাদ দিতে চাই।
সুতরাং উদাহরণস্বরূপ, যদি আমরা কারণটিকে ম্যাপ করা থেকে reasonWhyILoveThis
বাদ দিতে চাই তবে আমাদের যা করতে হবে তা হ'ল CodingKeys
এনাম থেকে এটি সরিয়ে ফেলা:
struct ProgrammingLanguage: Identifiable, Codable {
@DocumentID var id: String?
var name: String
var year: Date
var reasonWhyILoveThis: String = ""
enum CodingKeys: String, CodingKey {
case id
case name = "language_name"
case year
}
}
মাঝেমধ্যে আমরা Cloud Firestore নথিতে ফিরে একটি খালি বৈশিষ্ট্য লিখতে চাই। কোনও মানের অনুপস্থিতি বোঝাতে সুইফ্টের বিকল্পগুলির ধারণা রয়েছে এবং Cloud Firestore null
মানগুলিকে সমর্থন করে। যাইহোক, এনকোডিং বিকল্পগুলির জন্য ডিফল্ট আচরণ যা একটি nil
মান রয়েছে তা কেবল সেগুলি বাদ দেওয়া। @ExplicitNull
আমাদের এনকোড করার সময় কীভাবে সুইফট বিকল্পগুলি পরিচালনা করা হয় তার উপর কিছুটা নিয়ন্ত্রণ দেয়: @ExplicitNull
হিসাবে একটি al চ্ছিক সম্পত্তি পতাকাঙ্কিত করে আমরা Cloud Firestore এই সম্পত্তিটি নাল মান দিয়ে নথির সাথে লিখতে বলতে পারি যদি এতে nil
মান থাকে।
ম্যাপিং রঙের জন্য একটি কাস্টম এনকোডার এবং ডিকোডার ব্যবহার করে
কোডেবলের সাথে ম্যাপিং ডেটা আমাদের কভারেজের শেষ বিষয় হিসাবে, আসুন কাস্টম এনকোডার এবং ডিকোডারগুলি প্রবর্তন করি। এই বিভাগটি কোনও নেটিভ Cloud Firestore ডেটাটাইপটি কভার করে না, তবে কাস্টম এনকোডার এবং ডিকোডারগুলি আপনার Cloud Firestore অ্যাপ্লিকেশনগুলিতে ব্যাপকভাবে কার্যকর।
"আমি কীভাবে ম্যাপ করতে পারি রঙগুলি" কেবল Cloud Firestore জন্যই নয়, সুইফট এবং জসনের মধ্যে ম্যাপিংয়ের জন্যও সর্বাধিক ঘন ঘন জিজ্ঞাসিত বিকাশকারী প্রশ্নগুলির মধ্যে একটি। এখানে প্রচুর সমাধান রয়েছে তবে তাদের বেশিরভাগই জেএসএন -তে মনোনিবেশ করে এবং প্রায় সকলেই তার আরজিবি উপাদানগুলির সমন্বয়ে গঠিত নেস্টেড অভিধান হিসাবে রঙগুলিকে ম্যাপ করে।
দেখে মনে হচ্ছে আরও ভাল, সহজ সমাধান হওয়া উচিত। কেন আমরা ওয়েব রঙগুলি ব্যবহার করি না (বা আরও সুনির্দিষ্ট হতে, সিএসএস হেক্স রঙের স্বরলিপি) - এগুলি ব্যবহার করা সহজ (মূলত কেবল একটি স্ট্রিং), এবং তারা এমনকি স্বচ্ছতা সমর্থন করে!
তার হেক্স মানটিতে একটি সুইফট Color
মানচিত্র করতে সক্ষম হতে, আমাদের একটি সুইফট এক্সটেনশন তৈরি করতে হবে যা Color
কোডেবল যুক্ত করে।
extension Color {
init(hex: String) {
let rgba = hex.toRGBA()
self.init(.sRGB,
red: Double(rgba.r),
green: Double(rgba.g),
blue: Double(rgba.b),
opacity: Double(rgba.alpha))
}
//... (code for translating between hex and RGBA omitted for brevity)
}
extension Color: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let hex = try container.decode(String.self)
self.init(hex: hex)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(toHex)
}
}
decoder.singleValueContainer()
ব্যবহার করে, আমরা আরজিবিএ উপাদানগুলিকে বাসা বাঁধতে না পেরে এর Color
সমতুলে একটি String
ডিকোড করতে পারি। এছাড়াও, আপনি প্রথমে রূপান্তর না করে আপনি এই মানগুলি আপনার অ্যাপ্লিকেশনটির ওয়েব ইউআইতে ব্যবহার করতে পারেন!
এটির সাহায্যে আমরা ম্যাপিং ট্যাগগুলির জন্য কোড আপডেট করতে পারি, আমাদের অ্যাপের ইউআই কোডে ম্যানুয়ালি মানচিত্রের পরিবর্তে সরাসরি ট্যাগ রঙগুলি পরিচালনা করা সহজ করে তোলে:
struct Tag: Codable, Hashable {
var title: String
var color: Color
}
struct BookWithTags: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var tags: [Tag]
}
হ্যান্ডলিং ত্রুটি
উপরের কোড স্নিপেটগুলিতে আমরা ইচ্ছাকৃতভাবে ন্যূনতম সময়ে ত্রুটি পরিচালনা করে রেখেছি, তবে একটি উত্পাদন অ্যাপ্লিকেশনটিতে আপনি কোনও ত্রুটিগুলি করুণভাবে পরিচালনা করতে নিশ্চিত করতে চাইবেন।
এখানে একটি কোড স্নিপেট রয়েছে যা দেখায় যে কীভাবে আপনি যে কোনও ত্রুটি পরিস্থিতিগুলি পরিচালনা করতে পারেন তা পরিচালনা করতে হয়:
class MappingSimpleTypesViewModel: ObservableObject {
@Published var book: Book = .empty
@Published var errorMessage: String?
private var db = Firestore.firestore()
func fetchAndMap() {
fetchBook(documentId: "hitchhiker")
}
func fetchAndMapNonExisting() {
fetchBook(documentId: "does-not-exist")
}
func fetchAndTryMappingInvalidData() {
fetchBook(documentId: "invalid-data")
}
private func fetchBook(documentId: String) {
let docRef = db.collection("books").document(documentId)
docRef.getDocument(as: Book.self) { result in
switch result {
case .success(let book):
// A Book value was successfully initialized from the DocumentSnapshot.
self.book = book
self.errorMessage = nil
case .failure(let error):
// A Book value could not be initialized from the DocumentSnapshot.
switch error {
case DecodingError.typeMismatch(_, let context):
self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.valueNotFound(_, let context):
self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.keyNotFound(_, let context):
self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.dataCorrupted(let key):
self.errorMessage = "\(error.localizedDescription): \(key)"
default:
self.errorMessage = "Error decoding document: \(error.localizedDescription)"
}
}
}
}
}
লাইভ আপডেটে ত্রুটিগুলি পরিচালনা করা
পূর্ববর্তী কোড স্নিপেট কোনও একক নথি আনার সময় ত্রুটিগুলি কীভাবে পরিচালনা করতে হয় তা প্রদর্শন করে। একবার ডেটা আনার পাশাপাশি Cloud Firestore তথাকথিত স্ন্যাপশট শ্রোতা ব্যবহার করে আপনার অ্যাপ্লিকেশনগুলিতে আপডেটগুলি সরবরাহ করার পক্ষে সমর্থন করে: আমরা একটি সংগ্রহ (বা ক্যোয়ারী) এ স্ন্যাপশট শ্রোতা নিবন্ধন করতে পারি এবং Cloud Firestore যখনই সেখানে আমাদের শ্রোতাকে কল করবে একটি আপডেট।
এখানে একটি কোড স্নিপেট রয়েছে যা দেখায় যে কীভাবে স্ন্যাপশট শ্রোতা নিবন্ধন করা যায়, কোডযোগ্য ব্যবহার করে মানচিত্রের ডেটা কীভাবে নিবন্ধন করা যায় এবং যে কোনও ত্রুটি ঘটতে পারে তা পরিচালনা করতে পারে। এটি কীভাবে সংগ্রহে একটি নতুন ডকুমেন্ট যুক্ত করতে হয় তাও দেখায়। আপনি যেমন দেখতে পাবেন, ম্যাপযুক্ত নথিগুলি নিজেরাই ধারণ করে স্থানীয় অ্যারে আপডেট করার দরকার নেই, কারণ এটি স্ন্যাপশট শ্রোতার কোড দ্বারা যত্ন নেওয়া হয়।
class MappingColorsViewModel: ObservableObject {
@Published var colorEntries = [ColorEntry]()
@Published var newColor = ColorEntry.empty
@Published var errorMessage: String?
private var db = Firestore.firestore()
private var listenerRegistration: ListenerRegistration?
public func unsubscribe() {
if listenerRegistration != nil {
listenerRegistration?.remove()
listenerRegistration = nil
}
}
func subscribe() {
if listenerRegistration == nil {
listenerRegistration = db.collection("colors")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
self?.errorMessage = "No documents in 'colors' collection"
return
}
self?.colorEntries = documents.compactMap { queryDocumentSnapshot in
let result = Result { try queryDocumentSnapshot.data(as: ColorEntry.self) }
switch result {
case .success(let colorEntry):
if let colorEntry = colorEntry {
// A ColorEntry value was successfully initialized from the DocumentSnapshot.
self?.errorMessage = nil
return colorEntry
}
else {
// A nil value was successfully initialized from the DocumentSnapshot,
// or the DocumentSnapshot was nil.
self?.errorMessage = "Document doesn't exist."
return nil
}
case .failure(let error):
// A ColorEntry value could not be initialized from the DocumentSnapshot.
switch error {
case DecodingError.typeMismatch(_, let context):
self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.valueNotFound(_, let context):
self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.keyNotFound(_, let context):
self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
case DecodingError.dataCorrupted(let key):
self?.errorMessage = "\(error.localizedDescription): \(key)"
default:
self?.errorMessage = "Error decoding document: \(error.localizedDescription)"
}
return nil
}
}
}
}
}
func addColorEntry() {
let collectionRef = db.collection("colors")
do {
let newDocReference = try collectionRef.addDocument(from: newColor)
print("ColorEntry stored with new document reference: \(newDocReference)")
}
catch {
print(error)
}
}
}
এই পোস্টে ব্যবহৃত সমস্ত কোড স্নিপেটগুলি এমন একটি নমুনা অ্যাপ্লিকেশনটির অংশ যা আপনি এই গিটহাব সংগ্রহস্থল থেকে ডাউনলোড করতে পারেন।
এগিয়ে যান এবং কোডেবল ব্যবহার করুন!
সুইফটের কোডেবল এপিআই আপনার অ্যাপ্লিকেশন ডেটা মডেল থেকে এবং সিরিয়ালাইজড ফর্ম্যাটগুলি থেকে ডেটা মানচিত্রের জন্য একটি শক্তিশালী এবং নমনীয় উপায় সরবরাহ করে। এই গাইডে, আপনি দেখেছেন যে অ্যাপ্লিকেশনগুলিতে Cloud Firestore তাদের ডেটাস্টোর হিসাবে ব্যবহার করে এটি ব্যবহার করা কতটা সহজ।
সাধারণ ডেটা প্রকারের সাথে একটি প্রাথমিক উদাহরণ থেকে শুরু করে, আমরা ক্রমান্বয়ে ডেটা মডেলের জটিলতা বাড়িয়েছি, সমস্ত সময় আমাদের জন্য ম্যাপিং সম্পাদনের জন্য কোডেবল এবং ফায়ারবেসের বাস্তবায়নের উপর নির্ভর করতে সক্ষম হয়েছি।
কোডেবল সম্পর্কে আরও তথ্যের জন্য, আমি নিম্নলিখিত সংস্থানগুলি সুপারিশ করি:
- জন সানডেলের কোডেবলের বুনিয়াদি সম্পর্কে একটি দুর্দান্ত নিবন্ধ রয়েছে।
- বইগুলি যদি আপনার জিনিস বেশি হয় তবে সুইফট কোডেবলের জন্য ম্যাট এর ফ্লাইট স্কুল গাইড দেখুন।
- এবং অবশেষে, ডনি ওয়ালসের কোডযোগ্য সম্পর্কে একটি সম্পূর্ণ সিরিজ রয়েছে।
যদিও আমরা Cloud Firestore ডকুমেন্টগুলি ম্যাপিংয়ের জন্য একটি বিস্তৃত গাইড সংকলন করার জন্য যথাসাধ্য চেষ্টা করেছি, এটি সম্পূর্ণ নয় এবং আপনি আপনার ধরণের মানচিত্রের জন্য অন্যান্য কৌশলগুলি ব্যবহার করছেন। নীচে প্রেরণ করুন প্রতিক্রিয়া বোতামটি ব্যবহার করে, আমাদের অন্যান্য ধরণের Cloud Firestore ডেটা ম্যাপিংয়ের জন্য বা সুইফটে ডেটা উপস্থাপন করার জন্য আপনি কী কৌশলগুলি ব্যবহার করেন তা আমাদের জানান।
Cloud Firestore কোডেবল সমর্থন ব্যবহার না করার সত্যিই কোনও কারণ নেই।