Hi! Stephanie from Design plus Code. This week, we're going to learn how to
code another application from scratch. And this time we will
code a to-do application. This application will be fully functional,
meaning that we can add new tasks. For example, I will need
to wash the dishes as well. And I can also check the
task that I completed. For example, I mopped the floor
so I can check it and it will move to the bottom of the list. And not only can we check our
tasks, we can also delete them. So we'll learn how to implement
swipe actions, with a destructive method, which will delete our row. Before we start, if you like building
SwiftUI applications from scratch with me, don't forget to subscribe and to hit the
like button, because it means a lot to us. In this tutorial, we'll learn how to
implement the MongoDB Realm package. This package is an alternative to CoreData
and it allows us to perform CRUD actions, which means create, read, update, and
delete and also to persistently cache our data, even when we're offline. Before starting to code, make
sure you have at least Xcode 13 installed on your MacBook. If you have an older version of Xcode,
make sure to update it, because we will use new features introduced
since iOS 15 and iOS 15 is only available in Xcode 13 and higher. Now, without wasting any more time, let's
start by creating our Xcode project. In the modal that appears when
you launch Xcode, click on create a new Xcode project. In here, we'll need to go under the iOS
tab and click on app, then click on next. We'll need to give our project a name. In my case, I will name it ToDoDemo. Select your team as well as
your organization identifier. So the organization identifier
is your URL, but in reverse. For example, the URL of
Design+Code is designcode.io, so we will write io.designcode. Make sure the interface is
SwiftUI and the language is Swift. Usually these are the default values,
so you don't need to change anything. Then, click on Next. Choose where you want to save
your project and click on create. The first thing I like to do
when I create a new project is to change the simulator target. By default, it will either be iPod
touch or your Mac, so I like to set it to iPhone 13 or iPhone 12. So I'll select iPhone 13 in this case. And we can also add an
icon to our application by clicking on the assets folder. And under app icon, we will simply
need to add an icon for each empty slot by respecting the size that you see. For example, here we need a 60 by 60
pixels icon and here for the app store, we'll need a 1,024 by 1,024 pixels icon. I have my folder containing all the
sizes and I will simply need to drag and drop them into their specific slot. Now that we added all the icons to our
application, we can start coding the UI. We'll take care of the TasksView first,
which is this screen here, which shows a title as well as the task rows. So let's create a new file in our project. This file will be a SwiftUI view. And we'll name it TasksView. Then, click on create. Since we will have a few views,
let's organize our files a bit and create a group called views. Then we'll drag and drop the
content view as well as the tasks view under the views folder. Now in the TasksView, let's
remove the text hello world. And we'll be adding a VStack instead. We'll add two modifiers to the VStack. The first one will be a frame with
a maxWidth of infinity, as well as a maxHeight of infinity as well,
so that it takes the entire screen. We'll also add a custom background color. So the color I will add is
does yellowy orange color. And you can change it if you want
by clicking on the inspectors at the top right corner of Xcode. And at the bottom, you will see
the background and you can custom and change the color if you wish. Inside of the VStack, we'll add a title. So let's add a text that says My tasks. Then let's change the font of
this title, to title3, and add a bit of boldness to the title. Then we'll also add some padding
all around this title, as well as a frame with a maxWidth of infinity
and an alignment to leading. This tasks view will contain a few rows. If you take a look at the final
product, you can see that the task view will contain a few rows and each
row will present a different task. Let's create this component first. So let's add a new group under the
to do demo folder and we'll name it. Components. Then inside of the components folder,
we'll create a new file and this file will also be a SwiftUI view. Let's name this file TaskRow,
then click on create. So each task row will
require two arguments. The first one will be a
task, which will be a string. The second variable will be the completed
status, which will be a boolean. Don't forget to pass the task, as
well as the completed status to the preview, otherwise you will see
an error like this and you won't be able to build your application. So the task, let's say
that it's Do laundry. And the completed status will be true. Now in the body of our task
row, let's remove the text Hello world and add an HStack instead,
with some spacing of 20 pixels. Inside of this HStack,
we'll have two elements. The first one will be an
image with the systemName. The system name is a string and the
string will come from SF Symbols. So let's open the SF Symbols application. And if you don't have it, simply
search for SF Symbols in Google, and then you will find the link to
download this application on your Mac. So let's search for circle here. And you can see that we have a
simple circle, so let's use this icon when the task is not yet completed. And if we scroll down, you
will see at the bottom here, we have a checkmark dot circle. So let's use this when
the task is completed. To do so, instead of simply specifying
one string, we'll add a ternary operator. So we'll check if the
completed status is true. If it's true, we'll call the check. mark.circle icon. Otherwise we'll call the
circle without any checkmark. So you can see here that since
the completed status that I passed to the preview is true,
you can see the checkmark. If a change it to false, we can
see that we only have a circle. So let me change it back to true. And right below this image,
we'll simply display the task. So let's just add a text. And the string will be the task. So you can see that we have
do laundry, which is the task that we passed of this preview. And the completed status is true, so it
shows a circle with a check mark inside. Let's go back to the task view. And for now, you can see that the
task view is pretty empty, but don't worry, because we'll add the task
rows to this view later on, after integrating Realm to our project. Now, let's take care of the add task view. Under the views folder, we'll
right click and create a new file. This file will be a SwiftUI view. And let's name it AddTaskView. Then, click on create. This view is where the user will be
able to add a new task to the app. If we take a look at the final
product, once you click on the plus add button at the bottom of this
screen, we'll see a sheet presentation and this is the AddTaskView. So back in Xcode, let's remove the
text Hello world in AddTaskView and add a VStack, with an alignment to
leading and some spacing of 20 pixels. We'll also need to add a
few modifiers to the VStack. So let's add some padding to
the top 40 pixels, as well as some horizontal padding. Next we'll also need to add a
background color and we'll use the same color that we used in task view. So let's simply copy the background
modifier from the task view. And then paste it in the AddTaskView file. Don't forget to resume your preview. And you can see that for now. We only see a small rectangle in
our view, so let's add some content inside of it, so our background
color takes the entire screen. So let's add a title and it
will say Create a new task. We'll change the front of this title to
title3, and also add some boldness to it. Next, let's add a frame with a
maxWidth of infinity, as well as some alignment to leading. Below the title, we'll
need to add a text field. As the name says, the text field
is a field in which the user will be able to write some content. And this text field will
require two arguments. The first one is a string, and
this will be the placeholder. So we'll write Enter your task here. And the second argument is
a text, which as you can see here, it's a binding string. To create the binding string,
let's create a private state at the top of this structure. So we'll do at state private var
title, which will be a string and by default it will be an empty string. The private keyword here means that
this variable will only be accessible within this view and not outside of it. In order to add a binding, we
simply needs to add the dollar sign in front and then write title. So this will be our binding state,
and the value of the title will always change anytime that the user
adds a character in the text field. Let's resume our preview. And you can see that right now, we
have it text field at the bottom here. Let's change the style of this
text field a bit, so we'll add the text field style modifier. And we'll add the rounded border value. This means that our text field
will have a bit of rounded corners. Then, below the text below,
we'll need to add a button. So a button has two closures. The first one is the action. And the second one will be the label. In the action, let's simply
print a message to the console that says Task added!. In the label, we'll add a
text that says our task. So when the user taps on this button,
we'll add this task, but we'll take care of the functionality later on. Now, the textbooks a bit boring here. So let's change that. We'll add a foreground color of white. And we'll add some padding all round the
button and add more horizontal padding. Then we'll add a background color. And you can select any color
that you want, but I have my color, which is a dark green. And I'll also add some
corner radius of 30 pixels. Right below this button, we'll add
a spacer to push everything to the bottom and you can see that now our
background takes our entire screen. Now let's take a look
again at the final product. So we already coded the TasksView. And we also coded the AddTaskView. The last thing we need to code
is the SmallAddButton here. So let's create that under
the components folder. We'll right click, create a new file. It will be a SwiftUI view and we'll
name this file SmallAddButton. Then click on create. Let's remove the text HelloWorld
and add a ZStack instead. The ZStack means that we are working
on the Z axis, so each element will be on top of one another. Let's simply add a frame with a
height of 50 pixels to this ZStack. The element that we want to be on the
bottom of this ZStack is a circle. So let's add a circle. And you can see that by default, the
foreground color of the circle is black. Let's make sure that the width of
this circle will stay at 50 pixels. Then let's change the
foreground color of this circle. Let's add the foreground color modifier. And we'll take the same green as
the button in our AddTaskView. So let's simply copy the color
of our button's background. And then paste it in the
foreground color modifier in the small add button component. Now on top of this circle, we
want to show a little plus sign. So we'll add a text. And the content will be a string,
and the string will be a plus sign. Then we'll change the font to title. We'll also add some fontWeight, heavy. And the foreground color
of this text will be white. Now that we created all our
components and views, let's add everything to ContentView. So let's go to ContentView. And instead of showing a text that
says Hello world, we will add a ZStack. So let's remove the text and add a ZStack. The alignment of this ZStack will
be to bottom trailing, meaning that everything will be aligned to the
bottom and to the right of the screen. Let's also add a frame to our ZStack. The max west will be infinity and
the max height will also be infinity. We'll also add some alignment to bottom. Now we need a background color, and we
want the same background color that we added in our TasksView and AddTaskView. So let's simply go in
either one of these views. And copied the background modifier. Then go back to ContentView
and paste this modifier here. Now we can see our background color in
the preview here, and the first thing we want to add in our ZStack is a TasksView. So let's call the TasksView. And you can see that right now, we
only see My task here, but we will be adding some content later on. Right below the task view,
we'll call the SmallAddButton. And let's add some padding all around it. Now, we can see our TasksView and an
AddButton at the bottom right corner. However, the user has no way
to access the AddTaskView. We should implement that functionality
when the user taps on this SmallAddButton, so let's do it! In ContentView, let's
add a state at the top. So at state private var, and we'll
call it showAddTaskView and set it by default to false, because we don't want
to show the AddTaskView by default. The private keyword here means that this
state is only accessible inside of the ContentView, and not in any other file. Next, on tap gesture of our
SmallAddButton, so let's add the on tap gesture, we want to toggle
the value of the showAddTaskView. So let's call the showAddTaskView
state, and then call the toggle function, which means that when a
user taps on the SmallAddButton, we will toggle this value to true. Now on the ZStack, we'll add the sheet
modifier and double-click on the first one, with the isPresented parameter. You can see that the isPresented
accept a binding boolean value. If you remember, in our AddTaskView,
in order to add a binding value, we needed to add a dollar
sign in front of the variable. So we'll need to do the
same in ContentView. So let's replace the binding
bool by a dollar sign and then call the showAddTaskView. We don't need to do anything
on dismiss of the sheet. So let's simply remove this. And for the content, you can double-click
on it and add the content inside of the closure that X code will automatically
add, but what I like to do is to simply remove all of this and add a
closure after the closing parentheses. We need to call what we want
to show on the sheet that is being presented on the screen. In our case, we want to call the
AddTaskView, meaning that we will show the AddTaskView on top of the TasksView
when the user taps on the SmallAddButton. So let's resume our preview. And then click on play. And if we click on the plus sign, you can
see that we see a sheet presentation and we can dismiss it by dragging it down. However, we also want to dismiss
the AddTaskView sheet when the user taps on the Add task button. So let's go to the AddTaskView. And at the top, we'll need to
add an environment variable. So at environment. And then we need to add parentheses,
added backslash, dot, dismiss var dismiss. So dismiss is a new environment
variable, introduced since iOS 15, that allows us to easily dismiss a view. So right below the print Task added
to the console, let's call the dismiss with parentheses at the end, so that
it dismisses the sheet, once the user taps on the Add task button. Now let's go back to ContentView
and resume our preview. Let's click on the SmallAddButton. And we can see our sheet presentation. We can write anything in the text
field, but, for the moment, it's not adding anything to our database because
we didn't implement the database yet. So let's right Do laundry, and then
click on Add task and it dismisses the sheet for us, so we're good to go. We can now move on to the more complex
task of adding Realm and the CRUD functionalities to our application. If you don't see the sheet when you
click on this SmallAddButton, make sure you added the showAddTaskView state in
ContentView, toggle its value on top gesture of the SmallAddButton and added
the sheet modifier to the ZStack as well. Now, we need a way to persistently
save the tasks in a database. There's a lot of alternatives out
there to save data persistently in an application like CoreData from Apple,
UserDefaults or AppStorage, or even cloud managed databases like Firestore,
but in this tutorial, as I mentioned earlier, we will learn how to use
MongoDB Realm, because it's pretty straightforward, easy to learn, and we
can persist data even when we're offline. First, let's install the Realm
package in our Xcode project. So click on your project name
and then under project, click on your project name again. And then we'll click on the
Package dependencies tab. Here, we'll click the add button. And we'll add a package to our project. You can see that I already used the
Realm package, so they suggest me to install this package in this project. But for those of you who never used
this package, you will need to copy this link that you can also find in
the text content of the section, and then paste it in the search box here. And Xcode will find the
package on GitHub for you. Now we need to set the
options for this package. For the Dependency Rule option, we'll
keep it to up to next major version. However, we'll change the version to
at least 10.10.0, because we will use the new @Persisted property wrapper
from Realm, which is only available from version 10.10.0 and higher. For the add to project option, select
the project you're currently working on. So by default, this should
already select the project that you're currently working on. Then click on add package. This might take a few minutes
because it will fetch the package from GitHub into your project. You'll, then be able to choose
which package products you wish to install in your project. We'll need both the Realm and the
RealmSwift package, so check both boxes and then click on add package. Once the package is installed,
you might see a few warnings here. However, we have no way to
fix these warnings as they come with the Realm package. Also, they're just warnings,
so we can omit them. Then we'll need to
create the RealmManager. So under our ToDoDemo folder, we'll
right click and create a new file. This file will be his Swift file and
we'll name this file RealmManager. Inside of this file, we'll need to
import RealmSwift, then let's create a class called RealmManager that will
conform to the ObservableObject protocol. By conforming to this protocol, it
will allow the entire application, or at least all views connected
to the RealmManager class, to be notified of changes in this class. In order to use Realm, we
first need to open a Realm. Think of a Realm like a box that we need
to open in order to store data inside. Before opening the Realm, let's create a
variable inside of the RealmManager class. It will be a private set variable var
called localRealm, which will be of Realm type and it will be optional. The private set part means that
we can only set this variable within the RealmManager class. And we are adding an optional here
because opening a Realm might throw an error, and if we throw an error, we
cannot set the value to the localRealm. Next, right below this variable, we
will create a function called openRealm. Inside of this, we need to wrap
everything in a do catch statement. Because, as I mentioned earlier,
opening a Realm might throw an error. In the catch statement, we will print
a message that says Error opening Realm and we'll print the actual error. In the do statement, we first
need to create a config variable. So let config is equals to Realm
dot Configuration and we'll set our schema version to one. Then, right below the config variable,
we need to call Realm dot Configuration dot defaultConfiguration will be the
config variable that we just created. And finally we will try Realm, so
this will create a Realm and you can think of it as opening a box. So let's save this box in
our localRealm variable here. Now, let's call this openRealm
function on initialize of our class. So we'll add an init here and
call the openRealm function. That means that every time
that we will initialize this class, we will open our Realm. Next, we'll need to create a Realm schema. This is the model that each
object we'll create should follow. Think of a model, like a cupcake mold
and each cupcake that you make should follow the shape of the mold, so a
model is pretty similar to a mold. Let's create a new file
under the ToDoDemo folder. And it will be a Swift file. Let's name this file Task, because
our model will be named task. In here, we will need
to import RealmSwift. And Realm accepts classes as model, so
we'll need to create a class called Task and it will conform to the Object and
the ObjectKeyIdentifiable protocols. These two protocols are from
Realm, so remember to import RealmSwift in this file. Then, we need to create a few
variables inside of our class. We'll need to add the @Persisted
property wrapper at the beginning, as specified in Realm's documentation. We'll mark the first variable that
we create as primary key, true var id, which will be an ObjectId. This is pretty similar to the UUID that
is automatically generated in SwiftUI, but since we're using Realm, we'll
use the ObjectId from Realm instead. Then we also need to
add two other variables. The second one is the title,
which will be a string. And the last one will be
a completed status and by default, we'll set it to false. If you run into an error that Persisted
is not a property wrapper, or Xcode cannot find this property wrapper, upgrade your
Realm package to at least version 10.10.0. To check which version you have, simply
go to your project name and under package dependencies, you'll see the
Realm package and the version rules. So if the package is not at
least the version 10.10.0, you will need to upgrade it. You can upgrade it by double clicking
on it and then changing the number here. Now back in our RealmManager, if you
ever need to change the Realm schema, later on, in the config variable, we
will need to pass a second argument, which will be the migration block. And this accepts a closure. In the closure, you can get the migration
as well as the old schema version. And then you'll need
to add an if statement. If old version is higher than one. Then, in here, you will need to
add your code to update the schema. But since we don't need to
update our schema, we can omit the migration block here. The migration block is required
every time you update the schema, otherwise, you'll run into a crash. Now that we opened our Realm and created
a schema, let's take care of the C of the CRUD actions - that is, the create action. In our RealmManager class,
under the openRealm function. Let's read another one
and we'll call it addTask. It will accept the task title as an
argument, which will be a string. Then, inside, we need to unwrap the
optional of the localRealm variable. That means that if we don't have a value
assigned to the localRealm, we won't run the code inside of this if statement. We will wrap everything
in a do catch statement. And in the catch, we'll print
Error adding task to Realm and we'll print the actual error. In the do statement, we need
to try localRealm dot write. And inside of the closure
we will create a new task. So let newTask will be equals to a
task and we need to pass the value variable, which will be a dictionary. So in our task, we have a
title that we need to assign, as well as a completed status. The ID will be auto-generated, so
we don't need to take care of it. So let's assign a title, which will be the
taskTitle that we passed to this function. And we also need to assign
a completed status and by default, we'll set it to false. Then, after creating our
newTask variable, we'll call the localRealm dot add our new task. And finally let's print a message to
the console that says Added new task to Realm and we'll print the new task. Now that we created our create function,
we'll need to code the R of the CRUD actions, that is, the read action. Now, just a disclaimer, we'll create all
of the CRUD functions first, and then after creating all of the functions,
we'll apply the functionality to our UI. So first, at the top of our RealmManager,
we'll create @Published variable private set var and it will be called tasks. It will be an aray of Task and by
default, we'll set it to an empty array. The Published property wrapper
here will allow us to notify our views of any changes in this class. Next, right below our add task
function, let's create another one. And this function will be called getTasks. The first thing we need to do in
this function is the same as in our addTask function, that means unwrapping
the optional of our localRealm. So let's do if localRealm
is equals to localRealm. Inside of this if statement, we will
need to call our localRealm dot objects, so that means that we're getting
all the objects from our localRealm. And the objects requires us to specify
the type of objects that we want to fetch. So in this case, we want
to fetch the task objects. Then, let's call the sorted function
and you can see that it needs a key path, which is a string. So we'll sort all of our objects by
the completed status, meaning that the tasks that are not completed,
will go at the beginning of the array and the ones that are completed
will go to the bottom of our array. Then let's save this in a
variable called allTasks. And if you jump a definition
for the sorted function, you can see that it returns us results. So we'll need to iterate over the
all tasks results, and for each one, we'll append it to the tasks
variable that we created on line 13. So before adding the task to our
array, let's simply reset it to an empty array, because we will call
this function in our add task, update and delete functions as well. Then we'll call allTasks dot for each. So for each task, we will call
the tasks array and append the task that we're iterating through. Now that we have our getTasks
function, remember to call it in the init function of our RealmManager,
because we want to populate the tasks array on initialize of this manager. So let's call this function
right after adding our new task in our addTask function. By doing so, we'll update
the tasks array every time we add a task to our localRealm. Now let's code the updateTask function. So right below the getTasks, we'll
create a new function called updateTask. And this function will accept
two variables as arguments. The first one is the id, which is the
ObjectId, because this is the type of variable that we set in our task class. And we also have a second
argument, which is the completed state and it will be a boolean. Inside of this function again,
we need to unwrap the optional. So if localRealm equals to localRealm,
then we'll call the do catch statement, because writing to our
localRealm might throw an error. In the catch, let's print Error updating
task, and we'll print the ID, to Realm. And we'll print the actual error. Now, inside of the do, we need to find the
exact task that the user wishes to update. So let's call our localRealm dot objects. And we want to fetch all the tasks. Then we will filter. And by filtering, it
accepts and NS predicate. So let's create an NS predicate
and we need to specify the format. The format is what
variable you want to check. And we want to check the ID. So we'll write ID two equal
signs, a percentage, and a at. Then we need to pass the second
argument, which is the ID that we passed to this function. So this will filter all the tasks and only
return us the task with the specified ID. Then let's save this in
a taskToUpdate variable. And we will add a guard statement. So in the guard statement, we'll
add an exclamation mark and call our taskToUpdate, isEmpty. Else, return. So that means that we want to check if
the task to update is not empty, because adding an exclamation mark in front
means that we want the opposite of that. Then right below that, we'll
try our localRealm dot write. And we'll call our taskToUpdate
and get the first one. And we'll change its completed
status to the completed variable that we passed to our update function. Then right below that,
we'll get our tasks. And we'll print a message to
the console saying updated task with ID, and we'll print the ID. And the completed status is the completed
variable that we passed to this function. Finally, let's code the last letter of the
CRUD actions, which is the delete action. So we'll create a new function called
deleteTask, and it will accept an ID which will also be an ObjectId type. Inside of this, we need to
unwrap the localRealm optional. Then we'll add a do catch statement. In the catch, we'll print Error
deleting task, with the ID, from Realm and we'll print the actual error. The delete function is pretty
similar to the update function, so let's copy the first two lines and
paste it inside of our deleteTask. Instead of calling this variable
taskToUpdate, let's call it taskToDelete. And we'll also update the
variable in our guard statement. Then right below that, we'll
call try localRealm dot write. Inside of the closure. We'll call our localRealm dot delete and
pass the taskToDelete to our function. Finally, we'll call the getTasks
function to update the tasks array. And we'll print Deleted task
with id and print the id. Great! We coded all the CRUD actions. Now it's time to connect our
RealmManager to our views. First we'll need to initialize the class. So let's go to ContentView and
we'll intialize our RealmManager here, as a StateObject. So right before our showAddTask
state, we'll create an @StateObject var realmManager will be equals to
RealmManager with parentheses at the end, so that we can initialize this class. We'll also need to add the RealmManager
environment object in our two other views. So in our TasksView and our AddTaskView. So in TasksView, let's add the
EnvironmentObject property wrapper var realmManager will be of RealmManager type. In order to make the preview work
again, don't forget to pass the environmentObject modifier to the preview. And we'll initialize a
RealmManager just for the preview. Let's do the same for the AddTaskView. So @EnvironmentObject var realmManager
will be of RealmManager type. And let's not forget the preview,
by adding an environmentObject modifier and initializing a
RealmManager just for the preview. Now let's go back to ContentView,
and we'll also need to add the environmentObject modifier when
calling both of these views. So on the TasksView, we'll
add the environmentObject and pass our realmManager. Let's copy this line and we'll also need
to add this modifier to our AddTaskView. Now let's take care of the
create and read functionalities. Let's go to our AddTaskView. In the action of our button, instead
of printing task added, we will call the RealmManager dot addTask
and the taskTitle will be the title state that we created on line 12. However, this will add a new task. Even if the title is an empty string. So let's add an if statement. So if title is not equals
to an empty string, we will call our add task function. And we'll keep the dismiss because
once we add a task, we want to dismiss the sheet presentation. Now, let's go to our TasksView and we'll
display all of our tasks in a list. Right below our title,
we will create a list. Since by default, you can see that the
list has a white blue-ish background color, we'll need to change this
background color to a clear background, so that we can see our orange
yellowy background of our TasksView. So we'll call the onAppear modifier
and then we'll add UITableView dot appearance dot background color
will be equals to UIColor dot clear. Then we'll call the UITableViewCell dot
appearance dot background color will also be equals to UIColor dot clear. Where using a list here, because
we will use swipe actions later on. And swipe actions only
work inside of the list. Then inside of this list, we
will iterate through the tasks array of our RealmManager. We'll call the for each. So for each realmManager dot tasks,
we also need to provide an ID, so we will provide the ID of the task
that we're iterating through and we'll get a task in the closure. For each task we want
to display a task row. Luckily, at the beginning of this
tutorial, we already coded our task row. So let's simply call it
inside of our for each. So we'll display a task row and
we need to pass a task, which will be the task dot title. And we also need to pass a completed
status, which will be task dot completed. Now we don't see anything in
our view yet because we didn't add any task to our application. So let's go to ContentView. And in the preview, click on
the play button and we'll be adding a few tasks to our view. So let's click on the add button
here and we'll be adding a few tasks to our application. So let's say that I need to do my laundry,
because I have a lot of laundry to do. Then I will also need to do some grocery
shopping, as well as mop the floor. So you can see that all of our
tasks are added in our TasksView, and you can see that we have a
little separator between each row. We can hide it. So let's go back to TasksView. And on our for each, we'll call the
listRowSeparator and set it to hidden. Now that we implemented the create and
read functionalities, let's take care of the update and delete functionalities. So first we'll do the
update functionality. So we want to update the status of
each task on tap gesture of the row. Still in TasksView, on the task row,
let's add the on tap gesture modifier. So when the user taps on this want to
call the realmManager dot updateTask, and we need to specify an ID, which
will be the task that we get in the closure of the for each, dot ID. And the completed status will be the
opposite of the task dot completed. In other words, by adding the exclamation
mark here, we're passing the opposite value of the current completed state. For example, if the completed is
currently false, we'll pass true instead. And if the completed state is
true, we'll pass false and so on. Now let's go back to ContentView and build
our application again in the preview here. Let's say that we did our grocery
shopping, so I will click on this road and you can see that the circle changed
to a circle with a check mark inside and it moved to the bottom of the list. Great! We implemented the update functionality. We just have one last functionality
to tackle, which is the delete action. Let's go to the TasksView. And we'll implement a swipe action, which
means that the user will be able to swipe on the row, and it will delete our task. So right after the on tap gesture,
we'll add another modifier, which will be the swipe actions modifier. And we can specify an edge, so we'll
specify the trailing edge, which means that the user will need to swipe from
right to left in order to delete the row. You can also set it to leading,
which means that the user will need to swipe from left to right. The content of our swipe
action will be a button. So let's add a button,
with a role destructive. And the button will have a
closure for an action as well as another closure for the label. Let's take care of the label first. So let's create a label, which will
be a delete label and we'll use a system image from SF Symbols,
which will be the trashcan. For the action of this button, we will
call the realmManager dot deleteTask, and we need to specify an ID. Luckily, we already have the ID because
we're iterating through the tasks array. So let's call the task dot id. Now, if we go to ContentView,
and resume the preview. Let's try to delete the
grocery shopping row. So I can swipe on it and click on the
delete button and it will delete my row. Now, let's try to build it in the
simulator and we'll try to delete a row as well in our simulator. So I already added a few tasks,
so let's try to delete a row. By default with the swipe actions, we
can swipe all the way to the left of the screen and it will delete the row. So let's test that out,
I will swipe all the way. However, you can see that it bugged. And I ran into a crash. It says Object has been
deleted or invalidated. We get this error because we're
displaying the task in our task view. So that means that we're trying to
access the task, even though it has been deleted, or invalidated in Realm's terms. To fix this, let's go to our
TasksView and, right before, displaying our task row. We'll add an if statement. So we want to make sure that the task is
not invalidated before displaying our row. So by adding the question
mark, it means that we want the task to not be invalidated. Now let's build this application
again in the simulator. And we'll add another task. Let's say, read a book. And let's update our do launch task. And delete it as well. And you can see that we don't
run into any error anymore. Congratulations! You just built an entire To-do
application from scratch. In this tutorial, we learned how
to useMongoDB Realm as our database to persistently save our tasks. We also learned how to perform
CRUD actions with Realm. Moreover, we covered sheet presentations,
SwiftUI lists, a few modifiers, like swipe actions and on tap gesture. We learned a lot in this video, and I hope
that you enjoyed coding along with me. See you in the next tutorial.