Swift 4'te tanıtılan Swift'in Codable API'si, verileri serileştirilmiş formatlardan Swift türlerine eşlemeyi kolaylaştırmak için derleyicinin gücünden yararlanmamızı sağlar.
Verileri bir web API'sinden uygulamanızın veri modeline (ve tersi) eşlemek için Codable'ı kullanıyor olabilirsiniz, ancak bundan çok daha esnektir.
Bu kılavuzda, verileri Cloud Firestore'dan Swift türlerine (ve tersi) eşlemek için Codable'ın nasıl kullanılabileceğine bakacağız.
Cloud Firestore'dan bir belge alırken, 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 bir dizi sözlük) alacaktır.
Artık Swift'deki sözlükleri doğrudan kullanmaya kesinlikle devam edebilirsiniz ve bunlar, kullanım durumunuzun tam olarak gerektirdiği gibi büyük bir esneklik sunar. Bununla birlikte, bu yaklaşım yazım açısından güvenli değildir ve özellik adlarını yanlış yazarak veya ekibinizin geçen hafta bu heyecan verici yeni özelliği piyasaya sürdüklerinde eklediği yeni özelliği haritalamayı unutarak bulunması zor hatalara neden olmak kolaydır.
Geçmişte pek çok geliştirici, sözlükleri Swift türleriyle eşlemelerine olanak tanıyan basit bir eşleme katmanı uygulayarak bu eksiklikleri gidermeye çalıştı. Ancak yine de bu uygulamaların çoğu, Cloud Firestore belgeleri ile uygulamanızın veri modelinin karşılık gelen türleri arasındaki eşlemenin manuel olarak belirlenmesine dayanmaktadır.
Cloud Firestore'un Swift'in Codable API'sine verdiği destekle bu çok daha kolay hale geliyor:
- Artık herhangi bir eşleme kodunu manuel olarak uygulamanıza gerek kalmayacak.
- Niteliklerin farklı adlarla nasıl eşleneceğini tanımlamak kolaydır.
- Swift'in birçok türü için yerleşik desteğe sahiptir.
- Özel türlerin eşlenmesine yönelik destek eklemek de kolaydır.
- Hepsinden iyisi: basit veri modelleri için herhangi bir eşleme kodu yazmanıza gerek kalmayacak.
Verileri eşleme
Cloud Firestore, verileri anahtarlarla değerlerle eşleyen belgelerde saklar. Tek bir belgeden veri almak için, alan adlarını Any
ile eşleyen bir sözlük döndüren DocumentSnapshot.data()
öğesini çağırabiliriz: func data() -> [String : Any]?
.
Bu, her bir alana erişmek için Swift'in alt simge sözdizimini 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)
}
}
}
}
Uygulaması basit ve kolay görünse de bu kod hassastır, bakımı zordur ve hataya açıktır.
Gördüğünüz gibi belge alanlarının veri türleri hakkında varsayımlarda bulunuyoruz. Bunlar doğru olabilir de olmayabilir de.
Unutmayın, şema olmadığı için koleksiyona kolayca yeni bir belge ekleyebilir ve bir alan için farklı bir tür seçebilirsiniz. numberOfPages
alanı için yanlışlıkla dizeyi seçebilirsiniz; bu da bulunması zor bir eşleme sorununa yol açabilir. Ayrıca, yeni bir alan eklendiğinde eşleme kodunuzu güncellemeniz gerekecektir ki bu oldukça zahmetlidir.
Ve Book
her özelliği için doğru türü tam olarak bilen Swift'in güçlü tür sisteminden faydalanmadığımızı da unutmayalım.
Kodlanabilir nedir bu arada?
Apple'ın belgelerine göre Codable, "kendisini harici bir temsile dönüştürebilen ve bu temsilden çıkabilen bir türdür." Aslında Codable, Encodable ve Decodable protokollerinin tür takma adıdır. Derleyici, Swift tipini bu protokole uygun hale getirerek, bu tipteki bir örneği JSON gibi serileştirilmiş bir formattan kodlamak/kodunu çözmek için gereken kodu sentezleyecektir.
Bir kitapla ilgili verileri depolamak için 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ün Codable'a uygun hale getirilmesi minimal düzeyde invaziftir. Protokole yalnızca uygunluğu eklememiz gerekiyordu; başka hiçbir değişikliğe gerek yoktu.
Bunu yerine getirdiğimizde artık bir kitabı kolayca bir 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)")
}
Bir JSON nesnesinin Book
örneğine dönüştürülmesi şu şekilde çalışır:
let decoder = JSONDecoder()
let data = /* fetch data from the network */
let decodedBook = try decoder.decode(Book.self, from: data)
Cloud Firestore belgelerindeki basit türlerle eşleme
Codable'ı kullanma
Cloud Firestore, basit dizelerden iç içe haritalara kadar geniş bir veri türü kümesini destekler. Bunların çoğu doğrudan Swift'in yerleşik türlerine karşılık gelir. Daha karmaşık olanlara dalmadan önce, ilk olarak bazı basit veri türlerinin eşlenmesine bir göz atalım.
Cloud Firestore belgelerini Swift türleriyle eşlemek için şu adımları izleyin:
-
FirebaseFirestore
çerçevesini projenize eklediğinizden emin olun. Bunu yapmak için Swift Paket Yöneticisini veya CocoaPod'ları kullanabilirsiniz. -
FirebaseFirestore
Swift dosyanıza aktarın. - Türünüzü
Codable
uygun hale getirin. - (Türü
List
görünümünde kullanmak istiyorsanız isteğe bağlıdır) Türünüze birid
özelliği ekleyin ve@DocumentID
kullanarak Cloud Firestore'a bunu belge kimliğiyle eşlemesini söyleyin. Bunu aşağıda daha ayrıntılı olarak ele alacağız. - Bir belge referansını Swift türüne eşlemek için
documentReference.data(as: )
kullanın. - Swift türlerindeki verileri bir Cloud Firestore belgesine eşlemek için
documentReference.setData(from: )
öğesini kullanın. - (İsteğe bağlı, ancak önemle tavsiye edilir) Doğru hata işlemeyi uygulayı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 ona @DocumentID
özellik sarmalayıcısıyla açıklama eklememiz gerekiyordu.
Bir belgeyi getirmek ve eşlemek için önceki kod pasajını alarak, 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 bir şekilde yazabilirsiniz. Bu, eşlemeyi sizin için gerçekleştirecek ve eşlenen belgeyi içeren bir Result
türü veya kod çözmenin başarısız olması durumunda bir hata döndürecektir:
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 belgeyi güncellemek documentReference.setData(from: )
çağırmak kadar basittir. Bazı temel hata yönetimini de içeren bir Book
örneğini kaydetmeye yönelik 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 eklerken Cloud Firestore, belgeye yeni bir belge kimliği atama işlemini otomatik olarak üstlenir. Bu, uygulama şu anda çevrimdışı olduğunda 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, bir belge içinde iç içe geçmiş nesneler oluşturmak için kullanabileceğiniz yapılandırılmış türler olan bir dizi başka veri türünü de destekler.
İç içe özel türler
Belgelerimizde eşlemek istediğimiz özelliklerin çoğu, kitabın başlığı veya yazarın adı gibi basit değerlerdir. Peki ya daha karmaşık bir nesneyi saklamamız gereken durumlar? Örneğin, kitabın kapağının URL'lerini farklı çözünürlüklerde saklamak isteyebiliriz.
Bunu Cloud Firestore'da yapmanın en kolay yolu bir harita kullanmaktır:
İlgili Swift yapısını yazarken Cloud Firestore'un URL'leri desteklemesi gerçeğinden yararlanabiliriz; URL içeren bir alanı saklarken, bu bir 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 CoverImages
adlı yapıyı nasıl tanımladığımıza dikkat edin. BookWithCoverImages
kapak özelliğini isteğe bağlı olarak işaretleyerek, bazı belgelerin kapak özelliği içermeyebileceği gerçeğinin üstesinden gelebiliriz.
Verileri almak veya güncellemek için neden bir kod parçacığının olmadığını merak ediyorsanız, Cloud Firestore'dan okumak veya Cloud Firestore'a yazmak için kodu ayarlamanıza gerek olmadığını duymaktan memnuniyet duyacaksınız: bunların tümü bizim verdiğimiz kodla çalışır. İlk bölümde yazdım.
Diziler
Bazen bir değerler koleksiyonunu bir belgede saklamak isteriz. Bir kitabın türleri buna iyi bir örnektir: Otostopçunun Galaksi Rehberi gibi bir kitap birkaç kategoriye ayrılabilir - bu durumda "Bilim Kurgu" ve "Komedi":
Cloud Firestore'da bunu bir dizi değer kullanarak modelleyebiliriz. Bu, herhangi bir kodlanabilir tür için desteklenir ( String
, Int
vb. gibi). 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 herhangi bir kodlanabilir tür için işe yaradığından özel türleri de kullanabiliriz. Her kitap için bir etiket listesi depolamak istediğimizi düşünün. Etiketin adının yanı sıra etiketin rengini de şu şekilde saklamak istiyoruz:
Etiketleri bu şekilde saklamak için tek yapmamız gereken, bir etiketi temsil edecek ve onu kodlanabilir hale getirecek bir Tag
yapısını uygulamaktır:
struct Tag: Codable, Hashable {
var title: String
var color: String
}
Ve bunun gibi, Book
belgelerimizde bir dizi Tags
saklayabiliriz!
struct BookWithTags: Codable {
@DocumentID var id: String?
var title: String
var numberOfPages: Int
var author: String
var tags: [Tag]
}
Belge kimliklerini eşleme hakkında kısa bir bilgi
Daha fazla türü eşlemeye geçmeden önce, bir an için belge kimliklerini eşleme hakkında konuşalım.
Cloud Firestore belgelerimizin belge kimliğini Swift türlerimizin id
özelliğiyle eşlemek için önceki örneklerin bazılarında @DocumentID
özellik sarmalayıcısını kullandık. Bu birkaç nedenden dolayı önemlidir:
- Kullanıcının yerel değişiklik yapması durumunda hangi belgenin güncelleneceğini bilmemize yardımcı olur.
- SwiftUI'nin
List
öğelerin eklendiklerinde etrafa sıçramasını önlemek için öğelerininIdentifiable
olmasını gerektirir.
@DocumentID
olarak işaretlenen bir özelliğin, belgeyi geri yazarken Cloud Firestore'un kodlayıcısı tarafından kodlanmayacağını belirtmekte fayda var. Bunun nedeni, belge kimliğinin belgenin kendisinin bir niteliği olmamasıdır; dolayısıyla onu belgeye yazmak bir hata olur.
Yuvalanmış türlerle çalışırken (bu kılavuzdaki daha önceki bir örnekte Book
etiket dizisi gibi), @DocumentID
özelliği eklemenize gerek yoktur: yuvalanmış özellikler Cloud Firestore belgesinin bir parçasıdır ve bir belge oluşturmaz. ayrı bir belge. Bu nedenle belge kimliğine ihtiyaç duymazlar.
Tarihler ve saatler
Cloud Firestore, tarih ve saatleri işlemek için yerleşik bir veri türüne sahiptir ve Cloud Firestore'un Codable desteği sayesinde bunları kullanmak kolaydır.
Tüm programlama dillerinin atası olan Ada'nın 1843 yılında icat ettiği bu belgeye bir göz atalım:
Bu belgeyi eşlemek için kullanılan Swift türü şöyle görünebilir:
struct ProgrammingLanguage: Codable {
@DocumentID var id: String?
var name: String
var year: Date
}
@ServerTimestamp
hakkında konuşmadan tarih ve saatlerle ilgili bu bölümden çıkamayız. Bu özellik sarmalayıcı, uygulamanızdaki zaman damgalarıyla baş etme konusunda çok güçlü bir araçtır.
Herhangi bir dağıtılmış sistemde, bireysel sistemlerdeki saatlerin her zaman tamamen senkronize olmaması ihtimali vardır. Bunun çok da önemli olmadığını düşünebilirsiniz, ancak bir saatin hisse senedi alım satım sistemi için biraz senkronize çalışmasının sonuçlarını hayal edin: bir milisaniyelik sapma bile, bir alım satım gerçekleştirirken milyonlarca dolarlık bir farkla sonuçlanabilir.
Cloud Firestore, @ServerTimestamp
ile işaretlenmiş öznitelikleri şu şekilde işler: özniteliği sakladığınızda (örneğin, addDocument()
kullanarak) nil
ise, Cloud Firestore, alanı veritabanına yazarken geçerli sunucu zaman damgasıyla doldurur. . addDocument()
veya updateData()
öğesini çağırdığınızda alan nil
değilse, Cloud Firestore öznitelik değerine dokunulmadan kalacaktır. Bu şekilde createdAt
ve lastUpdatedAt
gibi alanları uygulamak kolaydır.
Coğrafi noktalar
Coğrafi konumlar uygulamalarımızın her yerinde mevcuttur. Pek çok heyecan verici özellik, bunların saklanmasıyla mümkün hale gelir. Örneğin, bir hedefe ulaştığınızda uygulamanızın size bir görevi hatırlatabilmesi için bir görevin konumunu saklamak yararlı olabilir.
Cloud Firestore, herhangi bir konumun enlem ve boylamını saklayabilen yerleşik bir veri türü olan GeoPoint
sahiptir. Bir Cloud Firestore belgesindeki/bulunan konumları eşlemek için GeoPoint
türünü kullanabiliriz:
struct Office: Codable {
@DocumentID var id: String?
var name: String
var location: GeoPoint
}
Swift'de buna karşılık gelen tür CLLocationCoordinate2D
ve 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.
Numaralandırmalar
Numaralandırmalar muhtemelen Swift'deki en az önemsenen dil özelliklerinden biridir; onlarda göründüğünden çok daha fazlası var. Numaralandırmaların yaygın bir kullanım durumu, bir şeyin ayrık durumlarını modellemektir. Örneğin makaleleri yönetmek için bir uygulama yazıyor olabiliriz. Bir makalenin durumunu izlemek için bir enum Status
kullanmak isteyebiliriz:
enum Status: String, Codable {
case draft
case inReview
case approved
case published
}
Cloud Firestore, numaralandırmaları yerel olarak desteklemez (yani, değerler kümesini uygulayamaz), ancak yine de numaralandırmaların yazılabildiği gerçeğinden yararlanabilir ve kodlanabilir bir tür seçebiliriz. Bu örnekte, String
seçtik; bu, bir Cloud Firestore belgesinde 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 numaralandırma durumuna karşılık geldiğini bile özelleştirebiliriz. Örneğin, Status.inReview
durumunu "inceleniyor" olarak saklamaya karar verirsek yukarıdaki numaralandırmayı ş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 öznitelik adları, Swift'deki veri modelimizdeki özelliklerin adlarıyla eşleşmiyor. Örneğin, iş arkadaşlarımızdan biri bir Python geliştiricisi olabilir ve tüm özellik adları için yılan_case'i seçmeye karar verebilir.
Endişelenmeyin: Codable bizi koruyor!
Bu gibi durumlarda CodingKeys
faydalanabiliriz. Bu, belirli niteliklerin nasıl eşleneceğini belirtmek için kodlanabilir bir yapıya ekleyebileceğimiz bir numaralandırmadır.
Bu belgeyi düşünün:
Bu belgeyi String
türünde bir name özelliğine sahip bir yapıyla eşlemek için, ProgrammingLanguage
yapısına bir CodingKeys
numaralandırması eklememiz ve belgedeki niteliğ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 nitelik adlarını belirlemek için Swift türlerimizin özellik adlarını kullanacaktır. Yani öznitelik adları eşleştiği sürece kodlanabilir türlerimize CodingKeys
eklemeye gerek yoktur. Ancak CodingKeys
belirli bir tür için kullandığımızda, eşlemek istediğimiz tüm özellik adlarını eklememiz gerekir.
Yukarıdaki kod parçacığında, SwiftUI List
görünümünde tanımlayıcı olarak kullanmak isteyebileceğimiz bir id
özelliği tanımladık. Eğer bunu CodingKeys
belirtmeseydik veri alınırken haritalanmazdı ve dolayısıyla nil
olurdu. Bu, List
görünümünün ilk belgeyle doldurulmasına neden olur.
İlgili CodingKeys
numaralandırmasında durum olarak listelenmeyen herhangi bir özellik, eşleme işlemi sırasında göz ardı edilecektir. Bazı özelliklerin eşleştirilmesinin özellikle hariç tutulmasını istiyorsak bu aslında kullanışlı olabilir.
Örneğin, Reason reasonWhyILoveThis
özelliğini eşlenmekten hariç tutmak istiyorsak, tek yapmamız gereken onu CodingKeys
numaralandırması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 belgesine boş bir özelliği tekrar yazmak isteyebiliriz. Swift, bir değerin yokluğunu belirtmek için seçenekler kavramına sahiptir ve Cloud Firestore da null
değerleri destekler. Ancak, nil
değeri olan kodlama seçeneklerinin varsayılan davranışı, bunların atlanmasıdır. @ExplicitNull
Swift seçeneklerinin kodlanırken nasıl ele alınacağı konusunda bize biraz kontrol sağlıyor: isteğe bağlı bir özelliği @ExplicitNull
olarak işaretleyerek, Cloud Firestore'a, nil
değeri içeriyorsa bu özelliği boş bir değerle belgeye yazmasını söyleyebiliriz.
Renkleri eşlemek için özel bir kodlayıcı ve kod çözücü kullanma
Codable ile veri eşleme kapsamımızın son konusu olarak, özel kodlayıcıları ve kod çözücüleri tanıtalım. Bu bölüm yerel Cloud Firestore veri türünü kapsamaz ancak özel kodlayıcılar ve kod çözücüler Cloud Firestore uygulamalarınızda oldukça kullanışlıdır.
"Renkleri nasıl eşleyebilirim" yalnızca Cloud Firestore için değil, aynı zamanda Swift ile JSON arasındaki eşleme için de en sık sorulan geliştirici sorularından biridir. Piyasada çok sayıda çözüm var, ancak bunların çoğu JSON'a odaklanıyor ve hemen hemen hepsi renkleri, RGB bileşenlerinden oluşan iç içe geçmiş bir sözlük olarak eşliyor.
Görünüşe göre daha iyi ve daha basit bir çözüm olmalı. Neden web renklerini (veya daha spesifik olmak gerekirse, CSS onaltılık renk gösterimini) kullanmıyoruz? Kullanımı kolaydır (temel olarak yalnızca bir dize) ve hatta şeffaflığı bile destekliyorlar!
Swift Color
hex değerine eşleyebilmek için Codable'ı Color
ekleyen bir Swift uzantısı oluşturmamız gerekiyor.
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()
işlevini kullanarak, RGBA bileşenlerini iç içe yerleştirmeye gerek kalmadan bir String
Color
eşdeğerinin kodunu çözebiliriz. Ayrıca bu değerleri, önce dönüştürmenize gerek kalmadan uygulamanızın web kullanıcı arayüzünde kullanabilirsiniz!
Bununla, etiketlerin eşlenmesine ilişkin kodu güncelleyebiliriz, böylece etiket renklerini uygulamamızın kullanıcı arayüzü kodunda manuel olarak eşlemek yerine doğrudan kullanmayı 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 parçacıklarında hata işlemeyi kasıtlı olarak minimumda tuttuk, ancak bir üretim uygulamasında hataların incelikli bir şekilde ele alındığından emin olmak isteyeceksiniz.
Karşılaşabileceğiniz hata durumlarını nasıl ele alacağınızı gösteren bir kod pasajını burada bulabilirsiniz:
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ı işleme
Önceki kod parçacığı, tek bir belge getirilirken hataların nasıl ele alınacağını gösterir. Cloud Firestore, verileri bir kez getirmenin yanı sıra, anlık görüntü dinleyicileri olarak adlandırılan uygulamaları kullanarak uygulamanıza güncellemeleri anında iletmeyi de destekler: bir koleksiyona (veya sorguya) bir anlık görüntü dinleyicisi kaydedebiliriz ve Cloud Firestore, dinleyicimizi orada olduğunda arayacaktır. bir güncellemedir.
Burada anlık görüntü dinleyicisinin nasıl kaydedileceğini, Codable kullanılarak verilerin nasıl eşleneceğini ve oluşabilecek hataların nasıl ele alınacağını gösteren bir kod pasajı bulunmaktadır. Ayrıca koleksiyona nasıl yeni bir belge ekleneceğini de gösterir. Göreceğiniz gibi, eşlenen belgeleri tutan yerel diziyi kendimiz güncellememize gerek yok çünkü bu, anlık görüntü dinleyicisindeki kod tarafından hallediliyor.
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 gönderide kullanılan tüm kod parçacıkları , bu GitHub deposundan indirebileceğiniz örnek uygulamanın parçasıdır.
Devam edin ve Codable'ı kullanın!
Swift'in Codable API'si, serileştirilmiş formatlardaki verileri uygulamanızın veri modeliyle eşleştirmek için güçlü ve esnek bir yol sağlar. Bu kılavuzda, Cloud Firestore'u veri deposu olarak kullanan uygulamalarda kullanımının ne kadar kolay olduğunu gördünüz.
Basit veri türlerine sahip temel bir örnekten başlayarak, veri modelinin karmaşıklığını giderek artırdık ve bu arada eşlemeyi bizim için gerçekleştirme konusunda Codable ve Firebase'in uygulamasına güvenebildik.
Codable hakkında daha fazla ayrıntı için aşağıdaki kaynakları öneririm:
- John Sundell'in Codable'ın Temelleri hakkında güzel bir makalesi var.
- Kitaplar sizin tercihinizse Mattt'in Swift Codable Uçuş Okulu Rehberi'ne göz atın.
- Ve son olarak Donny Wals'un Codable hakkında bir serisi var.
Cloud Firestore belgelerini eşlemeye yönelik kapsamlı bir kılavuz derlemek için elimizden gelenin en iyisini yapmış olsak da, bu 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 Cloud Firestore veri türlerini eşlemek veya verileri Swift'de temsil etmek için hangi stratejileri kullandığınızı bize bildirin.
Cloud Firestore'un Codable desteğini kullanmamak için gerçekten hiçbir neden yok.