iOS App Development with Swift

Shopping Cart & Refactoring Views

Maximiliano Firtman

Maximiliano Firtman

Independent Consultant
iOS App Development with Swift

Check out a free preview of the full iOS App Development with Swift course

The "Shopping Cart & Refactoring Views" Lesson is part of the full, iOS App Development with Swift course featured in this preview video. Here's what you'd learn in this lesson:

Maximiliano demonstrates adding content to the shopping cart page and an Xcode error message.


Transcript from the "Shopping Cart & Refactoring Views" Lesson

>> So let's do the step that we are missing here, that is the cart. So the cart page right now it's kind of empty, it's just a text saying, Hello World. So let's design that cart page. That, again, it's just a design so it's actually similar to what we've been doing so far.

Before doing that, my CartManager is empty. So, what is the cart? The cart it's actually an array of products, okay, but with one special addition, we have quantity as well, okay? So we have one cappuccino, two muffins. So we can use a tuple for that product quantity, product integer, that makes sense?

So I can create a published property, published variable, because we are going to actually see this in action. It can be the cart that the type is a tuple of products in the chart. And we start with an empty array, that's how you set an empty array like in JavaScript, like in JSON, okay?

By the way, it's an array of this, sorry, my fault. So it's an array of tuples. That's the type, my bad, so an array of tuple. Okay, does it make sense?
>> Yeah, is there a way to set it for example, the second one to zero right away?

>> No, you're going to do that when you add the value to the array, not here, okay? When you add the value to the array you can do that. Okay, so what about the cart page? I'm going now to cart page that is called OrdersPage actually. So I'm going to OrdersPage, I need to replace that text, Hello World, with something more interesting.

So, if we go back to the course, where is so, I'm now in five, four, okay? We need to update DetailsPage, because now we have a product actually but we can do that in a minute. And here we have the OrderPage, okay? So this is the final version of the OrderPage, I will just paste The body.

Okay, and it's a long body and I will show you how to refactor this a little bit, okay? When you have bodies like this that are really long, maybe it's a good idea to start refactoring and creating the smaller views. They don't need to be on a different file, you're gonna have several views in the same file, okay?

That's not actually a problem, but having things like this is too large, okay? So it's a good idea to work with several sub-structures. So, I know it sounds scary because we have a lot of stuff here, but actually we will see in a minute that it's not so big.

First, before actually trying to understand what's inside, we need the CartManager, right? So we need the CartManager. The CartManager is the file that we have just created. We injected that through the hierarchy, but how can we receive that here? So I need a CartManager, okay? And we know it's of type, CartManager.

It's the one that contains the array, okay, we have just created that, it contains the array. So how can we ask the injector, or whoever is working with injecting these views that we want this? We actually need to use another decorator, okay? So it's another decorator, in fact, it was already here, I didn't paste it on purpose, it's called EnvironmentObject.

We don't need to create the object, just with saying this, this is going to bring the singleton that was injected in the App. I don't need to do anything else, I'll just say, in this variable I will capture one of these observable objects that we injected in the app.

Just as a reminder I'm talking about this, when we were adding an EnvironmentObject. So from the other side, we are actually capturing this CartManager option. We are injecting it in the whole app, so any view in the hierarchy, any view no matter how deep is set, it can actually now receive it.

Okay, that's actually how you receive these global objects. Okay, does it make sense? So now if we read the code, it's on navigation view that's the one that sets the title and let us navigate. I'm asking if the count is 0, if it's 0 I'm rendering a text, can I use if?

Yes I can, I can use if, I can use conditionals within views body. So I'm just saying, your order is empty, if not, I'm rendering a list. The list can contain sections, okay? What is a section? Let me show you this directly in iOS settings app. This is a list, so, here we can see different sections.

So this is a section, this is another section, another section, just that. And it's another view with a title and its content. Okay, so I have the items, the details, I will see what is this in a minute. I have another section with a total, with an edge tag, and a button, and some other things.

Let me remove this for a minute. Or we can actually put it like so. Expect the expression, yeah, because this ends here. Okay, let's see if we can preview this, I have A balance issue, and why do I have a balance issue? Because this code is too large.

Remember I mentioned typically we wanna separate this. Can we separate this easily? Well actually when you come and click here, sometimes it depends on the case, you can extract a view. But if this view it's actually needing more items, well, it will not give you the option you need to do it manually, to integrate another structure for a resection or something like that.

So this is complaining because it's expecting an OrderItem, what is an OrderItem? In the cart, we have these UI, this view, this is the OrderItem, including the trash icon. Okay, so it's asking, hey, you need that. What is the OrderItem? Something I need to create. Another view, we have ProductItem, we need OrderItem.

