Add a ViewModel with @EnvironmentObject in SwiftUI | Todo List #3
Summary
TLDR在这段视频中,Nick 通过构建视图模型(ViewModel),向观众展示了如何在他们的应用程序中实现 MVVM(Model-View-ViewModel)架构。他首先解释了 ViewModel 在连接视图(UI 组件)和模型(数据点)之间的中心作用,然后逐步引导观众创建用于添加、读取、更新和删除数据的 ViewModel 类。Nick 还展示了如何在 SwiftUI 中实现列表的删除和移动功能,并将这些功能逻辑移至 ViewModel,以保持视图的简洁性。此外,他还介绍了如何通过环境对象在整个应用中共享 ViewModel,以及如何使用 `@StateObject` 属性包装器来观察 ViewModel 的变化。最后,Nick 通过添加新项、检查文本长度以及切换完成状态等功能,展示了如何在 ViewModel 中封装 CRUD(创建、读取、更新、删除)操作,强调了这种实践对于保持代码清晰和高效的重要性。
Takeaways
- 📝 创建视图模型(ViewModel)是MVVM架构中的核心组件,它连接视图(View)和数据模型(Model)。
- 🔄 ViewModel负责管理视图所需的数据,并通过模型来处理数据逻辑,如创建、读取、更新和删除数据。
- 🎬 通过SwiftUI的ForEach循环,可以使用onDelete和onMove来实现列表项的删除和移动。
- 🚀 将数据逻辑移动到ViewModel中,可以使得视图层(View)更加简洁,只负责UI界面的展示。
- 📚 ViewModel使用`@Published`和`@StateObject`来确保数据变化时,视图能够自动更新。
- 📦 通过创建环境对象(EnvironmentObject),可以在App的不同视图中共享ViewModel。
- 🔗 使用`firstIndex(where:)`来查找数组中特定元素的索引,这是更新数据时常用的方法。
- 🔧 在ItemModel中实现`updateCompletion`函数,用于更新项的状态,保持了数据模型的不可变性。
- 🛠️ 遵循CRUD原则(创建、读取、更新、删除),在ViewModel中实现数据的基本操作。
- 🔄 使用`withAnimation`为视图的交互添加动画效果,提升用户体验。
- 📛 通过`Environment`和`presentationMode`来控制视图的导航和状态。
- 🧩 在视图模型中封装所有数据逻辑,使得视图只关注UI展示,遵循MVVM的设计原则。
Q & A
在Swift UI中,ViewModel的作用是什么?
-ViewModel是MVVM架构中的核心组件,它连接视图(View)和数据模型(Model)。ViewModel负责存储和管理与视图相关的数据,以及包含创建、读取、更新和删除数据的逻辑。
为什么在MVVM架构中需要分离视图和数据逻辑?
-分离视图和数据逻辑可以使得代码更加模块化,易于维护和重用。视图仅负责UI展示,而数据逻辑则由ViewModel处理,这样可以提高代码的可读性和可测试性。
如何在Swift UI中实现删除列表项的功能?
-在Swift UI中,可以通过为List中的每个Item添加一个On Delete手势,来实现删除功能。在On Delete的perform方法中,调用删除数据的函数,并传入相应的Index Set作为参数。
如何实现在Swift UI列表中移动项的功能?
-在Swift UI中,可以通过为List中的每个Item添加一个On Move手势来实现移动功能。在On Move的perform方法中,调用移动数据的函数,并传入源索引集(Index Set)和目标偏移量(Integer)作为参数。
为什么需要将数据和逻辑移动到ViewModel中?
-将数据和逻辑移动到ViewModel中是为了遵循MVVM的设计原则,使得视图(View)只包含UI相关的代码,而所有的数据和业务逻辑都封装在ViewModel中,这样可以提高应用的架构清晰度和可维护性。
如何在Swift UI中创建一个新的ViewModel?
-在Swift UI中创建一个新的ViewModel,首先需要在项目导航器中创建一个新的Swift文件,并命名为ViewModel的名称,如`ListViewModel`。然后在该文件中定义一个类,使其符合`ObservableObject`协议,以便视图可以观察到ViewModel的变化。
如何确保ViewModel在整个应用中被使用?
-为了确保ViewModel在整个应用中被使用,可以将其作为环境对象(EnvironmentObject)嵌入到App的根视图中。这样,所有继承自根视图的子视图都能访问到这个ViewModel。
如何在添加视图中实现添加新项的功能?
-在添加视图中,可以通过创建一个按钮,并为其添加一个点击事件处理函数来实现添加新项的功能。在该函数中,首先检查输入的文本是否符合要求(如长度至少为3个字符),然后通过ViewModel将新项添加到数据数组中,并返回到前一个视图。
如何实现在列表项被点击时切换其完成状态?
-在列表项的ForEach循环中,可以为每个列表项添加一个On Tap Gesture手势。在该手势的处理函数中,调用ViewModel中的更新项的函数,并传入被点击的项,ViewModel会负责切换该项的完成状态。
为什么在更新数据模型时使用不可变结构(Struct)?
-使用不可变结构(Immutable Struct)可以确保数据模型的一致性和安全性。由于所有的属性都是常量(let),它们不能在Struct外部被修改,只能通过Struct提供的函数进行更新,这样可以避免数据不一致的问题。
如何在Swift UI中实现数据的CRUD操作?
-在Swift UI中实现CRUD操作,即创建(Create)、读取(Read)、更新(Update)、删除(Delete)。这通常通过ViewModel来完成,其中会包含相应的函数来处理数据的增加、查询、修改和删除逻辑。
Outlines
😀 构建视图模型 - MVVM架构的核心组件
Nick介绍了视图模型(ViewModel)是MVVM架构中的核心,它位于视图(View)和模型(Model)之间。视图模型负责连接视图和数据,处理数据的创建、读取、更新和删除逻辑。他强调了MVVM架构对于Swift UI开发者的重要性,并预告了视频中将涉及大量编码工作,但会逐步引导观众完成。
📚 视图模型与数据逻辑的分离
为了将视图与数据逻辑分离,Nick展示了如何将数据数组和删除、移动项的逻辑从视图中移至新创建的视图模型中。他解释了使用`@Published`变量和初始化函数(init)来添加模拟数据,并演示了如何将视图模型作为环境对象(Environment Object)在Swift UI中使用。
🔄 视图模型的可观察性与数据更新
Nick说明了如何通过使视图模型符合`ObservableObject`协议,来让视图能够观察数据的变化。他演示了在`ToDo ListApp.swift`文件中创建和初始化视图模型,并使用`@StateObject`属性包装器来观察它。此外,他展示了如何通过环境对象将视图模型传递给所有视图,从而实现数据的集中管理。
🧱 向视图模型添加添加项功能
为了实现添加新项的功能,Nick在视图模型中创建了`addItem`函数,该函数接收一个字符串类型的标题,并将其转换为`ItemModel`对象,然后添加到数据数组中。他进一步演示了如何在添加视图(Add View)中调用此函数,并通过`presentationMode`返回到列表视图。
🚫 输入验证和用户界面反馈
为了提高用户体验,Nick添加了文本输入验证,确保用户输入至少有三个字符才能添加新项。他还创建了一个警报(alert),当输入不满足条件时向用户显示提示信息。此外,Nick展示了如何使用Swift UI的`.alert`修饰符来实现这一交互。
🔄 切换待办项的完成状态
Nick介绍了如何通过`onTapGesture`和`withAnimation`在列表中切换待办项的完成状态。他展示了如何在视图模型中添加`updateItem`函数,并在`ItemModel`中实现更新逻辑,以保持数据的一致性和完整性。
📝 优化代码并遵循MVVM架构
为了遵循MVVM架构的最佳实践,Nick对代码进行了优化,将更新待办项逻辑的代码移至`ItemModel`中。他解释了使用不可变结构体(immutable struct)的好处,并演示了如何通过一个函数来更新待办项的状态,以确保数据的一致性和视图的更新。
🏁 完成MVVM架构并理解CRUD操作
在视频的最后,Nick总结了通过视图模型实现的CRUD(创建、读取、更新、删除)操作,强调了这些操作在处理数据时的重要性。他提醒观众,理解CRUD操作对于任何涉及数据的应用程序都是至关重要的。
Mindmap
Keywords
💡MVVM
💡ViewModel
💡Swift UI
💡Environment Object
💡Observable Object
💡CRUD
💡List
💡Data Array
💡Item Model
💡Swift
💡Xcode
Highlights
在MVVM架构中,视图模型(ViewModel)是连接视图(View)和模型(Model)的核心组件。
视图模型负责管理视图所需的所有数据,并且包含创建、读取、更新和删除数据的逻辑。
通过视图模型,可以实现视图与数据逻辑的分离,提高代码的可维护性。
在SwiftUI中,可以使用`@State`和`@Published`属性包装器来观察对象变化,从而更新视图。
视图模型使用`init`函数初始化时,可以添加假数据以模拟真实应用中的数据。
通过将数据和逻辑移动到视图模型中,视图文件变得更加简洁,只包含UI相关的代码。
使用SwiftUI的环境对象(EnvironmentObject)可以在多个视图中共享视图模型。
视图模型需要遵循`ObservableObject`协议,以便在SwiftUI中被观察。
在添加新项目时,可以通过检查文本框的内容长度来限制用户输入,以确保数据的有效性。
使用`.alert`修饰符可以在SwiftUI中快速创建并显示警告信息。
在SwiftUI中,使用`.onTapGesture`可以为视图添加点击事件。
通过自定义模型的初始化器,可以保持模型的ID不变,同时更新其他属性。
在模型中封装更新逻辑,可以使数据更新更加集中和一致。
CRUD操作(创建、读取、更新、删除)是处理数据的基本函数,适用于大多数数据操作场景。
通过遵循MVVM架构和良好的编程实践,可以创建出结构清晰、易于维护的应用程序。
在SwiftUI开发中,理解并应用MVVM架构对于构建大型和复杂的应用程序至关重要。
Transcripts
[Music]
welcome back everyone i'm nick
this is swiffle thinking and in this
video we're gonna build the view model
in our app
and the view model is really like the
bread and butter the main component of
mvvm and that's because in mvvm
on one side we have the view which is
all of the ui components and we already
did that
on the other side we have the model and
that's that's all of the data
points in our app and then in the middle
is the view model which we're going to
build now
and the view model is going to be
connected to our view and it's going to
hold
all of the data for our views and that
data of course
is going to be a bunch of models that we
made in the last video
so in the view model is going to be all
of the logic
for creating reading updating
and deleting data so it's going to be a
lot of coding in this video
it's probably going to be one of the
harder videos but it's not going to be
hard because i'm going to walk you
through the whole thing
so by the end of this video you will
have successfully set up
mvvm architecture in your first app so i
hope you guys are excited because mvvm
is
such an important architecture to know
as a swift ui developer
and we have just effectively put it into
our app
in such a short amount of time and very
efficiently so now you are an mvvm
expert with that said let's start
building our view model
all right i am back in our xcode project
i'm on the list
view and before we actually create the
view model
let's create some functions as if we
weren't going to use a view model
just so you can see the difference so i
want to add two functions to this list
the first one is going to be for
deleting items from the list
and i did a whole video in the swift ui
bootcamp on this so it should not be new
but when you're in a list and you have a
for each loop you can call at the bottom
of the for each loop
dot on delete and then there's a
completion that says perform
click enter on that and in here where
this says code we can write a function
to remove items from our data array so
from this item's
data at the top now of course we can put
the function here and we can say items
dot i believe it's remove at
and there's a completion that has
removed at offsets
and the at offsets requires an index set
and it just so happens that this on
delete
also gives us an index set for where we
are swiping to delete
so we can pass this index set into here
i'm going to take this one step further
and make it its own function so down
here we'll call func
delete item and we're going to pass in
an
index set so i'm going to say index set
of type index set then i'm going to open
the brackets
and i'm going to take this items.remove
i'm going to copy it i'm going to paste
it into my
new function and now we can just call
this function
from the on delete and because it is set
up with just an index
set we can actually delete all of this
so from the start of this open brackets
to the end of those open brackets
and just add here delete item we click
enter it will include that first
parameter the index
set which we said down here but we
actually don't need this parameter
because it's going to automatically
connect to that parameter so
we can just delete this and we have this
super short super awesome
on delete perform delete item
if we click resume on the canvas
press play on the live preview we should
now be able to swipe
to delete items so it was that easy
and the next one we're going to do is on
move so underneath on delete i'll call
dot on move
and again we have perform with indices
and new offset
so i'm going to create a function down
here below delete item called func
move item and this first indices
is where it's coming from and the new
offset is where it's going to
so i'm going to make these parameters
from
of type so i'm going to hold the option
button click on perform
and we can see that the action here is
giving us an optional
index set so the indices is an index set
and then new offset which is an integer
so
move item from index set
to and this will be an integer
open the brackets and there's another
handy completion here we can call items
which is our data array
dot move and then of course from offsets
will be our from and then two
will be our two offsets so in here
we can now delete this curly bracket
section and just call
move item and of course we can get rid
of the
parameters here and it's just nice and
neat
on delete delete item on move move item
and if we click resume on the canvas and
this is why we added that edit button
in the previous video we can now click
on the edit button
it's going to go into editing mode and
we can press these minuses on the left
to delete items
but we can also click and drag on these
lines on the right
to move items in our list so this is
looking awesome
click done when we're done editing and
now let's start creating the view model
and the reason we create a view model is
to separate the view
from the data logic so all the code in
here is strictly for the view this is
the ui
code but this items array is has nothing
to do with the actual
view this is just the background data
and this delete item in this move item
also has nothing to do with the actual
ui components it's just the background
data
so we're going to move all of this into
our view model
so let's start by right clicking on
the navigator up here and i'm going to
create a new group
i'm going to call this view models
i'm going to move it in between the
models and the views
so we kind of have three main folders
here and i do that because
the view model kind of connects the
models to the views so i like to put it
in the middle
and we're going to right click on the
view models create a new file
and we're not going to need a swift ui
preview here so we're just going to use
a regular swift
file double click it and
let's call this list view model
go ahead and click create and once we're
inside let's create a class
we're going to call it list view model
and we're going to open the brackets
now i'm going to open up two different
screens on my screen here so i'm going
to click this
plus button on the right side which is
the add editor on right
and now we have two screens and right
now they're both in the same file
i'm going to click onto this right file
here
and then i'm going to go to the
navigator and click on the list view
and while i'm in this right side i'm
going to then hide the canvas
because we don't need it right now and
now
this really cool xcode function i can
see two different files
in my app so on the right we have our
list view file and the left we have the
list view model
and the first thing we're going to do is
take this items array and move it into
the class here so if you follow the
swift ui bootcamp you know that we
cannot use the at
state in a regular class we can only use
add state when we're in a view
so instead of at state we're going to
use at published
var and this is going to be an item's
array we're going to call it items
and it will be of type array of item
model
and for right now we'll set it equal to
a blank array
and when we go ahead and create this
class
it's going to call an init function so
we're going to customize that init by
adding init
and we don't need any parameters here
and in our init we then want to add some
of this fake data to our array
so i'm going to create a function to
append items to this array
so we'll call func get items
open and close parentheses open the
brackets
i'm going to say let new items equals
and i'm going to copy this whole array
here so from this open brackets to this
closing bracket here the square brackets
i'm going to set new items equal to that
array that we already had
and then we're going to append these new
items to this items array so we'll say
items dot append contents of
so if we were pending multiple items we
need to do the contents of
then we'll pass in new items
so moving this over just to see it very
quickly
now in our knit we can call get items
so when we create a listview model it's
going to
immediately on its initializer called
get items get items is going to create
these three fake models and then append
them to our items array
the next thing moving back into our list
view here and i'm just going to hide
this on the left here
so on our list view we created those two
functions delete item
move item i'm going to copy those as
well
and i'm just going to paste them into
our list view model
these automatically compile and we don't
have to edit them because it's now
referencing
this new items array that we already
created
so in a second we're going to delete
them from our list view model
and now the only problem we have is that
we need to reference this list view
model
in our list view instead of typing this
in directly
so i'm going to x out of this view on
the right by clicking this little x
button here
and open up the project navigator
and we're going to make a list view
model into an environment object
which i did cover in the swift ui boot
camp so you should be a little bit aware
of what this is
and this listview model is going to use
throughout our entire app so i'm going
to create it
back at the beginning of our app in the
to do listapp.swift file
so in this file before we have the body
i'm going to add a
variable here i'm going to add a var and
we'll call this list
view model and i'm going to keep that l
as lowercase because this is the
variable
it will be of type list view model which
we just created
and we're going to set it equal to a new
list view model
now if you followed the swifty bootcamp
you know that
if we want to observe this object so
that if this object changes
our views will actually update we need
to add a property wrapper on this
and when we create these classes on the
initializer the best property wrapper to
use is the at
state object
and when we do this we're going to get
an error and i did this on purpose just
to show you guys
uh that this the listview model does not
conform to observable object
so now we need to do that and this is
and this is awesome that xcode reminds
us
in case we ever forget so i'm going to
right click on listviewmodel jump to
definition
so we go into the listview model and all
we need to do is make it conform
to observable object just do a colon
observable object
and now it conforms and now we can
observe this class
from our views i'm going to press the
back button and go back to the to-do
list app.swift
i'm going to click command shift k to
clean and rebuild
and our error goes away and now that we
have
this state object here now we have this
object we could pass it directly into
the list view
but even more efficient than that i'm
going to just put it in the environment
as an environment
object so on the entire navigation view
so that
all the views within the navigation view
have access to this
i will add dot environment object
and the object of course needs to be an
observable object which is what we just
made sure it was observable
we'll pass in our list view model
and now the list view as well as the ad
view and all the other views have access
to this listview model so if i
jump into the list view
i will click resume on the canvas just
to make sure we're all connected still
instead of this items we now have our
environment object we can call at
environment object
var and i'm going to call it list view
model
of type list view model
we can delete our items array because we
don't need it because we're going to use
the items that are in the model now so
we'll delete this items array
we will also delete our on our delete
and move
functions because now those functions
are in our model
so we'll delete these
and we're getting an error message we
cannot find the items array
in scope and that's because we need to
now access the model
and then the items that are inside the
model so we'll call
list view model dot items
that first error goes away and now it's
also telling us that it can't find the
delete item and that's because delete
item is not
just on our list view now it is in our
list view model so we can call
list view model dot delete item
and again we don't need that parameter
so that works
and list view model dot move item
all of our errors go away and you'll
notice here immediately that
we just have so much less logic in our
list view like this is a very
short view and that's because we moved
all of the
data references where we add data edit
data
into the view model so that all the
logic that remains in our view here is
strictly for the ui if i click resume on
the canvas
we're getting a crash and that's because
the preview which is what the canvas is
running
doesn't have that environment object
because that
environment object is only being added
when we actually run the app
through our to-do list app.swift file so
to fix that we can also just add
an object into the preview so we'll call
dot environment object
and we'll add a new list view model
open and close parentheses this is just
initialize a new one and put it into the
environment
our preview now loads we can swipe to
delete items still
and of course we can edit and
move items as well
so all of our functions are running our
logic is in our view model
and there's a couple more functions i
want to add before we finish this
video the first one of course is being
able to add an item to this
so if we click the add button and we
segue to our ad item view
when we're here i want to be able to
type something this
is a new item and then click save and
have it
update on our list so let's jump into
the ad view
and we already have a button here so i'm
gonna create a function that's gonna run
when we click on this button so
underneath the body
i'll call funk and let's call
save button pressed open and close
parentheses
open the brackets and because this is a
function now we can get rid of these
curly brackets in our action
and just add save button pressed
and now we're going to add some logic
into our save button pressed
and the first thing we want to do is
append our new item
to the data array and since our view
model is in the environment we already
have access to it in our ad view
so at the top i'll call at environment
object var list view
model of type list view model
now just like on the last screen the
preview doesn't have access to it so we
got to add one to the preview
so down on the preview on the navigation
view we'll add dot environment object
a new list view model that will
initialize
and let's click resume on the canvas
just to make sure it still builds
and now when we add an item we're going
to use the text that's in the text
field and create a new item but all of
that logic for creating a new item and
adding it to the array i want to put
into our list view model
so i'm going to jump into the list view
model
we have delete item we have move item
now let's create a funk
add item and as i just mentioned when we
add an item we're going to have the text
already so we'll call it
title of type string
and we'll open the brackets and with
this title we'll create a new item so
we'll say let
new item equals item model
open the parentheses and the title will
be title
and is completed so since we're adding a
new item is completed is going to be
false to start so we did not complete it
yet otherwise we wouldn't be putting it
on our list
and then we're just going to add this
new item to our items array so we'll say
items dot append new element and we'll
add new item
it's that simple let me go back into our
add view
i'm going to press command shift k to
clean and rebuild
again you can always click the product
and clean build folder which is
shift command k click resume
and when save button is pressed we're
going to call list view model
dot add item and the title is going to
be
the text field text so text field
text
now after we click add item and we save
that item
i want to automatically pop out of this
screen so it navigates back to our list
to go back when we're in an environment
or in a sheet
we use that environment presentation
mode so i covered this in the swift ui
bootcamp as well
at the top here we'll add at environment
open the parentheses and we're going to
use the key path
the key path will be backslash dot
presentation
mode and this will be a variable called
presentation mode and if you did this
right
this will turn purple so that you know
it is connected
and the presentation mode again is is
something that comes by default on all
of our screens in swift ui
and it is just monitoring where we are
in our view hierarchy
so by using the presentation mode we can
always basically tell it to go back one
in our view hierarchy
and that's what we're going to tell it
right now so save button press we're
going to call
presentation mode which is our new
variable we just created
dot wrapped value dot dismiss
and this looks a little ugly but all it
does is tell the presentation mode
to go back one in the in the view
hierarchy
so let's test this out quick so let's
press run on the simulator
simulator should pop up on your computer
somewhere
so we have our to-do list we can swipe
to delete we can
edit and move items but now let's try
adding an item so we click add we go
here
let's say this is
the new item i'm going to click save
when we click save it should add the
item to our data array
and also navigate me back to the list so
click save and boom it popped us back
and we have our new this is the new item
at the bottom of the list
so this is working perfectly now while
we're here there's a little bit of logic
i want to add to this ad screen
because if someone doesn't have anything
typed here
or if they only have like one letter
typed i don't want them to be able to
add items
i want to do a quick little check to
make sure they have at least like three
or
four characters in this text field in
order to save a new item
so let's do that quickly and i'm gonna
hide the simulator quickly and where we
have our save button pressed
let's first check the text field text to
make sure that it is long enough
so i'm going to create a function to
check the text so we'll create a new
func
we'll say funk text is appropriate
open and close the parentheses and this
is going to return
which we use an arrow and then a bool
open the brackets
so if the text is appropriate it will
return true
if it's not appropriate it will turn
false
so we're gonna say if text field text
dot count is less than three so if it's
less than three characters long
open the brackets and we will return
false
and if we return here it's going to
return out of this function and never
call the rest of this logic
but if it gets the rest of this logic
we'll just return
true so if it's less than three it will
return false if it's
equal to or greater than three it will
return true
and now when we call save button pressed
we're just going to add some logic here
we're going to say
if text is appropriate open brackets
then we're going to put this logic
inside these brackets
so it's going to say if and then it's
going to run this function this new text
is appropriate function
which is going to return true or false
so if this
function is equal to true then it's
going to run
all this logic but of course in swift we
don't need this actual equals to true it
just knows
that this is saying the same thing as
equal to true so if
text is appropriate add item and go back
and now the last thing i want to do here
is if this is
false because it's less than 3 i want to
show an alert
on the screens because right now if the
user clicked save and it was less than
three
nothing would happen it would just look
like the app wasn't working
so instead i want something to pop up
that says this should be at least three
characters long
so the user knows they need to keep
typing
so let's create an alert so at the top
of our view here
i'm going to add two variables for alert
we'll do at
state var alert title of type string
we'll set it equal to a blank string for
now and
at state var show alert of type
bool and we'll set it equal to false
and now this is going to be the title
that we want to show on the alert and
this of course is going to be toggled
when we want to show the alert
so we could add this alert modifier
anywhere in our view and it would work
i'm just going to add it underneath the
navigation title so we'll do
dot alert and we're looking for the is
presented completion and
is presented of course we're going to
bind it to our money sign
show alert and for our content we could
put it in here but i like to create
functions just to keep our code clean
so down at the bottom underneath text is
appropriate we'll call funk
get alert open and close parentheses and
this is going to return
an alert open the brackets
and right now it's just going to return
alert
open the parentheses and we're just
going to use the method with just a
title
the title requires a text so we'll type
in a text
and we need to add a string to the text
and of course the title for our alert
is going to be the variable we created
at the top so we'll call alert
title
now when do we want to show this alert
we want to show it when we
fail this text is appropriate so right
before we return
false we're going to call show alert dot
toggle
and the last thing i want to make sure
here is that before we show the alert we
set that alert title so that it sets
appropriately
so before we call show alert i'm going
to say alert title
equals and let's set it to our alert
message
because we're failing this count right
here of three characters let's say
your new to do item
must be at least three characters
long and i'm going to press the ctrl
command space
to get the emojis and i love putting
emojis in my code
users usually love it too and let's use
maybe uh
one of these like worried emojis put a
couple of them in because it might look
cool
so now our alert should be working and
if this was your app
you might have other checks you want to
do here when the text is appropriate
a lot of apps want to check for curse
words so after
this check you might have another check
down here
if there are curse words in your string
and if there are curse words
you would obviously change the alert
title to you can't use curse words in
your
in your to-do item and then show alert
as well
but with what we have now we can put
this get alert into our alert function
so i'm going to remove these curly
brackets here
and just call get alert
so this is nice and clean up here in our
view
and let's test this out and click run on
the simulator one more time
all right click the add and before we
type anything in let's click save
your new to do item must be at least
three characters long so our alert is
working
and if we type in two characters hi
we're still gonna get that error but
once we have three characters i'm gonna
do
hii click save it now works and we
append our new to do item to our to-do
list
so working perfectly and there is just
one more function i want to do in our
view model before we end this video
and that's if i click on one of these
rows
i want to be able to toggle it or switch
it between completed
and not completed because right now if
we do if we click on them
we have no way of actually changing them
from not completed to completed
so let's do that real quick i'm gonna
hide the simulator
one more time all right so i'm gonna
jump back into our list view
and click resume on the canvas and on
this list row view
on one of these rows if i click on that
row
i want to toggle the completion between
not completed and completed
so on this whole list row view we'll
just add an
on tap gesture
and when we do this i want to add some
animation so that it animates
from the current state to the next state
so we'll do with animation
and let's do dot linear which is just a
kind of a seamless straight line
animation
and we'll open the brackets and now in
this with animation we need to add some
logic to
update our item and so we have the item
here that we're looping on in our for
each
but we don't have a function yet and as
you're probably already guessing a great
place to put that function is in our
list view model
so let's jump into the list view model
i'm going to right click it jump into
definition
and we have delete we have move we have
add and now let's add
func update item
and we're going to pass in the item that
we clicked on so we'll say item
of type item model we're going to open
the brackets
so we have this item already but we're
going to need to
figure out where this item is in our
items array at the top here
because we don't have the actual index
for this item so first let's get the
index
so where this item that we clicked on is
in our
items array and to do that we're going
to say
i'm going to walk you through this
quickly because some people might get
confused
so first let's say let index equals
items which is our items array dot first
index where
and this is going to find the first
index
and we know the first index is the same
as the only index because all these
items are unique
so first index where and let's click
enter on this completion so this is
going to loop through our array
and for each of the existing items in
the array so that's what this
is here so this will be existing item
we want to return the first case where
the existing item's id
is the same as this item's id so in here
we will return
existing item dot id is
is equal to item dot id
so this index is going to basically be
the first index where
an item in your items array has the same
id
as the item that we are looking for and
we know that's going to be the same item
and if we hold the option button and
click on first index
we'll see that this returns an optional
integer if we click on
hold the index option button and click
on the index here
we'll see that it is optional with the
question mark and it's optional because
in case there's a situation where we try
to get this first index and there is no
item with the same id
then it will return nil but of course we
know this is going to be true and we
want to
safely unwrap this index so instead of
just using a
let statement here we can say if let
and then we will open the brackets
afterwards so if this index is true
we will run this code
and now i wanted to walk you through
this because this is super powerful
but there's actually a shorter way to
write this bit of logic here
so let's do that down here and this is
just good practice
so this time let's just do if let index
equals items
dot first index where
and then in this instead of pressing
enter we're gonna open
the brackets here and press space
and in here we will do the money sign
zero dot id and this money sign
zero is what we're looping on so it's
the same thing as this existing item
here
and we'll just set the existing item
equal to
item dot id and then we can open the
brackets
so this line of code is the exact same
as these three lines of code
but it's just a little shorter to write
this and if you're working on like a
team with other professionals they're
probably gonna end up writing it like
this
just because it is quicker to read it's
quicker to write
so we actually don't need this code
anymore i'm going to just
highlight it press the command and
backslash to comment it out
just wanted to show you guys that and
now now that we have this new index
we just want to update the item at the
index so what we're going to do is
call items and we're going to access
that index by using the brackets
the index so this refers to the item at
that index
and we're just going to set it equal to
a new item model
open the parenthesis and this new item
model is going to have the same title as
the
current item model but a different
completed status
so we'll call item dot title
and is completed will be item dot
completed and then we're just going to
use the
not sign so print an exclamation point
before it so it's the opposite of the
item.completed
and now there's something happening
behind the scenes here that you might
not have realized
but when we create a new item model
every time we create a new item model
it creates a new id so if we set this
index equal to this new item model
it's actually not the same as this item
so it's actually going to be a
new item model with a new id but what we
really want is the same
item models id just with the new is
completed
so we need to fix the initializer in our
item model and when we go to do that
it's also going to be more convenient to
include all the logic
for updating and changing these item
models within the item model itself
so that if we ever need to update item
model from anywhere else in our app
we don't do it from our view model we do
we update the item model directly from
the item model and that's good practice
and
in line with mvvm architecture as well
so this logic here we're going to get
rid of in a second
so let's just right click the item model
jump to definition
so we go into our item model screen here
and
as i mentioned when we create a new item
model right now it creates a new
uuid string every time but let's
customize this initializer so that if we
have the id
already it doesn't create a new one so
right now the init looks like this we'll
do init
and it looks like title of type string
is completed of type pool
and open the brackets and right now
what's happening
it's really calling self.id which is
this variable here equals uuid
dot string self.title equals
title self dot is completed eagles is
completed
so this is what we just saw before this
is the current initializer that we're
using
what we're going to do is customize it
so that we can optionally add an id in
here
and the first thing we're going to do is
get rid of this string so instead of
setting it up here
automatically we're going to set it
through the initializer
and now i want to pass in an id into our
knit as well so we'll do id
of type string and then make that as new
parameter
i want to reference this id but i also
want to include a default case so that
if we don't have a string here
we can still initialize this variable by
using a random id string
so i'm going to make the id of type
string but we're going to also
set it equal to uuid dot
uuid string and here we're going to call
id so that we reference this id so
with this parameter in here we basically
can
initialize an item model with an id
if we give it a string or without an id
and it will automatically create one
so anywhere in my app if i call item
model
open the parentheses i have two
different initializers here
one where i want to add the id directly
and then another item model
one where i don't have an option of
adding an id and it will automatically
create the id
for us so this first item model we're
going to use
when we create new items and this second
one with the id we're going to use when
we update items because we already have
an id
let's delete these the last thing i want
to do here is put a function within our
item model
so that we can update the item model
so let's create a func update completion
open close parenthesis and this is going
to return an item
model and open the brackets
and we're going to return of course item
model open the parentheses
and we're going to use the id title and
is completed
so this will be called from within an
item model so we already have an item
model
and then on that existing model we're
going to call update completed
so when we do this we're going to call
we're going to create a new item model
but we want to use the same id
so now in here we're going to reference
the current id that's in the item model
that we're inside
so we'll pass in id which is this id
here
the title is going to stay the same so
we'll pass in the title which is here
and then we want to do the opposite of
the current is completed so we're going
to do
exclamation point for not is completed
and something i haven't really touched
on but you can google
this struct is called an immutable
struct so i'll type in here immutable
struct and it is a very common pretty
good practice a lot of developers will
recommend it
when you create these structs all of
these items are let
items and because they're not variable
that means they will never
change and the reason we do that is
because if it's not immutable and these
were
var there would be a bunch of cases
where you could change this title
directly you could change it from the
view model you could change it from
somewhere else
and if you did that there might be a
case like on accident
where the title is different from where
it actually is maybe in your database or
something like that
so by creating an immutable struct and
creating these all as
let constants they cannot just change on
the fly
and that's why we're updating our struct
here only through this function down
here
so we can never change any of these
items or any of these
variables unless it's through this
function and this way we can make sure
that
all of the logic in our app that refers
to updating and model
is just within this function keeping it
clean
and efficient let's now call this update
completion
back in our code so i'm going to go to
the list view model
and let's press command shift command k
to clean and rebuild
i'm going to delete this item model here
because we
we now know that we do not update items
outside of the item model we only do it
within an item model
so what we're going to do we're going to
set this equal to our current item which
is item
dot update completion
this is going to handle all the logic
for updating that item for us
all right this is looking good i'm going
to delete this code here you can keep it
in your code if you want to just
reference it
this is looking clean this is looking
concise and let's put this update item
back into our code so let's jump into
the list view
with animation we're going to call list
view model dot
update item and we're going to pass in
our item
and let's run the simulator one more
time for this video and see if it works
we have our items and now if we click it
boom
it's completed and we can toggle all
these
so easily so well we did it with super
efficient code
we can add new items this is
my item and we can toggle them
we can then edit and move them and of
course we can
delete them we added a view model to our
app
and in the view model we handled all the
logic for
updating moving adding deleting items
and for those of you who are wondering
why we added these four functions it's
because
these are the crud functions so i'm
going to add at the top here a little
bit of comment here
let's do a multi-line comment with crud
functions and basically anytime you're
dealing with data
all of the functions in your app are
going to derive they're going to be
derivatives of
the four crud functions and crud stands
for
create read update
delete so all functions for your data
are going to derive from these four
so in our app we now have a function to
add items which is creating a new
item we have a function to get items
which is
reading items we have a function to
update the item so we toggle the item's
completion
and we have a function to delete the
item if we want to swipe to delete that
item
and this is the same for if you had
users in your app you could create a
user profile you would read and get that
user's information
update a user's profile and then delete
a user's profile
so anytime you're learning and you're
dealing with that data a good thing to
benchmark would be to just make sure
that you
can understand and know how to create
all four of these types of functions
with your data
all right guys i'm done rambling sorry
this was another longer video but we
covered a lot of really good juicy stuff
hope you enjoyed it as always i'm nick
this is swiffle thinking
and i will see you in the next video
you
Просмотреть больше связанных видео
![](https://i.ytimg.com/vi/SMt4_WUdKag/hq720.jpg)
Create a custom data model for Todo items in SwiftUI | Todo List #2
![](https://i.ytimg.com/vi/EPdivac0kwE/hq720.jpg)
Create a List of Todo items in SwiftUI | Todo List #1
![](https://i.ytimg.com/vi/KamCx-Hfdxk/hq720.jpg)
User Experience and Animations in SwiftUI app | Todo List #5
![](https://i.ytimg.com/vi/3CasiUiJPVo/hq720.jpg)
Adding an App Icon and Launch Screen to SwiftUI | Todo List #7
![](https://i.ytimg.com/vi/ugiNsb9pc8k/hq720.jpg)
Unreal Engine 5 RPG Tutorial Series - #14: Equipment System
![](https://i.ytimg.com/vi/1QOLiELlpGk/hq720.jpg)
Save and persist data with UserDefaults | Todo List #4
5.0 / 5 (0 votes)