Swift 4'te kullanıma sunulan Swift Codable API, serileştirilmiş biçimlerdeki verileri Swift türleriyle eşlemeyi kolaylaştırmak için derleyicinin gücünden yararlanmamızı sağlar.
Codable'ı bir web API'sindeki verileri uygulamanızın veri modeline (veya tam tersi) eşlemek için kullanmış olabilirsiniz ancak Codable bundan çok daha esnektir.
Bu kılavuzda, Codable'ın Cloud Firestore verilerini Swift türleriyle eşlemek ve tam tersini yapmak için nasıl kullanılabileceğini inceleyeceğiz.
Cloud Firestore kaynağından bir belge getirilirken uygulamanız, anahtar/değer çiftlerinden oluşan bir sözlük (veya birden fazla belge döndüren işlemlerden birini kullanıyorsanız sözlük dizisi) alır.
Swift'te sözlükleri doğrudan kullanmaya devam edebilirsiniz. Sözlükler, kullanım alanınızın gerektirdiği esnekliği sağlayabilir. Ancak bu yaklaşım tip güvenliği sağlamaz ve özellik adlarını yanlış yazarak veya ekibinizin geçen hafta heyecan verici yeni bir özellik yayınlarken eklediği yeni özelliği eşlemeyi unutarak izlenmesi zor hatalar ortaya çıkarabilir.
Geçmişte birçok geliştirici, sözlükleri Swift türleriyle eşleştirmelerine olanak tanıyan basit bir eşleme katmanı uygulayarak bu eksiklikleri gideriyordu. Ancak bu uygulamaların çoğu, Cloud Firestore dokümanları ile uygulamanızın veri modelinin ilgili türleri arasındaki eşlemenin manuel olarak belirtilmesine dayanır.
Cloud Firestore, Swift'in Codable API'sini desteklediği için bu işlem çok daha kolay hale gelir:
- Artık herhangi bir eşleme kodunu manuel olarak uygulamanız gerekmeyecek.
- Farklı adlara sahip özelliklerin nasıl eşleneceğini tanımlamak kolaydır.
- Swift'in birçok türü için yerleşik destek sunar.
- Özel türleri eşleme desteği eklemek de kolaydır.
- En iyi tarafı ise basit veri modelleri için eşleme kodu yazmanız gerekmez.
Eşleme verileri
Cloud Firestore, verileri anahtarları değerlerle eşleyen dokümanlarda depolar. Belirli bir dokümandan veri almak için DocumentSnapshot.data() yöntemini çağırabiliriz. Bu yöntem, alan adlarını bir Any ile eşleyen bir sözlük döndürür: func data() -> [String : Any]?.
Bu, her bir alana erişmek için Swift'in alt simge söz dizimini kullanabileceğimiz anlamına gelir.
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)
}
}
}
}
Bu kod, basit ve kolay uygulanabilir gibi görünse de hassastır, bakımı zordur ve hataya açıktır.
Gördüğünüz gibi, doküman alanlarının veri türleri hakkında varsayımlarda bulunuyoruz. Bunlar doğru olabilir veya olmayabilir.
Şema olmadığı için koleksiyona kolayca yeni bir doküman ekleyebilir ve bir alan için farklı bir tür seçebilirsiniz. numberOfPages alanı için yanlışlıkla dizeyi seçebilirsiniz. Bu durumda, bulunması zor bir eşleme sorunu ortaya çıkar. Ayrıca, yeni bir alan eklendiğinde eşleme kodunuzu güncellemeniz gerekir. Bu da oldukça zahmetli bir iştir.
Ayrıca, Book özelliklerinin her birinin doğru türünü tam olarak bilen Swift'in güçlü tür sisteminden yararlanmadığımızı da unutmayalım.
Codable nedir?
Apple'ın belgelerine göre Codable, "kendisini harici bir temsile dönüştürebilen ve harici bir temsilden dönüştürebilen bir türdür." Aslında Codable, Encodable ve Decodable protokolleri için bir tür takma adıdır. Bir Swift türünü bu protokole uyumlu hale getirdiğinizde derleyici, bu türün bir örneğini JSON gibi serileştirilmiş bir biçimden kodlamak/kodunu çözmek için gereken kodu sentezler.
Bir kitapla ilgili verileri depolamak için kullanılan basit bir tür şöyle görünebilir:
struct Book: Codable {
var title: String
var numberOfPages: Int
var author: String
}
Gördüğünüz gibi, türü Codable'a uyarlamak için çok az değişiklik yapmak yeterli. Yalnızca protokole uygunluğu eklememiz gerekti, başka bir değişiklik yapmamız gerekmedi.
Bu sayede, bir kitabı kolayca JSON nesnesine kodlayabiliriz:
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)")
}
JSON nesnesinin Book örneğine dönüştürülmesi şu şekilde gerçekleşir:
let decoder = JSONDecoder()
let data = /* fetch data from the network */
let decodedBook = try decoder.decode(Book.self, from: data)
Codable kullanarak Cloud Firestore belgelerindeki basit türlerle eşleme yapma
Cloud Firestore, basit dizelerden iç içe yerleştirilmiş haritalara kadar geniş bir veri türü grubunu destekler. Bunların çoğu doğrudan Swift'in yerleşik türlerine karşılık gelir. Daha karmaşık veri türlerine geçmeden önce bazı basit veri türlerini eşlemeye bakalım.
Cloud Firestore belgelerini Swift türleriyle eşlemek için aşağıdaki adımları uygulayın:
FirebaseFirestoreçerçevesini projenize eklediğinizden emin olun. Bunun için Swift Package Manager veya CocoaPods'u kullanabilirsiniz.FirebaseFirestoreöğesini Swift dosyanıza aktarın.- Türünüzü
Codableile uyumlu hale getirin. - (İsteğe bağlı, türü bir
Listgörünümünde kullanmak istiyorsanız) Türünüze biridözelliği ekleyin ve Cloud Firestore'e bunu doküman kimliğiyle eşlemesini söylemek için@DocumentIDkullanın. Bu konuyu aşağıda daha ayrıntılı olarak ele alacağız. - Bir doküman referansını Swift türüyle eşlemek için
documentReference.data(as: )kullanın. - Swift türlerindeki verileri bir Cloud Firestore belgesiyle eşlemek için
documentReference.setData(from: )kullanın. - (İsteğe bağlı ancak kesinlikle önerilir) Hataların etkili bir şekilde yönetilmesini sağlayın.
Book türümüzü buna göre güncelleyelim:
struct Book: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
}
Bu tür zaten kodlanabilir olduğundan yalnızca id özelliğini eklememiz ve @DocumentID özellik sarmalayıcısıyla ek açıklama eklememiz gerekti.
Bir dokümanı getirmek ve eşlemek için önceki kod snippet'ini kullanarak tüm manuel eşleme kodunu tek bir satırla değiştirebiliriz:
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:) işlevini çağırırken dokümanın türünü belirterek bu kodu daha da kısa yazabilirsiniz. Bu işlem, eşlemeyi sizin için gerçekleştirir ve eşlenen dokümanı içeren bir Result türü veya kod çözme işleminin başarısız olması durumunda bir hata döndürür:
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)"
}
}
}
Mevcut bir dokümanı güncellemek için documentReference.setData(from: ) yöntemini çağırmanız yeterlidir. Temel hata işleme de dahil olmak üzere, bir Book örneğini kaydetmek için gereken kod şu şekildedir:
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)
}
}
}
Yeni bir doküman eklediğinizde Cloud Firestore, dokümana yeni bir doküman kimliği atama işlemini otomatik olarak gerçekleştirir. Bu özellik, uygulama çevrimdışıyken bile çalışır.
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, basit veri türlerini eşlemenin yanı sıra başka veri türlerini de destekler. Bunlardan bazıları, bir dokümanın içinde iç içe yerleştirilmiş nesneler oluşturmak için kullanabileceğiniz yapılandırılmış türlerdir.
İç içe yerleştirilmiş özel türler
Belgelerimizde eşlemek istediğimiz özelliklerin çoğu, kitabın başlığı veya yazarın adı gibi basit değerlerdir. Peki daha karmaşık bir nesneyi depolamamız gerektiğinde ne olur? Örneğin, kitabın kapağının URL'lerini farklı çözünürlüklerde depolamak isteyebiliriz.
Cloud Firestore uygulamasında bunu yapmanın en kolay yolu harita kullanmaktır:

