[MUSIC PLAYING]
PETER FRIESE: Hello, everyone.
So last time, I showed you how to fetch data from Firestore
and display it in your SwiftUI application.
And you might recall that after we wrote this piece of code,
I told you that it was less than ideal.
Why is that?
So first of all, it makes a number
of assumptions about the structure of our data
that we're fetching from Firestore.
So for example, here, we assume that the title attribute
is a String or that the pages attribute is an integer.
So what happens if we change our minds
and change data types in our Firestore documents?
Or what happens if we add new attributes to our Firestore
documents?
Or what happens if we add new properties to our Swift classes
or structs?
This code is likely going to break.
Also, it is rather verbose and a lot of lines of code too write.
And that's always a bad sign, because less lines of code
means less mistakes that we can make.
So if you've been around in the Swift community for any length
of time, you know that Apple has introduced the Codable
API a while ago in Swift 4.
So the Codable API makes it a lot easier
to map data between your Swift classes or structs
and an external representation, such as JSON file.
So you might have been using the Codable API
for mapping from JSON files to Swift structs.
The good news is that Firestore also supports the Codable API.
And today, I'm going to show you how to make use of that.
So let's dive right in.
The first thing that we need to do
is to add another pod to get support for this API.
So Firebase Firestore Swift is the pod that we need.
And then let me just quickly run Pod Install.
And while we're waiting for this to finish,
we can go back into our IDE and start updating our model here
to make it codable.
So the first thing we need to do is basically
just say, this is codable.
And usually, that is enough.
So if all the attributes match the attributes
of your external representation, that's perfect.
But you might recall that for a number of pages,
we have a different attribute name in Firestore.
Let me show you.
So in here, you see we've got Author.
We've got Title.
But it's called Pages.
So what we need to do is we need to tell Swift
that we want to use a different attribute name here.
And we can do that by implementing the CodingKeys
enum, CodingKeys, and then specifying they
attribute names, CodingKey.
So we need to provide all the attribute names here.
So first of all, case title, case author.
And these are just the same.
So we don't change the attribute names here.
But for a numberOfPages, it is pages, actually.
So we provide the name that we're using in our documents.
So that's one thing.
And then the other thing is what about this id attribute up
here?
We don't have an id attribute in our document over there.
But what we want to do is--
so no id attribute here.
But we want to map our document id to the id attribute
in our Swift struct here.
And to do that, we will use a property wrapper that Firestore
provides for us.
So Firestore Swift.
And then we can use this document id property wrapper
here, make this optional.
And that means whenever we map from a document
into our book struct, Firestore will
read the document id of the document
and map into this id attribute here.
All right, so that's everything we needed to do in our model.
Now let's go into our view model,
where we have this complicated piece of code,
which performs the mapping.
So what we want to do is, instead of doing all this,
make things easier.
And to make it easier, let me import this bundle here.
And then we can do this, queryDocumentSnapshot.data as.
And you can see that this has decodable protocol.
And what we need to do is we need
to provide the class of the struct
that we want to map into.
So book.self.
So this tells the codable support
we're using this class as a template
that we want to map into.
And then let's guard this with a try and then return this.
Comment out this piece of code here.
And we're using a compact map.
So whenever we return nil, this gets filtered out
by the compact map.
So this basically should be it.
Let me run this on the simulator.
And if everything went well and I didn't screw up,
we should see the same data in the simulator
as we did last time.
And there we go.
So let me show you this at the same time.
OK, there we go.
And just to prove that everything is still working,
let me just change the number of pages
here and reduce the number of pages.
And you can see that it has updated straight away.
Cool.
So this is how you map data if you're reading data.
So how would this look like if we wanted
to write data into Firestore?
And to show you how this would look like,
I'm just going to quickly implement
a method for adding new data.
So let me quickly do that.
So we're just going to addBook.
And it receives a book instance, book.
Here's a Book.
And then it actually is pretty straight forward.
So we already have our db instance here.
So db.collection.
And we want to write to the books collection.
And then, we want to add a new document.
addDocument.
And then you can see that we now have the option
to use from Encodable.
Perfect, because our book is encodable.
from book, just like that.
And that's it.
So the compiler will complain that this might throw,
so we're going to do a little try here.
That's one thing.
And then it's also going to--
well, that should be fine.
Oh yeah, for sure.
We need to wrap this.
So do.
And then catch this.
And then if there is an error, we
will just print the error like this.
And then yeah, I was looking for this.
So we'll just discard the result like this.
And this is how you store data into Firestore.
Pretty easy, right?
So that's basically it.
There are a couple of things that I would like to show you
before I let you go.
So first of all, let's go back here into our model.
So we've been using the document id property wrapper.
And there are a few other property wrappers
that are very useful.
So let's go find out more about them.
And to do so, let's go into our browser
here and use the open source search engine.
So this is a search engine that Google provides,
that you can use to search the source
code for all of these projects here
and the Firestore SDKs here as well.
And we want to search within the iOS SDK.
So let's first start by looking for the document id
wrapper that we've been using before.
And you can see here, it says something about codable.
So that's probably the right place for us to start looking.
So this is the source code for the document id property
wrapper.
All right, so what about the other property
wrappers that I promised you?
So you can see that this is a property wrapper.
And then we'll just use that to search for all
the other property wrappers.
So there are three in total.
And then we can look at the other ones.
So let's look at the serverTimestamp property
wrapper first.
So the documentation says that this
is a type that can initialize itself
from a Firestore timestamp, which
makes it suitable for use with the AdserverTimestamp property
wrapper.
All right, so what does it mean?
Server timestamps are useful because the time
on the server and the time on the user's devices
will very likely not be in sync.
So if you want to be sure that all your data in your Firestore
instance adheres to the same clock, essentially.
Then you want to use server timestamps, which
is really important if your application logic depends
on everything being in sync with one clock instance.
And this is where you want to use the serverTimestamp
property wrapper.
So for example, you would have an attribute
on your Swift class.
And then you would prefix that with add serverTimestamp.
And then it would mean, if your property in the Swift struct
is nil, then at the time when it gets written into Firestore,
the server below replace nil with the current server
timestamp.
And then if you have the same struct,
and it's already pre-filled with the timestamp,
and you write this back to the server
again because some other attribute has changed,
the server will have not changed the attribute
which contains the timestamp.
So this is pretty useful if you want
to do things like having a couple of attributes that
track the created date for a document and the last updated
date.
So whenever you want to update the timestamp,
just set it to nil.
And then write it back.
And the server will update it to the new timestamp.
There is a very good article by Doug Stevenson, who
goes into detail about what he calls
"The secrets of Firestore's FieldValue.serverTimestamp."
So I recommend reading this article
to get the lowdown of how server timestamps work like.
OK, so let's go back and look at the other property wrapper,
which is explicit null.
And this one is useful if you have an attribute that you want
to store back into Firestore and it is nil on the client's side.
So in Swift, it is nil.
And you want to write it back Firestore.
And you want to make sure that the attribute is
written into Firestore.
If you have cases like that, use this property wrapper.
If you don't use it and your attribute is nil
and you write the document back to Firestore,
it will not write the attribute.
So if you want to make sure that the attribute is written,
use this property wrapper.
All right, these are the property wrappers.
Let's go back into our code.
All right, so that was it.
That was a very quick introduction
on how to use codable in Firestore.
I encourage you to give it a try and use it
in your applications.
So this, obviously, also applies to regular Swift applications,
not just SwiftUI applications.
Let us know how you like it.
And if you run into any issues, please
feel free to leave us a comment on our GitHub repository.
Thanks for watching.
And I will see you next time.
[THEME MUSIC]