Does it make sense? So in your file, OrderItem. But it's not a view, it's part of a view, yeah. But, I mean, I didn't use the template view so, can I use it anyway? Yeah, but I will not have a preview unless I create a preview manually, that's the only problem.

Okay, so it's not a big deal, but you can still work with this. So, I can delete this and create a new one. So, new file, swiftUI view, and now OrderItem, like so. Let's keep it Hello World, that's okay. So now we do have an ordered item Like that, so let's see what it's complaining here about.

So we have here a name and a phone number. So there is a form, because you're sending a form, from there we have TextFields. Remember with TextFields, we have to set Some variables, remember how @State, yeah, so @State, we have a name And we have an email, okay, like so.

So remember we actually need that, always we need that. So we're removing some elements from here. So we have another problem here, List. When we are looping through elements for each, so far if you remember, we were doing for each with something like this. Remember, we were doing it forEach with a fixed number, okay?

Does it make sense? Instead of doing that, we can do forEach with a collection. Makes sense? The only mandatory problem is the collection needs a way to identify each item individually So we must identify each element somehow. There are two ways to do that, we can define here where is the ID, and for that we use a path.

You say .0, what's that .0? Do you remember what's this?
>> Is it the tuple?
>> Sorry, what?
>> Is it the tuple?
>> The tuple.
>> Yeah.
>> Yeah.
>> In the CartManager, we created a tuple. So the tuple was .0, and because it's a product it has its own ID.

Does it make sense? I'm using that as an example, by the way, I'm calling this cart not product.
>> But the cart manager could have account itself-
>> It's not email form, sorry. It's phone, not email, we can change that for email, I'm sorry, yeah?
>> No, no, the CartManager, you could have count within CartManager too probably, right?

>> Yeah, it's just the same. Well, look at that, it's asking me if I wanna send a bug report.
>> [LAUGH]
>> So let's close the app and try again, just to make this better, okay, interesting. I have this Bill fail, let's see By the way, I can just comment as in any other language, this is just swift that we are writing, so you can use Command forward slash, to comment and uncomment and that's perfectly fine.

I wanna see if I can get rid of that, okay, we are kind of there. So now it's complaining in the provider. So it's missing argument, why? Because it's saying that it should have a name and a phone, that's because I didn't set a default value, and they're not optional.

So, if you wanna create an orders page, you have to set a value unless you have a default value like this, okay? And I think that finally we have something to render, something to see. So when that happens, you see, okay, we have a big crash. And it's important to understand what this crash is actually telling us.

It says coffee masters quit unexpectedly. It says, what's going on here? There is some problem somewhere in our code, and remember that the preview is actually executing the app in the simulator, so it's executing the app in the simulator. So the simulator is not a real iPhone, it's running on my Mac.

So the app actually crashed on my Mac. To actually see what's going on, you need to click on diagnostics in the preview. That will give you the error message or what's going on, sometimes it's useful sometimes it's not. Here it says, let's zoom in, and it's a pretty easy to understand problem.

It says no ObservableObject of type CartManager found. So, it's saying that it didn't find any environmentObject in the preview, this has to do with the preview, look at this, preview is missing environment object. So what's going on? We are injecting, we are bringing an object from the hierarchy, but what about the preview?

>> You're not injecting it there.
>> The preview is just the OrdersPage, it doesn't contain the app, does it make sense? So it doesn't contain that object. So we have a problem, the problem is that, it cannot render the preview, because it doesn't contain the dependency in the hierarchy, because we are just rendering one small part of our app.

So to solve the problem, we need to come to the PreviewProvider, and also add, while it's complaining, so ignore again, ignore again. And we need to inject the environment object in the preview, so which one, a new CartManager, so an empty one, problem solved. Again, this is important, every time you are previewing, you have a preview on a view that has a dependency and that dependency is an environment object, the preview needs that environment object.

So you need to inject one, because it's not actually within the hierarchy. Okay, now it's telling that the order is empty, why? Well, because that CartManager is empty, is one that they have just created without any elements inside. Okay, does it makes sense? So we have that situation that-

>> I think we should put the environment object right away, but maybe not in this page, right?
>> Add the product?
>> Yeah.
>> Yeah, well, probably we will use it later, yeah, somehow for the item. But because the item says, Hello World, right now, that's why nothing happens, okay?

So any questions at this point?
>> So, I mean, if you were really stubborn, if you don't want a preview, you could ignore, I mean, you will have to just compile it each time which would be come out.
>> Yeah, I mean, you can remove the preview and just run the app.

>> But you're losing the preview
>> I know you have to kind of you have it.
>> You're losing the preview ability, that's the problem.
>> You wanted it.
>> Yeah, that's the problem.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now