İlgili Swift yapısını yazarken Cloud Firestore'nin URL'leri desteklemesinden yararlanabiliriz. URL içeren bir alan depolanırken dizeye dönüştürülür ve bunun tersi de geçerlidir:
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 belgesindeki kapak haritası için nasıl bir yapı (CoverImages) tanımladığımıza dikkat edin. BookWithCoverImages üzerindeki kapak özelliğini isteğe bağlı olarak işaretleyerek bazı belgelerin kapak özelliği içermeyebileceği gerçeğini ele alabiliriz.
Verileri getirmek veya güncellemek için neden kod snippet'i olmadığını merak ediyorsanız Cloud Firestore kaynağından/kaynağına okuma veya yazma için kodu ayarlamanıza gerek olmadığını öğrenmekten memnuniyet duyacaksınız: Tüm bunlar, ilk bölümde yazdığımız kodla çalışır.
Diziler
Bazen bir dokümanda bir değer koleksiyonu depolamak isteriz. Bir kitabın türleri buna iyi bir örnektir: Otostopçunun Galaksi Rehberi gibi bir kitap birden fazla kategoriye girebilir. Bu örnekte "Bilim Kurgu" ve "Komedi" kategorileri geçerlidir:

Cloud Firestore dilinde bunu bir değer dizisi kullanarak modelleyebiliriz. Bu, kodlanabilir tüm türler (ör. String, Int vb.) için desteklenir. Aşağıda, Book modelimize tür dizisinin nasıl ekleneceği gösterilmektedir:
public struct BookWithGenre: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var genres: [String]
}
Bu yöntem, kodlanabilir tüm türlerde çalıştığı için özel türleri de kullanabiliriz. Her kitap için bir etiket listesi depolamak istediğimizi düşünelim. Etiketin adıyla birlikte rengini de şu şekilde saklamak istiyoruz:

