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 modeliyle eşlemek için kullanıyor olabilirsiniz (veya tam tersi). Ancak Codable bundan çok daha esnektir.
Bu kılavuzda, Codable'ın Cloud Firestore'deki verileri Swift türlerine (veya tam tersi) eşlemek için nasıl kullanılabileceğini inceleyeceğiz.
Cloud Firestore kaynağından bir doküman getirirken uygulamanız bir anahtar/değer çiftleri sözlüğü (veya birden fazla doküman döndüren işlemlerden birini kullanıyorsanız bir sözlük dizisi) alır.
Artık Swift'teki sözlükleri doğrudan kullanmaya devam edebilirsiniz. Üstelik bunlar, kullanım alanınızın tam olarak ihtiyaç duyabileceği kadar esnek bir esneklik sunar. Ancak bu yaklaşım tür açısından güvenli değildir ve özellik adlarını yanlış yazarak veya ekibinizin geçen hafta heyecan verici yeni özelliği kullanıma sunarken eklediği yeni özelliği eşlemeyi unutarak bulunması zor hatalar oluşturmak kolaydır.
Geçmişte birçok geliştirici, sözlükleri Swift türleriyle eşlemelerine olanak tanıyan basit bir eşleme katmanı uygulayarak bu eksiklikleri gideriyordu. Ancak yine de bu uygulamaların çoğu, Cloud Firestore dokümanları ile uygulamanızın veri modelindeki ilgili türler arasındaki eşlemeyi manuel olarak belirtmeye dayanır.
Cloud Firestore'ın Swift'in Codable API'sini desteklemesi sayesinde bu işlem çok daha kolay hale geliyor:
- Artık eşleme kodunu manuel olarak uygulamanız gerekmeyecek.
- Farklı adlara sahip özelliklerin nasıl eşleneceğini tanımlamak kolaydır.
- Çoğu Swift türü için yerleşik desteğe sahiptir.
- Özel türlerin eşlenmesi için destek eklemek de kolaydır.
- Üstelik, basit veri modelleri için herhangi bir eşleme kodu yazmanıza gerek kalmaz.
Verileri eşleme
Cloud Firestore, verileri anahtarları değerlerle eşleyen belgelerde depolar. Bir dokümandan veri almak için DocumentSnapshot.data()
işlevini çağırabiliriz. Bu işlev, alan adlarını bir Any
ile eşleyen bir sözlük döndürür: func data() -> [String : Any]?
.
Bu sayede, her bir alana erişmek için Swift'in alt dize söz dizimini kullanabiliriz.
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)
}
}
}
}
Basit ve uygulanması kolay gibi görünse de bu kod hassastır, bakımı zordur ve hatalara açıktır.
Gördüğünüz gibi, belge alanlarının veri türleri hakkında varsayımlar yapıyoruz. Bu bilgiler doğru veya yanlış olabilir.
Şema olmadığından koleksiyona kolayca yeni bir doküman ekleyebileceğinizi ve bir alan için farklı bir tür seçebileceğinizi unutmayın. numberOfPages
alanı için yanlışlıkla dize seçebilirsiniz. Bu, bulunması zor bir eşleme sorununa neden olur. Ayrıca, yeni bir alan eklendiğinde eşleme kodunuzu güncellemeniz gerekir. Bu da oldukça zahmetli bir işlemdir.
Ayrıca, Book
öğesinin her bir özelliği için doğru türü tam olarak bilen Swift'in güçlü tür sisteminden yararlanmadığımızı da unutmayın.
Kodlanabilir nedir?
Apple'ın dokümanları uyarınca, Kodlanabilir "kendisini harici bir temsile dönüştürebilen ve harici bir temsilden dönüştürülebilen 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 uygun hale getirerek derleyici, bu türün bir örneğini JSON gibi serileştirilmiş bir biçimden kodlamak/kodunu çözmek için gereken kodu sentezleyecektir.
Bir kitapla ilgili verileri depolamak için kullanılan basit bir tür şu şekilde görünebilir:
struct Book: Codable {
var title: String
var numberOfPages: Int
var author: String
}
Gördüğünüz gibi, türü Kodlanabilir'e uyarlamak çok az rahatsız edici bir işlemdir. Yalnızca protokole uygunluğu eklememiz gerekiyordu. Başka bir değişiklik gerekmiyordu.
Bu işlemden sonra bir kitabı JSON nesnesine kolayca 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)")
}
Bir JSON nesnesinin kodunu Book
örneğine dönüştürme işlemi aşağıdaki gibi çalışır:
let decoder = JSONDecoder()
let data = /* fetch data from the network */
let decodedBook = try decoder.decode(Book.self, from: data)
Codable'ı kullanarak Cloud Firestore dokümanlarındaki
basit türlerle eşleme yapma
Cloud Firestore, basit dizelerden iç içe yerleştirilmiş haritalara kadar geniş bir veri türü yelpazesini destekler. Bunların çoğu doğrudan Swift'in yerleşik türlerine karşılık gelir. Daha karmaşık olanları incelemeden önce, bazı basit veri türlerinin eşlenmesine göz atalım.
Cloud Firestore belgelerini Swift türleriyle eşlemek için aşağıdaki adımları uygulayın:
- Projenize
FirebaseFirestore
çerçevesini eklediğinizden emin olun. Bunu yapmak için Swift Package Manager veya CocoaPods'u kullanabilirsiniz. FirebaseFirestore
dosyasını Swift dosyanıza aktarın.- Türünüzü
Codable
'e uygun hale getirin. - (İsteğe bağlı, türü
List
görünümünde kullanmak istiyorsanız) Türünüze birid
mülkü ekleyin ve Cloud Firestore'e bunu doküman kimliğiyle eşlemesini söylemek için@DocumentID
kullanı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: )
simgesini kullanın. - Swift türlerindeki verileri bir Cloud Firestore belgesiyle eşlemek için
documentReference.setData(from: )
kullanın. - (İsteğe bağlıdır, ancak kesinlikle önerilir) Doğru hata işleme yöntemleri uygulayın.
Book
türünü 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
mülkünü eklememiz ve @DocumentID
mülk sarmalayıcısıyla ek açıklama eklememiz gerekiyordu.
Bir belgeyi getirme ve eşlemeyle ilgili önceki kod snippet'ini ele alırsak 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 belgenin türünü belirterek bunu 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ü döndürür veya kod çözme başarısız olursa 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: )
işlevini çağırmanız yeterlidir. Bazı temel hata işleme işlemleri de dahil olmak üzere Book
örneğini kaydetmek için gereken kod aşağıda verilmiştir:
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 belge eklediğinizde Cloud Firestore, belgeye yeni bir belge kimliği atamayı otomatik olarak yapar. Bu işlem, uygulama şu anda çevrimdışı olsa 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şlemeye ek olarak bir dizi başka veri türünü de destekler. Bunlardan bazıları, bir belge 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 yapmalıyız? Örneğin, kitabın kapağının URL'lerini farklı çözünürlüklerde depolamak isteyebiliriz.
Cloud Firestore ürününde bunu yapmanın en kolay yolu harita kullanmaktır:
İlgili Swift yapısını yazarken Cloud Firestore'ün URL'leri desteklediği gerçeğinden yararlanabiliriz. URL içeren bir alan depolanırken dize biçimine 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 dokümanında kapak haritası için nasıl bir CoverImages
yapısı tanımladığımıza dikkat edin. BookWithCoverImages
üzerindeki kapak mülkünü isteğe bağlı olarak işaretleyerek bazı dokümanların kapak özelliği içermeyebileceğini hesaba katabiliriz.
Verileri getirme veya güncellemeyle ilgili kod snippet'inin neden olmadığını merak ediyorsanız Cloud Firestore kaynağından veri okumak veya yazmak için kodu ayarlamanıza gerek olmadığını duymaktan memnuniyet duyacaksınız: Tüm bunlar, ilk bölümde yazdığımız kodla çalışır.
Diziler
Bazen bir dokümanda değer koleksiyonu depolamak isteriz. Bir kitabın türleri buna iyi bir örnektir: Ulaşımcının Galaksi Rehberi gibi bir kitap, bu örnekte "Bilim Kurgu" ve "Komedi" olmak üzere birkaç kategoriye ayrılabilir:
Cloud Firestore'te bunu bir değer dizisi kullanarak modelleyebiliriz. Bu, tüm kodlanabilir türler (String
, Int
vb.) için desteklenir. Aşağıda, Book
modelimize bir 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, kodlanabilir her tür için işe yaradığından özel türleri de kullanabiliriz. Her kitap için bir etiket listesi depolamak istediğimizi varsayalım. Etiketin adının yanı sıra rengini de şu şekilde saklamak istiyoruz:
Etiketleri bu şekilde depolamak için tüm yapmanız gereken, bir etiketi temsil eden Tag
struct uygulamak ve kodlanabilir hale getirmektir:
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 bir hatırlatma
Daha fazla türün eşlenmesine geçmeden önce doküman kimliklerinin eşlenmesi hakkında biraz konuşalım.
Cloud Firestore belgelerimizin belge kimliğini Swift türlerimizin id
mülküyle eşlemek için önceki örneklerin bazılarında @DocumentID
mülk sarmalayıcısını kullandık. Bu, birkaç nedenden dolayı önemlidir:
- Kullanıcı yerel değişiklikler yaptığında hangi dokümanın güncelleneceğini bilmemize yardımcı olur.
- SwiftUI'nın
List
, öğelerin eklendiğinde etrafta zıplamasını önlemek için öğelerininIdentifiable
olmasını gerektirir.
@DocumentID
olarak işaretlenen bir özelliğin, doküman geri yazılırken Cloud Firestore'un kodlayıcısı tarafından kodlanmayacağını belirtmek gerekir. Bunun nedeni, doküman kimliğinin dokümanın bir özelliği olmamasıdır. Bu nedenle, doküman kimliğini dokümana yazmak hata olur.
İç içe yerleştirilmiş türlerle çalışırken (bu kılavuzun önceki bir örneğindeki Book
öğesindeki etiket dizisi gibi) @DocumentID
özelliği eklemeniz gerekmez: İç içe yerleştirilmiş özellikler Cloud Firestore belgesinin bir parçasıdır ve ayrı bir belge oluşturmaz. Bu nedenle, belge 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. Cloud Firestore'ın Kodlanabilir'i desteklemesi sayesinde bu tür verileri kullanmak kolaydır.
Tüm programlama dillerinin anası olan Ada'yı (1843'te icat edilmiştir) temsil eden bu belgeye göz atalım:
Bu dokümanın eşlenmesi için Swift türü şu şekilde görünebilir:
struct ProgrammingLanguage: Codable {
@DocumentID var id: String?
var name: String
var year: Date
}
Tarih ve saatlerle ilgili bu bölümden @ServerTimestamp
hakkında konuşmadan çıkamayız. Bu mülk sarmalayıcı, uygulamanızda zaman damgalarıyla ilgili işlemler yapma konusunda çok güçlüdür.
Herhangi bir dağıtık sistemde, sistemlerdeki saatlerin her zaman tamamen senkronize olmaması olasıdır. Bunun önemli olmadığını düşünebilirsiniz ancak bir saatin hisse senedi alım satımı sistemiyle biraz senkronize olmamasının sonuçlarını düşünün: Bir milisaniyelik sapma bile bir işlemin yürütülmesinde milyonlarca dolarlık bir farka neden olabilir.
Cloud Firestore, @ServerTimestamp
ile işaretlenmiş özellikleri aşağıdaki şekilde işler: Özelliği depolarken nil
ise (örneğin, addDocument()
kullanılarak) Cloud Firestore, alanı veritabanına yazılırken geçerli sunucu zaman damgasıyla doldurur. addDocument()
veya updateData()
çağrısını yaptığınızda alan nil
değilse Cloud Firestore, özellik değerini değiştirmez. Böylece, createdAt
ve lastUpdatedAt
gibi alanları uygulamak kolaylaşır.
Coğrafi noktalar
Coğrafi konumlar uygulamalarımızda her zaman vardır. Birçok heyecan verici özellik bunları saklayarak mümkün hale gelir. Örneğin, uygulamanızın bir hedefe ulaştığınızda size bir görevi hatırlatabilmesi için görevle ilişkili bir konumu depolamak yararlı olabilir.
Cloud Firestore, her konumun enlem ve boylam bilgisini kaydedebilen yerleşik GeoPoint
veri türüne sahiptir. Cloud Firestore belgesindeki konumları eşlemek için GeoPoint
türünü kullanabiliriz:
struct Office: Codable {
@DocumentID var id: String?
var name: String
var location: GeoPoint
}
Swift'te karşılık gelen tür CLLocationCoordinate2D
'dür. Bu iki tür arasında aşağıdaki işlemi kullanarak eşleme yapabiliriz:
CLLocationCoordinate2D(latitude: office.location.latitude,
longitude: office.location.longitude)
Dokümanları fiziksel konuma göre sorgulamak hakkında daha fazla bilgi edinmek için bu çözüm kılavuzuna göz atın.
Sıralamalar
Listeler, Swift'teki en az değer verilen dil özelliklerinden biridir. Ancak göründüğünden çok daha fazlasını sunar. Listelerin yaygın kullanım alanlarından biri, bir şeyin ayrık durumlarını modellemedir. Örneğin, makaleleri yönetmek için bir uygulama yazabiliriz. Bir makalenin durumunu izlemek için bir enum Status
kullanabiliriz:
enum Status: String, Codable {
case draft
case inReview
case approved
case published
}
Cloud Firestore, enum'leri doğal olarak desteklemez (yani değer kümesini zorunlu klamaz). Ancak yine de enum'lerin yazılabilir olmasından yararlanabilir ve kodlanabilir bir tür seçebiliriz. Bu örnekte String
'yi seçtik. Bu işlem, Cloud Firestore dokümanında depolandığında tüm numaralandırma değerlerinin dizeyle/dizeden eşleneceği anlamına gelir.
Swift, özel ham değerleri desteklediğinden hangi değerlerin hangi sıralamayı ifade ettiğini bile özelleştirebiliriz. Örneğin, Status.inReview
destek kaydını "inceleniyor" olarak kaydetmeye karar verirsek yukarıdaki enumu aşağıdaki gibi 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 mülkleri adlarıyla eşleşmez. Örneğin, iş arkadaşlarımızdan biri Python geliştirici olabilir ve tüm özellik adları için snake_case seçeneğini tercih edebilir.
Endişelenmeyin: Codable bu konuda size yardımcı olacaktır.
Bu gibi durumlarda CodingKeys
'ten yararlanabiliriz. Bu, belirli özelliklerin nasıl eşleneceğini belirtmek için kodlanabilir bir struct'a ekleyebileceğimiz bir sıralamadır.
Aşağıdaki dokümanı ele alalım:
Bu belgeyi String
türünde ad özelliğine sahip bir struct ile eşlemek için ProgrammingLanguage
struct'a bir CodingKeys
sıralaması eklememiz ve belgede ö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
}
}
Varsayılan olarak Codable API, eşlemeye çalıştığımız Cloud Firestore belgelerindeki özellik adlarını belirlemek için Swift türlerimizin özellik adlarını kullanır. Bu nedenle, özellik adları eşleştiğinde kodlanabilir türlerimize CodingKeys
eklemenize gerek yoktur. Ancak belirli bir tür için CodingKeys
'ü kullandıktan sonra eşlemek istediğimiz tüm mülk 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
mülkü tanımladık. CodingKeys
içinde belirtilmezse veri getirilirken eşlenmez ve nil
olur.
Bu durumda List
görünümü ilk dokümanla doldurulur.
İlgili CodingKeys
enum'da büyük/küçük harf duyarlı olarak listelenmeyen tüm mülkler, eşleme işlemi sırasında yoksayılır. Özellikle bazı mülklerin haritalanması hariç tutulmak isteniyorsa bu özellik kullanışlı olabilir.
Örneğin, reasonWhyILoveThis
özelliğini eşlemenin dışında bırakmak istersek tek yapmamız gereken bu özelliği CodingKeys
sıralamasından kaldırmaktır:
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 tekrar boş bir özellik yazmak isteyebiliriz. Swift, değer olmadığını belirtmek için isteğe bağlı kavramına sahiptir ve Cloud Firestore, null
değerlerini de destekler.
Ancak nil
değeri olan isteğe bağlı parametrelerin kodlanması için varsayılan davranış, bu parametreleri atlamaktır. @ExplicitNull
, Swift isteğe bağlılarının kodlanırken nasıl ele alınacağı konusunda bize bir miktar kontrol sağlar: İsteğe bağlı bir mülkü @ExplicitNull
olarak işaretleyerek Cloud Firestore'e bu mülkü, nil
değeri içeriyorsa belgeye null değerle yazmasını söyleyebiliriz.
Renkleri eşlemek için özel kodlayıcı ve kod çözücü kullanma
Codable ile veri eşleştirme kapsamımızın son konularından biri olarak, özel kodlayıcıları ve kod çözücüleri tanıtalım. Bu bölümde, doğal Cloud Firestore veri türü ele alınmamaktadır ancak özel kodlayıcılar ve kod çözücüler, Cloud Firestore uygulamalarınızda yaygın olarak kullanışlıdır.
"Renk eşlemesi nasıl yapabilirim?", geliştiriciler tarafından hem Cloud Firestore hem de Swift ile JSON arasında eşleme yapmak için en sık sorulan sorulardan biridir. Sunulan çok sayıda çözüm vardır, ancak çoğu JSON'a odaklanır ve bunların neredeyse tümü renkleri, RGB bileşenlerinden oluşan, iç içe yerleştirilmiş bir sözlük olarak eşler.
Daha iyi ve daha basit bir çözüm olmalı. Web renklerini (veya daha spesifik olarak CSS onaltılık renk gösterimini) neden kullanmıyoruz? Kullanımı kolaydır (temelde bir dizedir) ve hatta şeffaflığı destekler.
Bir Swift Color
değerini onaltılık değerine eşleyebilmek 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 bir String
değerinin kodunu Color
eşdeğerine çözebiliriz. Ayrıca, bu değerleri önce dönüştürmenize gerek kalmadan uygulamanızın web kullanıcı arayüzünde kullanabilirsiniz.
Bu sayede, etiket eşleme kodunu güncelleyebiliriz. Böylece, etiket renklerini uygulamamızın kullanıcı arayüzü kodunda manuel olarak eşlemek yerine doğrudan yönetmek daha kolay hale gelir:
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şleme işlemini kasıtlı olarak minimum düzeyde tuttuk ancak üretim uygulamasında tüm hataları düzgün bir şekilde ele almanız gerekir.
Karşılaşabileceğiniz tüm 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ı güncellemelerdeki hataları ele alma
Önceki kod snippet'i, tek bir doküman getirilirken karşılaşılan hataların nasıl işleneceğini gösterir. Cloud Firestore, verileri bir kez getirmenin yanı sıra, anlık görüntü dinleyicileri adı verilen öğeleri kullanarak uygulamanıza güncellemeleri gerçekleştiği anda sunmayı da destekler. Bir koleksiyona (veya sorguya) anlık görüntü dinleyici kaydedebiliriz. Cloud Firestore, güncelleme olduğunda dinleyicimizi çağırır.
Aşağıda, anlık görüntü dinleyicisinin nasıl kaydedileceğini, Codable'ı kullanarak verilerin nasıl eşleneceğini ve oluşabilecek hataların nasıl ele alınacağını gösteren bir kod snippet'i verilmiştir. Ayrıca koleksiyona nasıl yeni doküman ekleneceği de gösterilmektedir. Göreceğiniz gibi, eşlenen dokümanları içeren yerel diziyi güncellememiz gerekmez. Bu işlem, anlık görüntü dinleyicisindeki kod tarafından yapılır.
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 modeliyle eşlemek için güçlü ve esnek bir yöntem sunar. Bu kılavuzda, veri deposu olarak Cloud Firestore kullanan uygulamalarda bu aracın ne kadar kolay kullanıldığını gördünüz.
Basit veri türlerine sahip temel bir örnekten başlayarak veri modelinin karmaşıklığını kademeli olarak artırdık. Bu süreçte, eşlemeyi bizim için gerçekleştirmek üzere Codable ve Firebase'in uygulamasına güvenebildik.
Codable hakkında daha fazla bilgi için aşağıdaki kaynakları inceleyebilirsiniz:
- John Sundell'in Codable'ın Temel Özellikleri hakkında güzel bir makalesi var.
- Kitap okumayı tercih ediyorsanız Mattt'in Swift Codable için Uçuş Okulu Kılavuzu'na göz atın.
- Son olarak, Donny Wals'ın Codable ile ilgili bir serisinin tamamını inceleyebilirsiniz.
Cloud Firestore dokümanlarının haritasını çıkarmayla ilgili kapsamlı bir rehber derlemek için elimizden geleni yapmış olsak da bu, tam kapsamlı 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 veri türlerini eşlemek veya verileri Swift'te temsil etmek için hangi stratejileri kullandığınızı bize bildirin.Cloud Firestore
Cloud Firestore'ın Codable desteğini kullanmamak için hiçbir neden yok.