Etiketleri bu şekilde depolamak için tek yapmamız gereken, bir etiketi temsil edecek ve kodlanabilir hale getirecek bir Tag yapısı uygulamaktır:
struct Tag: Codable, Hashable {
var title: String
var color: String
}
Bu şekilde, Book dokümanlarımızda bir Tags dizisi depolayabiliriz.
struct BookWithTags: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var tags: [Tag]
}
Doküman kimliklerini eşleme hakkında kısa bilgi
Daha fazla türü eşlemeye geçmeden önce doküman kimliklerini eşleme hakkında konuşalım.
Cloud Firestore belgelerimizin belge kimliğini Swift türlerimizin id özelliğine eşlemek için önceki örneklerin bazılarında @DocumentID özellik sarmalayıcısını kullandık. Bu, birkaç nedenden dolayı önemlidir:
- Bu sayede, kullanıcı yerel değişiklikler yaptığında hangi dokümanı güncelleyeceğimizi biliriz.
- SwiftUI'ın
Listgörünümü, öğelerin eklendiklerinde yer değiştirmesini önlemek içinIdentifiableolmasını gerektirir.
@DocumentID olarak işaretlenen bir özelliğin, doküman geri yazılırken Cloud Firestore kodlayıcısı tarafından kodlanmayacağını belirtmekte fayda var. Bunun nedeni, doküman kimliğinin dokümanın bir özelliği olmamasıdır. Bu nedenle, kimliği dokümana yazmak hata olur.
İç içe yerleştirilmiş türlerle (bu kılavuzun önceki bir örneğinde Book üzerindeki etiket dizisi gibi) çalışırken @DocumentID özelliği eklemeniz gerekmez: İç içe yerleştirilmiş özellikler Cloud Firestore dokümanının bir parçasıdır ve ayrı bir doküman oluşturmaz. Bu nedenle, doküman kimliğine ihtiyaçları yoktur.
Tarihler ve saatler
Cloud Firestore, tarih ve saatleri işlemek için yerleşik bir veri türüne sahiptir ve Cloud Firestore'ın Codable desteği sayesinde bunları kullanmak kolaydır.
1843'te icat edilen ve tüm programlama dillerinin anası olan Ada'yı temsil eden bu belgeye göz atalım:

Bu dokümanı eşlemek için kullanılan Swift türü aşağıdaki gibi görünebilir:
struct ProgrammingLanguage: Codable {
@DocumentID var id: String?
var name: String
var year: Date
}
Tarihler ve saatlerle ilgili bu bölümü @ServerTimestamp hakkında konuşmadan geçemeyiz. Bu özellik sarmalayıcı, uygulamanızda zaman damgalarıyla çalışırken çok işinize yarar.
Dağıtılmış sistemlerde, her bir sistemdeki saatlerin her zaman tamamen senkronize olmaması muhtemeldir. Bunun önemli bir sorun olmadığını düşünebilirsiniz ancak bir borsa sisteminde saatin biraz senkronizasyon dışı çalışmasının sonuçlarını düşünün: Bir milisaniyelik sapma bile işlem yaparken milyonlarca dolarlık bir farka neden olabilir.
Cloud Firestore, @ServerTimestamp ile işaretlenmiş özellikleri şu şekilde işler: Özellik, depoladığınızda (örneğin, addDocument() kullanarak) nil ise Cloud Firestore, alanı veritabanına yazıldığı andaki geçerli sunucu zaman damgasıyla doldurur. addDocument() veya updateData() yöntemini çağırdığınızda alan nil değilse Cloud Firestore, özellik değerini olduğu gibi bırakır. Bu sayede createdAt ve lastUpdatedAt gibi alanları kolayca uygulayabilirsiniz.
Coğrafi noktalar
Coğrafi konumlar, uygulamalarımızda her yerde bulunur. Bu verileri depolayarak birçok heyecan verici özelliği kullanıma sunabiliriz. Örneğin, bir görev için konum depolamak faydalı olabilir. Böylece uygulamanız, bir hedefe ulaştığınızda size bir görevi hatırlatabilir.
Cloud Firestore, herhangi bir konumun boylam ve enlemini depolayabilen yerleşik bir veri türüne (GeoPoint) sahiptir. Konumları bir Cloud Firestore belgesinden/belgesine eşlemek için GeoPoint türünü kullanabiliriz:
struct Office: Codable {
@DocumentID var id: String?
var name: String
var location: GeoPoint
}
Swift'teki karşılığı CLLocationCoordinate2D olan bu iki tür arasında aşağıdaki işlemle eşleme yapabiliriz:
CLLocationCoordinate2D(latitude: office.location.latitude,
longitude: office.location.longitude)
Belgeleri fiziksel konuma göre sorgulama hakkında daha fazla bilgi edinmek için bu çözüm kılavuzuna göz atın.
Sıralamalar
Numaralandırmalar, Swift'te muhtemelen en az değer verilen dil özelliklerinden biridir.
Göründüklerinden çok daha fazlasını sunarlar. Numaralandırmaların yaygın kullanım alanlarından biri, bir şeyin farklı durumlarını modellemektir. Örneğin, makaleleri yönetmek için bir uygulama yazıyor olabiliriz. Bir makalenin durumunu izlemek için enum Status kullanabiliriz:
enum Status: String, Codable {
case draft
case inReview
case approved
case published
}
Cloud Firestore, enum'ları yerel olarak desteklemez (yani değer kümesini zorunlu kılamaz) ancak enum'ların yazılabilir olmasından yararlanabilir ve kodlanabilir bir tür seçebiliriz. Bu örnekte String seçeneğini belirledik. Bu, tüm enum değerlerinin bir Cloud Firestore dokümanında depolanırken dizeye/dizeden eşleneceği anlamına gelir.
Swift özel ham değerleri desteklediği için hangi değerlerin hangi enum durumuna karşılık geldiğini de özelleştirebiliriz. Örneğin, Status.inReview durumunu "incelemede" olarak depolamaya karar verirsek yukarıdaki enum'u şu şekilde güncelleyebiliriz:
enum Status: String, Codable {
case draft
case inReview = "in review"
case approved
case published
}
Eşlemeyi özelleştirme
Bazen eşlemek istediğimiz Cloud Firestore belgelerinin özellik adları, Swift'teki veri modelimizdeki özelliklerin adlarıyla eşleşmez. Örneğin, iş arkadaşlarımızdan biri Python geliştiricisi olabilir ve tüm özellik adları için snake_case'i seçmeye karar verebilir.
Endişelenmeyin, Codable bu konuda bize yardımcı olur.
Bu gibi durumlarda CodingKeys kullanabiliriz. Bu, belirli özelliklerin nasıl eşleneceğini belirtmek için kodlanabilir bir yapıya ekleyebileceğimiz bir enum'dur.
Şu belgeyi inceleyin:

Bu dokümanı, String türünde bir ad özelliğine sahip bir yapıya eşlemek için ProgrammingLanguage yapısına bir CodingKeys enum eklememiz ve dokümandaki özelliğin adını belirtmemiz gerekir:
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
}
}
Codable API, eşlemeye çalıştığımız Cloud Firestore belgelerindeki özellik adlarını belirlemek için varsayılan olarak Swift türlerimizin özellik adlarını kullanır. Özellik adları eşleştiği sürece kodlanabilir türlerimize CodingKeys eklememiz gerekmez. Ancak belirli bir tür için CodingKeys kullanırsak eşlemek istediğimiz tüm özellik adlarını eklememiz gerekir.
Yukarıdaki kod snippet'inde, SwiftUI List görünümünde tanımlayıcı olarak kullanmak isteyebileceğimiz bir id özelliği tanımladık. CodingKeys içinde belirtmeseydik veri getirilirken eşlenmezdi ve nil olurdu.
Bu durumda List görünümü ilk dokümanla doldurulur.
İlgili CodingKeys enum'da bir büyük/küçük harf olarak listelenmeyen tüm özellikler, eşleme işlemi sırasında yoksayılır. Bu, özellikle bazı mülkleri eşlemenin dışında tutmak istediğimizde kullanışlı olabilir.
Örneğin, reasonWhyILoveThis özelliğini eşlemeden hariç tutmak istiyorsak bu özelliği CodingKeys enum'dan kaldırmamız yeterlidir:
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
}
}
Bazen Cloud Firestore dokümanına boş bir özellik yazmak isteyebiliriz. Swift'te bir değerin yokluğunu belirtmek için isteğe bağlı değerler kavramı vardır ve Cloud Firestore, null değerlerini de destekler.
Ancak nil değerine sahip isteğe bağlı öğeleri kodlamanın varsayılan davranışı, bunları atlamaktır. @ExplicitNull, Swift isteğe bağlı değerlerinin kodlanırken nasıl işleneceği konusunda bize bir miktar kontrol sağlar: İsteğe bağlı bir özelliği @ExplicitNull olarak işaretleyerek Cloud Firestore'e, nil değeri içeriyorsa bu özelliği dokümana boş bir değerle yazmasını söyleyebiliriz.
Renkleri eşlemek için özel kodlayıcı ve kod çözücü kullanma
Codable ile verileri eşleme konusundaki son başlığımızda özel kodlayıcılar ve kod çözücülerden bahsedelim. Bu bölümde yerel bir veri türü ele alınmamaktadır ancak özel kodlayıcılar ve kod çözücüler, Cloud Firestore uygulamalarınızda oldukça kullanışlıdır.Cloud Firestore
"Renkleri nasıl eşleyebilirim?" sorusu, yalnızca Cloud Firestore için değil, Swift ile JSON arasında eşleme yapmak için de geliştiricilerin en sık sorduğu sorulardan biridir. Bu konuda birçok çözüm mevcut ancak bunların çoğu JSON'a odaklanıyor ve neredeyse tamamı renkleri RGB bileşenlerinden oluşan iç içe yerleştirilmiş bir sözlük olarak eşliyor.
Daha iyi ve basit bir çözüm olmalı. Web renklerini (veya daha spesifik olarak CSS onaltılık renk gösterimini) kullanabiliriz. Bunların kullanımı kolaydır (temelde yalnızca bir dizedir) ve şeffaflığı bile desteklerler.
Swift Color değerini onaltılık değerine eşlemek için Color değerine Codable ekleyen bir Swift uzantısı oluşturmamız gerekir.
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() kullanarak RGBA bileşenlerini iç içe yerleştirmek zorunda kalmadan String değerini Color eşdeğerine dönüştürebiliriz. Ayrıca bu değerleri, önce dönüştürmek zorunda kalmadan uygulamanızın web kullanıcı arayüzünde kullanabilirsiniz.
Bu sayede, etiketleri eşlemek için kodu güncelleyebilir ve etiket renklerini uygulama kullanıcı arayüzü kodumuzda manuel olarak eşlemek yerine doğrudan işlemeyi kolaylaştırabiliriz:
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]
}
Hataları işleme
Yukarıdaki kod snippet'lerinde hata işlemeyi kasıtlı olarak minimum düzeyde tuttuk ancak üretim uygulamasında tüm hataları düzgün bir şekilde işlediğinizden emin olmanız gerekir.
Karşılaşabileceğiniz hata durumlarını nasıl ele alacağınızı gösteren bir kod snippet'i aşağıda verilmiştir:
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)"
}
}
}
}
}
Canlı haberlerdeki hataları ele alma
Önceki kod snippet'i, tek bir dokümanı getirirken oluşan hataların nasıl işleneceğini gösterir. Cloud Firestore, verileri bir kez getirmenin yanı sıra anlık görüntü dinleyicileri kullanarak uygulamanıza güncellemeleri anında iletmeyi de destekler. Bir koleksiyona (veya sorguya) anlık görüntü dinleyici kaydedebiliriz. Cloud Firestore, güncelleme olduğunda dinleyicimizi çağırır.
Aşağıdaki kod snippet'inde, anlık görüntü dinleyicisinin nasıl kaydedileceği, Codable kullanılarak verilerin nasıl eşleneceği ve oluşabilecek hataların nasıl ele alınacağı gösterilmektedir. Ayrıca koleksiyona yeni bir doküman eklemeyi de gösterir. Eşlenen dokümanları tutan yerel diziyi kendimiz güncellememize gerek yok. Bu işlem, anlık görüntü dinleyicisindeki kod tarafından gerçekleştirilir.
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)
}
}
}
Bu yayında kullanılan tüm kod snippet'leri, bu GitHub deposundan indirebileceğiniz örnek bir uygulamanın parçasıdır.
Codable'ı kullanmaya başlayın.
Swift'in Codable API'si, serileştirilmiş biçimlerdeki verileri uygulamanızın veri modeline ve veri modelinden eşlemek için güçlü ve esnek bir yöntem sunar. Bu kılavuzda, veri deposu olarak Cloud Firestore kullanan uygulamalarda bu özelliğin ne kadar kolay kullanıldığını gördünüz.
Basit veri türleri içeren temel bir örnekle başlayıp veri modelinin karmaşıklığını kademeli olarak artırdık. Bu süreçte, eşlemeyi bizim için gerçekleştirmesi amacıyla Codable'a ve Firebase'in uygulamasına güvendik.
Codable hakkında daha fazla bilgi için aşağıdaki kaynakları incelemenizi öneririz:
- John Sundell'in Codable'ın Temelleri hakkında güzel bir makalesi var.
- Kitap okumayı tercih ediyorsanız Mattt'in Flight School Guide to Swift Codable adlı kitabına göz atabilirsiniz.
- Son olarak Donny Wals'ın Codable hakkında bir serisi var.
Cloud Firestore dokümanlarını eşlemek için kapsamlı bir kılavuz hazırlamaya çalışmış olsak da bu kılavuz eksiksiz değildir ve türlerinizi eşlemek için başka stratejiler kullanıyor olabilirsiniz. Aşağıdaki Geri bildirim gönder düğmesini kullanarak diğer Cloud Firestore veri türlerini eşlemek veya verileri Swift'te temsil etmek için hangi stratejileri kullandığınızı bize bildirin.
Cloud Firestore'ın Codable desteğini kullanmamanız için hiçbir neden yoktur.