LongCut logo

SwiftUI Course for Beginners – Create an iOS App from Scratch

By freeCodeCamp.org

Summary

## Key takeaways - **Avoid hard-coded strings**: Hard-coded strings are convenient but can lead to typos and be harder to maintain when the application grows. Create a constants file with static let strings to build good coding habits. [07:47], [08:07] - **Use DRY principle**: Don't repeat yourself promotes efficiency and reduces redundancy; if developers use code more than once, write it once. Create a ghost button extension on Text to apply modifiers without copying. [21:23], [21:43] - **MVVM separates concerns**: Model handles data like Title struct for API decoding, ViewModel manages business logic with @Observable class and fetch states, View focuses purely on UI display. This keeps code organized and testable even in small projects. [01:32:00], [01:33:17] - **Debounce search API calls**: Delay API calls 500ms after user stops typing using task with ID and debounce to prevent unnecessary requests on every keystroke. If task cancelled, exit early to avoid outdated searches. [03:25:23], [03:26:28] - **Swift Data simplifies persistence**: Mark Title class with @Model, add @Attribute(.unique) on id to prevent duplicates, use @Query for automatic fetching and sorting by title, modelContext.insert and delete for CRUD operations. [03:35:43], [03:40:06]

Topics Covered

  • Constants Prevent Typos in Scaling Apps
  • DRY Principle Cuts Code Redundancy
  • Async Let Enables Parallel API Calls
  • MVVM Separates UI from Business Logic
  • Swift Data Handles Persistence Seamlessly

Full Transcript

Learn how to create a complete iOS app from scratch using Swift UI and Xcode.

In this tutorial, Carlos will teach you to build a featurerich movie and TV browsing app with a dynamic home screen powerful search, and detail screens that

play YouTube trailers. You'll also

implement a download manager to save titles for offline viewing using Swift data. By integrating the movie database

data. By integrating the movie database and YouTube APIs, this project provides excellent hands-on practice with Swift UI API integration and modern data

storage. Hey everyone, Carlos from

storage. Hey everyone, Carlos from Blossom Build back with a new exciting project. We'll be building a complete

project. We'll be building a complete iOS app from scratch using Swift UI and Xcode.

Before we look at the app, I want to give a quick shout out to Ommer. I

originally learned how to build this app by watching Omer's YouTube channel. He

built it using UI kit and I learned a great deal from him. Please check out his channel and show some support.

This is a similar app but updated with Swift UI, Swift data, and the latest iOS version. There are also a few more added

version. There are also a few more added features. We're going to try to keep the

features. We're going to try to keep the app updated so it can remain a valuable resource.

Let's take a look at it. I named it Blossom Movie.

First, opening the app, you can see it has four tabs on the bottom toolbar. The

home screen features a hero title that changes whenever the app is opened.

Scrolling down, the app shows trending movies and TV shows, as well as top rated movies and TV shows.

We're able to scroll vertically to get more titles that are instantly loaded.

This is possible by working with the movie database API.

Clicking on a title opens a detail screen.

This screen contains a name description, and a YouTube trailer. This

project also works with the YouTube API.

Navigating to upcoming we see a list of upcoming movies in a vertical list this time. We can scroll down to quickly get more titles.

Selecting any title brings up the title detail screen.

Moving on to search.

This is my favorite part of the app. The

titles on a grid this time and we can conduct a search. For example, I can search for Harry Potter and instantly get results.

We can cancel and the default list displays.

The button up top can be pressed to switch the search to TV shows. I'll

search my favorite TV show, Breaking Bad.

Finally, we have the download tab.

Currently, this message is displayed because we have no downloads.

If we download a title and go back to the download tab it displays right away.

Closing and reopening the app does not remove it from the download tab.

We can instead swipe left to delete the file.

This is a great app to build for some practice with Swift UI APIs, Swift data and more. I'm excited to start this

and more. I'm excited to start this journey with you. We're ready to start this project. The first step, of course

this project. The first step, of course is to create it. Before that, please make sure your Xcode and Mac OS are updated. I'll select create new project.

updated. I'll select create new project.

We're going to be making an iOS application.

Next, for the product name, let's go with Blossom Movie. I do have myself as a

Blossom Movie. I do have myself as a team. To do this, you'll have to log

team. To do this, you'll have to log into your Apple ID. You can also use none.

The organization identifier is a domain name of your company backwards. I have

mine as Blossom Build, but feel free to use yours or make one up. Interface has

to be Swift UI. language swift testing system none and storage none. We'll be

using swift data as a storage. However

if we select it, the project will create unnecessary code. It's easier to add

unnecessary code. It's easier to add later. Hit next.

later. Hit next.

You can save this project anywhere you like. Select create once you got your

like. Select create once you got your folder.

We have our project created and the famous hello world pops up. I'll do

command control F to go full screen.

I'll also make my canvas smaller to allow more space for the code.

I do want to write just a bit of code to get our feet wet. A good place to start is to create the four tabs our app will use. In the content view, we can delete

use. In the content view, we can delete from padding up to vstack.

Instead, type tab view open curly brace and enter. Inside the

tab view curly braces, we need to define our tabs. Type tab open parenthesis.

our tabs. Type tab open parenthesis.

There are two parameters here. First, a

string for the tab name. Quote, type in home quote again, comma. The second

parameter is the icon image we want to use. We're going to be using the free SF

use. We're going to be using the free SF symbols built into Xcode.

Type system and Xcode is recommending the correct name. Press tab to autocomplete.

quote type house close quote close parenthesis open curly brace and enter.

Inside these braces, we need to define the content of this tab. A text display home would do just fine. That is our first

tab complete. Notice our preview updated

tab complete. Notice our preview updated correctly.

For efficiency, let's copy our tab by highlighting and pressing command C.

Paste it three times with command V.

The second tab will be called upcoming.

We can change system image to play.

Circle.

The text will also change to upcoming.

The third tab is search system image will be magnifying glass text of course is also search.

Finally we have download system image is arrowd down.2 two line

down.2 two line text is also download.

I think that looks pretty good for now.

We can click through the different tabs in our canvas to confirm.

We build our bottom toolbar containing four tabs using pretty simple code.

Swift UI makes it that easy. This is a great place to stop. Awesome work.

We have our tab view working properly.

Before we get into more Swift UI, a quick topic. Currently, our content view

quick topic. Currently, our content view contains hard-coded strings. An example

is the home string in our first tab.

While hard-coded strings are convenient they, can lead, to, typos, and, be, harder, to maintain when the application grows.

This is not going to be a published app but I do believe in building good coding habits. Therefore, we'll create a

habits. Therefore, we'll create a constant file and put all the strings there. To begin, use a shortcut command

there. To begin, use a shortcut command N to make a new file. Select Swift file.

Next, call this file constants.

Create.

Under import foundation, type strr constants.

If the editor recommends the correct syntax, press tab to complete like I just did. Time to make our first string.

just did. Time to make our first string.

Static. Let home string equals home.

Static allows this string to be accessed across the app. Let means the string won't change within the program. Of

course, we can still change it in the code. We'll keep going. Static let

code. We'll keep going. Static let

upcoming string equals upcoming.

static let search string equals search static let download string equals download.

It's also good to be specific when naming variables. Navigate back to

naming variables. Navigate back to content view delete home instead type constants

home string.

Do the same for the home inside text.

Upcoming is next. Delete and replace both occurrences.

constants upcoming string.

Constants do upcoming string.

Next to search constants dot search string constants

search string.

Finally download constants.d

constants.d download string constants.d downloadd download string.

constants.d downloadd download string.

We also want to put the system image names in our constants file. Go back to constants.

Make some space. Static let home icon string equals house static let

upcoming icon string equals play dot circle static lamp. Search icon string equals

static lamp. Search icon string equals magnifying glass static lamp. Download icon string equals

static lamp. Download icon string equals arrow down.2

arrow down.2 line. Back to our content view one more

line. Back to our content view one more time.

Replace house with constants dot home icon string.

Playout circle with constants dot upcoming icon string.

Magnifying glass with constants dot search icon string and arrow.down.2.line

and arrow.down.2.line

with constants dot download icon string.

Not the most exciting work, but it's good for us in the long run. One more

benefit to doing this is easier migration to different languages.

We'll stop here, but I promise to have exciting Swifty action next time.

We're effectively following good stream practices. It'll be easier to apply for

practices. It'll be easier to apply for the rest of our app. Now, we can start working on the home screen. I do want our content view to stay clean and just hold the tab view. It'll get big fast if

we code all the screens on it. Let's

make a new Swift UI file for the home screen. Press command N. Swift UI view.

screen. Press command N. Swift UI view.

Next, home view.

Create.

The home screen first features a hero title. All the images used for this app

title. All the images used for this app are inside the internet as URLs. For

testing, we can define a temporary image at the top of our home view. Hit enter

after the first curly brace twice.

Back to line 11 bar hero test title equals quotes.

Navigate to constants.

After our last string, hit enter a couple times.

static let test title URL equals single quote.

I'll paste a sample URL. It will be in the video description. Please copy and paste it as well. Go back to home view.

Replace the empty string with constants test title URL.

Now we can build our view. Delete the

text view.

Type vstack with braces.

vstack aligns all the views inside its braces vertically.

Our image needs to be loaded asynchronously because it's coming from online. This means it loads on a

online. This means it loads on a separate thread, not the main thread.

That's for our UI. Swift UI has a great tool called async image.

Start typing async.

Select the second one and press enter.

Async image takes a URL, but our hero test title is a string. We can easily convert it by typing URL in all caps open parenthesis

string colon hero test title.

We're using Beetlejuice as our practice title. I honestly like the poster art.

title. I honestly like the poster art.

The image is pretty big right now. In

order to adjust it, put braces at the end of our async image.

After the first brace, type image in.

This puts our async image inside a variable called image.

We can put modifiers on this variable to adjust it. Before we do that, we have to

adjust it. Before we do that, we have to define a placeholder while the image loads. After the async image curly

loads. After the async image curly braces, type place and hit enter to autocomplete.

In the placeholder braces, we can type progress view.

As the image loads, progress view, which contains a progress circle, will show.

Let's go back inside our async image braces.

Type image.resizable.caleto

fit.

These two things we just added are called modifiers.

Modifiers allow us to change our views.

In this case, we're allowing the image to be resizable and fitting it in our parent view without distortion.

Modifiers are usually in separate lines so they can stand out. Put your cursor right after the Ean image and press enter.

I'll press escape to dismiss that suggestion.

Press enter again after the resizable closing parenthesis.

This is looking pretty good. I also

think it's a good place to stop. Great

job on this one. Our hero test title is displayed correctly. Under the hero

displayed correctly. Under the hero title, we need two buttons. One button

to play the trailer and another to download the title. Make some space after the placeholder braces.

These two buttons should be side by side. To do this, we can type hstack

side. To do this, we can type hstack with braces.

htack orders its views horizontally.

Inside our H stack, type button with braces.

After the closing brace, type label and hit tab to autocomplete.

In the first set of braces, we'll tell the button what action to perform. The

second set is for design. Before that

we need to define a string. Navigate to

constants.

Make some space after the download string.

static let play string equals play.

Lucky for us, the download string is already defined. Navigate back to home

already defined. Navigate back to home view.

Inside label, type text constants dot play string.

We can now see a small play button in our preview. Copy and paste the button

our preview. Copy and paste the button code to make another one. Change the

second text to constants dot download string. These buttons work, but they

string. These buttons work, but they need some design. We can add some modifiers. After the play string text

modifiers. After the play string text hit enter and type dot frame.

The first option is in what we need.

Press your right arrow key for more options. Select the option that contains

options. Select the option that contains width and height.

The frame modifier adds an invisible frame with a specified size, but 100 for the width and 50 for the height.

Press enter after the closing parenthesis and add dot bold. This bolds

the text. A border would look nice around the button. Press enter

dot background.

We want the one that has content.

Press enter for braces.

Inside the background braces, input rounded rectangle.

Select the option with corner radius and style.

Use 20 for the radius and dot continuous for style.

This makes a rounded rectangle with the radius of 20, but our text is covered.

We can use the stroke modifier to just keep the border. After the closing parenthesis, press enter.

Type dot stroke.

Select the one with line width five.

Much better. The button looks pretty good. But I would like to add our custom

good. But I would like to add our custom color. Navigate to assets.

color. Navigate to assets.

Right click.

New color set.

But in border enter. Open the inspector.

enter. Open the inspector.

Change appearances to none.

This color will be the same for dark and light mode. Click where it says

light mode. Click where it says universal.

Show color panel.

Type in 34 B4 6C for the hex color.

Enter.

That's blossom build green.

Close the color panel. While we're here let's add one more color. Right click

new color set button text enter.

Make sure appearance is any dark.

Click on any appearance.

Show color panel.

Six zeros. 1 2 3 4 5 6.

Enter.

Close the color panel.

When the app is in light mode, the button text color should be black. In

dark mode, the color should be white.

This is a great place to stop.

Outstanding work.

The colors are ready to go. We just need to apply them to our buttons. Navigate

back to home view.

In our play text after frame, hit enter.

Type dot foreground.

Press enter on foreground style.

Use dot button text for the argument.

This modifier defines the text color. A

quick reminder, button text is black for light mode and white for dark mode. We

can test this by selecting canvas device settings.

Toggle color scheme dark appearance.

The plate text changes to white.

I'll toggle back to light mode for now.

Let's add our border color. In the

stroke modifier before line width, add a comma.

Left arrow key once.

Dot button border.

I'd say the play button looks much better. We can't leave the download

better. We can't leave the download button hanging. It would be easy to copy

button hanging. It would be easy to copy and paste the modifiers to the download button. However, this brings us to our

button. However, this brings us to our next coding principle. The famous don't repeat yourself or dry idea. Dry is all

about promoting efficiency and reducing redundancy.

If we the developers are going to use code more than once, we should only write it once. Of course, as with any principle, try to follow the best you

can. Don't lose any sleep if you can't

can. Don't lose any sleep if you can't use it 100% of the time.

This button style is used a few times in the project. Hence, let's write the code

the project. Hence, let's write the code once and use it more. Notice the

modifiers are on text, not the button itself. Therefore, we can create a

itself. Therefore, we can create a function that automatically applies these modifiers to a text. Highlight the

modifiers.

Copy with command C.

Navigate to constants at the top. import swift UI.

This import is needed because we'll be calling Swift UI elements. Make some

space after the closing braces.

Type extension text with curly braces.

Extension allows us to add functionality to an already existing type without modifying the original. Inside the

braces, type funk ghost button parenthesis

return arrow some view with braces.

This creates a function called ghost button that returns a view. Inside the

braces, type self.

Self refers to the current instance where the function is located.

The function is located inside an extension of text. Therefore, self

refers to text. Hit enter after self and paste the modifiers.

Usually, when a function returns a value, you have to type return at the end. However, because self is the only

end. However, because self is the only thing written and matches the return type, it automatically returns after applying the modifiers.

Steer back to home view.

delete all the modifiers in our play text.

Instead, type dot ghost button.

Go down to our closing download text parenthesis.

Enter dot ghost button.

Our custom function ghost button applies all the modifiers we defined.

If you ever need to see the source code of a function, hold command and click on it. very helpful for looking under the

it. very helpful for looking under the hood. Back to home view. The code looks

hood. Back to home view. The code looks pretty and most importantly, it was easy to use. This is because we took the time

to use. This is because we took the time to make it more efficient. Go ahead and click on the buttons for a quick test.

Notice the whole button clicks when tapped.

Switching to dark mode. It looks

awesome.

This is a great place to stop and take a break. Outstanding job again. I can't

break. Outstanding job again. I can't

wait to see you in the next one.

The home screen is really coming together. We have a hero title and two

together. We have a hero title and two buttons underneath. Our next step is to

buttons underneath. Our next step is to make the horizontal list under the buttons. There'll be four lists total.

buttons. There'll be four lists total.

These lists will be trending movies trending TV shows, top rated movies, and top rated TV shows. Keeping the dry

method in mind, we'll create one file and reuse it. First thing we need is more test images.

Navigate to constants.

Make some space after test title URL.

I'll make sure to include these strings in a description. Please copy and paste them.

Static let test title URL 2 equals paste static lat test title URL 3 equals

I'll grab the string paste.

While we're here, let's also make a trending movie string. Press enter after play string

static. Let trending movie string equals

static. Let trending movie string equals trending movies.

Great work. Use command N to make a new file. Swift UI view. Next.

file. Swift UI view. Next.

Horizontal.

List view. Create.

Let it load.

Press enter twice after the first brace.

Up arrow once.

Let header equals constants dot trending movie string

bar titles equals bracket constants dot test title URL comma constants dot test

title URL 2 comma constants dot test title URL 3. We define two variables here. Header is our trending movie

here. Header is our trending movie string. Let was used because it won't

string. Let was used because it won't change. Titles is an array of strings

change. Titles is an array of strings currently holding our three test titles.

Bar was used because the list might change when the data refreshes.

Delete the hello world text.

Instead, type BS stack down arrow to the one that has alignment and more options. Right arrow.

Select the one with alignment and content.

For alignment, put dole leading tab. Hit

enter to get braces.

This vstack will align all its views to the left of the phone inside the braces text header.

Add the modifier font.title.

This makes the text bigger. Under the

text, type scroll view with parentheses and curly braces.

Scroll view allows us to scroll through views that exceed the screen capacity.

Inside the parenthesis, put dot horizontal.

This allows our views to scroll horizontally.

In the braces, type lazy hstack with braces.

Lazy htack only loads the views when needed. inside lazy hstack for each

needed. inside lazy hstack for each titles open brace title in enter three times

for each is a handy tool that loops through a list. It also gives us access to each element. We're getting an error because each element needs to have an

ID. Since each string is different, we

ID. Since each string is different, we can use the string itself as an ID.

after titles comma id colon backslash self.

We can now access each element in our titles array. Each element is

titles array. Each element is represented by the title variable after the braces. Inside for each, we'll use

the braces. Inside for each, we'll use async image.

Select the one with the URL URL string title.

That loads our images, but we desperately need some modifiers.

Open brace image in enter three times.

After the brace, type place tab progress view in the placeholder. Back inside

async image image entertizable.caleto

image entertizable.caleto fit.

We've seen these modifiers in our home view. The difference is we're going to

view. The difference is we're going to add a frame to better contain it. After

our placeholder frame, right arrow, select the one with width and height.

Do 120 for width and 200 for height.

All right, we're almost there. A frame

is needed on the parent vstack itself.

There are quite a few closing braces but if you double click on the leading BS stack brace, Xcode will show you where the closing brace is. After the

closing brace enter dot frame. Right arrow only height this time. Do 250.

this time. Do 250.

This constricts the view to have a max height of 250.

We're pretty close to the edge. To fix

this, we can add a padding modifier.

This creates space. The default is 16 points. Put 10 in the parenthesis for

points. Put 10 in the parenthesis for less space. Wow, we did a lot in this

less space. Wow, we did a lot in this video. Great work as usual. Let's take a

video. Great work as usual. Let's take a break and I can't wait to see you in the next one.

We're almost done with the horizontal list view. Before we use it in our home

list view. Before we use it in our home view, just a few final details.

To begin, after the image scale to fit modifier, add the clip shape modifier.

Inside the parenthesis, type rounded rectangle.

Select the one with corner radius.

Use 10.

This modifier gives our images nice rounded corners. Slightly more modern

rounded corners. Slightly more modern looking in my opinion.

Next, I would like header to be dynamic.

If we use the view now, header can only be trending movies. This was great for testing. However, our view is finished

testing. However, our view is finished and ready to be versatile.

To do that, delete everything after header.

Instead, type colon space string.

This allows us to define a string for header when calling the view. Very

helpful since we'll have four different types of lists.

There's an error in the file because our preview needs an argument for header.

Scroll down. Click on the error. Select

fix.

Click on the string. Type constants.

Trending movie string.

This wraps up our horizontal list view.

Before we put it in our home screen navigate to constants. Make some space after trending movie string.

Let's define the rest of our headers.

Static L trending TV string equals trending TV.

Static L top rated movie string equals top rated movies.

Static L top rated TV string equals top rated TV.

Steer to home view.

We want these titles after our buttons.

Double click on the starting hstack brace to find the closing one. Make some

space after the closing brace.

Horizontal list view with header constants. trending movie string.

constants. trending movie string.

Copy that line.

Paste three times.

Change the second one to constants.trending

constants.trending TV string.

The third one to constants.toprated

movie string. And the last one a constants.toprated

constants.toprated TV string.

The horizontal list are displaying correctly with the right headers.

However, our home view doesn't look the best. This is because the number of

best. This is because the number of views exceed the phone screen. To fix

this, we can add a scroll view to our parent view. This can easily be done by

parent view. This can easily be done by scrolling up, right clicking on our vstack, select embedded

scroll view.

Much better. While we're here, we should change the vstack to lazy vstack.

Lazy vstack is best here as the views will load when they are needed. The home

view layout is really close to being done. To finish up, I want to improve

done. To finish up, I want to improve the hero title slightly.

If we click on an empty line and hit space on our keyboard this makes the view reload.

Watching the hero title, it kind of just pops up. A frame modifier will fix this.

pops up. A frame modifier will fix this.

We're going to need the size of the screen to pull this off. This can be done by using geometry reader. Right

click on scroll view embed geometry reader. After the braces

geometry reader. After the braces go n. Navigate to the placeholder

go n. Navigate to the placeholder closing brace.

Entertrame.

Right arrow. Select the one with width and height.

For width, type go.

D.width.

For height, do go.

This makes the frame the size of the screen. We can make this better by

screen. We can make this better by adjusting the height. Type times

0.85.

To summarize, we embedded scroll view into geometry reader. Geometry reader

got the available screen space minus the safe space. We put the frame of our hero

safe space. We put the frame of our hero title to the full width of our available space. However, we only want the height

space. However, we only want the height to be 85% of the available space. Now

if we test the hero title doesn't jump on screen.

As mentioned before, home view is close to being finished. Just a few more final touches, but we'll do them in the next video. Thank you for joining us. Can't

video. Thank you for joining us. Can't

wait to see you in the next one.

We have all the elements on our home view. To finish, I'd like to add a

view. To finish, I'd like to add a gradient between the hero image and space after it. The first step is to

define a new color. Navigate to assets right click, new color set, gradients.

In light mode, the gradient should be white to match the space after our hero title. Thus, in dark mode, the gradient

title. Thus, in dark mode, the gradient should be black. Select dark.

Open the inspector.

Color set.

Show color panel.

Six zeros. 1 2 3 4 5 6. Enter. Close the

color panel. Close inspector.

Go back to home view.

Hit enter after the scale to fit modifier dot overlay with braces. The overlay

modifier will put a view in front of our hero image. Inside the overlay braces

hero image. Inside the overlay braces type linear gradient.

Select the one with stops, start point and end point. We'll space this out a bit as it can get confusing. Right

before the S and stops, press enter.

Press enter again before the S and start point.

Finally, enter before the E and end point.

Double click on gradients stop.

Left arrow key once.

Add an open parenthesis.

Enter on the suggestion color location.

For color, usecle tab 0.8 for location. The editor is recommending adding another gradient.

Stop, which is what we need. Press tab

to complete.

The second color should be dot gradient.

Location should be one. Press enter

before the second G in gradient to space it out better.

for start point top tab endpoint will be dotbottom.

Let's break this code down. Linear

gradient creates a smooth transition between colors along a straight line. In

this case, it transitions from clear to our custom gradient color. Linear

gradient has three arguments. First is

an array of gradients stops. This

defines the colors and their locations.

Start point and end point define the direction of the gradient.

In this case, it starts at the top and ends at the bottom of the async image.

Picture 0% means the top and 100% the bottom. From 0 to 80, the gradient is

bottom. From 0 to 80, the gradient is clear or fully transparent.

From 80 to 100, the gradient transitions to our custom gradient color, white or black, depending on the mode. We can see in our preview, the gradient really

makes the design pop. I'll switch to dark mode.

It looks just as awesome. This wraps up home view, but we have one more task left. We need to run this code to test.

left. We need to run this code to test.

In order to run, home view needs to be called from content view. Open content

view.

Replace constants home string with home view.

At the top toolbar, drop down your target devices.

I'm going with the iPhone 16.

Press command R to run.

If everything works, you should have our home view displaying on your emulator.

We can scroll down to see all of our titles. Testing the horizontal list.

titles. Testing the horizontal list.

They seem to work. A quick note, our play and download buttons work but don't click smoothly.

You have to long click to get the animations.

This is because the buttons are embedded in the scroll view. The scroll view swipe gesture takes priority over the button animations. But don't worry, the

button animations. But don't worry, the buttons still work 100% of the time.

This truly wraps up section one. We

learned a lot in these nine videos. I

want to say thanks for supporting this course so far. My team and I really appreciate it. Go ahead and take a break

appreciate it. Go ahead and take a break if you need it. You deserve it. Give

yourself a pat on the back and I'll see you in section two.

At some point in your iOS journey you'll work with APIs or external data sets. Most of these rely on JSON objects

sets. Most of these rely on JSON objects to store and transfer data.

Understanding how to model these objects in your Swifti projects is critical. By

doing this correctly, you'll be able to use and display the data in your apps.

This video marks the official start of section two in our Swift UI beginner course. The main goal of this section is

course. The main goal of this section is to fetch and display life data from the movie database API.

We'll complete the first step in this video, modeling the JSON objects in our project. If you're just joining us, feel

project. If you're just joining us, feel free to grab the section one code from our GitHub. The link will be in the

our GitHub. The link will be in the comments. I do recommend having some

comments. I do recommend having some experience with Swift UI before starting this section. Section one of this free

this section. Section one of this free course is a great place to start.

Let's take a look at our API data. This

set is for training movies. The data

contains an object as shown by the top left curly brace. This object has an end called page and an array named results.

The results array contains the data that we're interested in. From here, we can pick and choose what is needed for our

app. For Blossom movie, we need ID

app. For Blossom movie, we need ID title, overview, and poster path. Just a

heads up, I tried original title at first, but got titles in their original language. Spanish and Japanese included.

language. Spanish and Japanese included.

Now that we know what data is needed it's possible to build a model. I'll

navigate back to Xcode.

Command N to create a new file.

Swift file. Next.

Title. Create.

Make some space after import foundation strruct title with braces.

Struct is short for structure in Swift UI. It's a lightweight and efficient way

UI. It's a lightweight and efficient way to group related data together.

Type, bar, id, colon, int, enter, bar v, bar title colon string enter bar var overview

colon string enter and bar poster path colon string.

We model title based on what we need from the results array. Before moving

on, please make sure your spelling matches with mine. Misspelling is one of the most common mistakes when working with APIs.

I want to show you one more thing with our data. This next set contains trying

our data. This next set contains trying TV. Notice title has been replaced with

TV. Notice title has been replaced with name. ID, overview, and poster path are

name. ID, overview, and poster path are still here, but it's important to see the slight difference. Keeping this in mind, I'll go back to Xcode. Hit enter

after title bar name colon string. This will allow our title strct to also work with the TV data.

However, what happens if our app tries to find a title string for TV data? As

we saw, title is replaced with name in that set. The app will unfortunately

that set. The app will unfortunately crash because it's expecting a value for title. To fix this, we can mark name and

title. To fix this, we can mark name and title optionals by putting a question mark at the end.

This tells our strct that title and name may or may not have a value. If they

don't have a value, instead of crashing the app will assign nil to them.

Nil means no value in Swift.

Because this isn't data that we manage it's a good idea to mark all the properties as optionals just in case.

The properties we need from the results array are modeled well here. Just a few more things. After title, type colon

more things. After title, type colon decodable, comma identifiable.

This makes the struck conform to the decodable and identifiable protocols.

Decodable allows title to be created from an external data source. Swift will

automatically map the data to the strs properties if the names match.

Identifiable requires this strct to have an ID property that uniquely identifies each instance. This will be useful when

each instance. This will be useful when we use title in our Swift UI views.

Our title strct is complete in the API data. Title was under an array called

data. Title was under an array called results. We need to do the same thing

results. We need to do the same thing with our title model. Make some space before strruct strruct

api object colon decodable with braces bar results colon bracket title

bracket equals brackets.

Instead of listing results as an optional, we set it as an empty array.

That way if the database returns null there's already an empty array there.

I'll bring up the working data one more time and put it side by side with our model.

Everything is matching up. We start with an array called results. This array

contains a strct title which has the properties we need. Good work on this one. This is a really important concept.

one. This is a really important concept.

It goes beyond iOS if you ever want to get into any other coding space. Great

job again. I can't wait to see you in the next one. With our title model completed, we're ready to grab our API key. This key allows us to request and

key. This key allows us to request and fetch data from the movie database API after which we'll be able to display the data in our app. A quick word of

caution, safeguarding API keys is a broad topic that exceeds the scope of this course. While hard- coding keys

this course. While hard- coding keys directly into your code isn't recommended, we'll take a simple approach for this practice app by storing them in a JSON file. This method

does keep the key out of the source code, but it's not a production level solution. However, it's a good step

solution. However, it's a good step towards better practices. In addition

it'll also teach us concepts like the singleton design pattern and error handling. The first step is to get our

handling. The first step is to get our credentials. Open a new Chrome window.

credentials. Open a new Chrome window.

Inside the search bar, type the movie database.

The first link will do.

Click on login.

Please create an account if you need to.

I already have mine, so I'll just log in.

Select user profile settings API.

This is where your API key will be. If

you just made an account, you'll need to generate a key. It's a pretty straightforward process and totally free. Once you have your key, keep it

free. Once you have your key, keep it handy. We'll need it here shortly.

handy. We'll need it here shortly.

Navigate back to Xcode.

Command N for a new file. Scroll all the way down. Empty. Next.

way down. Empty. Next.

API config.json.

Create.

Please make sure the file is inside your project directory. This file will hold

project directory. This file will hold sensitive information like our API keys and base URLs. In turn, this sensitive information will be out of the app

source code. Inside the JSON file, start

source code. Inside the JSON file, start with an open curly brace and hit enter.

This creates an object. Type quotes TMDB base URL quotes.

I'll paste the base URL here. It'll be

in the comments. Please copy and paste it as well. close quotes, comma, enter quotes tmdb

API key quotes, colon quotes. Paste your

own API key here as a placeholder. I'll

type in your key.

Before we move on, please ensure your spelling lines up with mine. Caps as

well. Our API compact.json file is ready to go. It just contains two strings, the

to go. It just contains two strings, the base URL and our API key. Hit command N.

Swift file this time. Next

API config create.

API config.sswift will be used for reading and accessing the contents of the JSON file. This allows us to dynamically load sensitive information like our API keys into our app at

runtime. Make some space after import

runtime. Make some space after import foundation strct API config colon decodable

with braces. We have to include

with braces. We have to include decodable because API config will be created by an external source ourjson

file. Let tmdb

file. Let tmdb URL colon string enter. Let tmdb API key colon string.

These are the variables in our do.json

file. They have to match exactly. Hit

enter a couple times. Type static let shared colon API config equals braces at the end of the braces parenthesis.

We're using something called a singleton pattern here. This is a design pattern

pattern here. This is a design pattern that ensures only one instance of API config is created. We're naming that one instance shared. Singletons are good for

instance shared. Singletons are good for centralized resource management and usage. We know our logic goes inside the

usage. We know our logic goes inside the braces. The parenthesis at the end are

braces. The parenthesis at the end are new. They immediately execute the

new. They immediately execute the closure at runtime. In turn, our credentials are available right away.

Inside the braces, type guard let URL equals bundle main URL.

Select four resources with extension in four resources quotes API config tab quotes JSON

else braces fatal error API config.json

API config.json is missing or invalid.

We're trying to find the API config.json

file in apps bundle. If the file isn't found, the uslocks executes and fatal error will crash the app. This ensures

the app can't proceed without the JSON file. Make room after the closing brace.

file. Make room after the closing brace.

Now that we found our file, we can get the data out of it. Type do with braces.

After the closing brace, type catch with braces.

The do catch block is used to execute code that might throw an error. This

allows us to handle that error gracefully if thrown.

Inside the do block let data equals try data contents of URL return try JSON decode API config self

from data inside catch fatal error failed to load or decode

API config.json JSON

config.json JSON back slash error autocomplete was pretty good here. Let's break this code down.

good here. Let's break this code down.

Load the contents of the API comp.json

file from the URL we made above. Try as

needed here because data contents of can throw an error. After that, try a decode and return the data as an API config.

This is done using a JSON decoder. The

catch block only runs if an error occurs in the do block. Again, fatal error is used because that app can't run without this information. To finish up, we need

this information. To finish up, we need to run the app to make sure this works.

Navigate to content view.

Double click on the tab view starting brace.

Click on the closing brace. Enter

onappear with braces.

print API config.shared

tmdb URL below that print API config.shared

API config.shared tmdb api key. Go ahead and run this code with command r.

If everything works, you should have the base URL and API key showing in your log. If your app crashed, please double

log. If your app crashed, please double check all the files we created.

Misspelling and capitalization are the common mistakes.

Right now, the app crashes if there's an issue with the.json file. In the next video, we'll explore a more userfriendly way to handle this error. Thank you for spending time with us today. We truly

appreciate your support. Can't wait to see you in the next one. Handling errors

is a key part of your iOS development journey. Whether you're connecting to a

journey. Whether you're connecting to a backend or loading a JSON file, errors will definitely pop up. In this video we'll refactor our code to gracefully

handle errors. Currently, our file uses

handle errors. Currently, our file uses fatal error. This was good to help us

fatal error. This was good to help us understand how to load our API credentials, but it's not a long-term solution. Let's dive in and learn how to

solution. Let's dive in and learn how to handle errors better. We'll start by creating a new file to keep all our errors organized. Hit command N on your

errors organized. Hit command N on your keyboard. Swift file. Next. Errors.

keyboard. Swift file. Next. Errors.

Create.

Make some space after import foundation.

Enum API config error colon error localized error with braces.

This is the first time we've seen an enum. Enums are used for grouping

enum. Enums are used for grouping related values together in a way that's easy to manage and use. Think of them like a list of options or categories you

can choose from. In this enum, we'll list all the possible errors related to our API config.

We've also made our enum conform to the error and localized error protocols.

Error allows the enum to be used for throwing errors in functions. Localized

error enables us to provide more user-friendly messages or logs. This

makes it easier to debug issues. These

two protocols have part enum handle and describe errors in a clean way. Inside

the braces type case file not found enter case data loading failed

parentheses under lying error colon error right arrow key enter case

decoding failed underlaying error error.

These are the three issues that can cause errors in our API config file. the

file isn't found, the data doesn't load or there is a decoding issue the data loading failed, and decoding failed cases both include an underlying

error. This is because these errors can

error. This is because these errors can occur for multiple reasons. For example

data loading failed might happen because the file is corrupted or maybe the app doesn't have the right permissions to read the file. By including the underlying error, we capture the exact

system error and use it for debugging.

To finish the file, we can include user-friendly error descriptions. Hit

enter a couple times after the last case bar. Error description. Hit tap to

bar. Error description. Hit tap to autocomplete.

If we pause for a second, the editor will recommend finishing the property for us. Hit tap to autocomplete again.

for us. Hit tap to autocomplete again.

The editor did a pretty good job completing this property. Please make

sure your file looks like mine.

The error description string gives a better error description for each of our cases. When we get a data loading failed

cases. When we get a data loading failed or decoding field case, we include the errors localized description to see what went wrong. A quick note, the error

went wrong. A quick note, the error description string is an optional because it conforms to the localized error, which is also an optional. Our

API config error enum is complete and now it's time to put it to work.

Navigate back to API config.sswift.

To start, let's organize the loading and decoding logic by placing it in its own function. After the parenthesis, make

function. After the parenthesis, make some space.

Private static funk load config parenthesis throws

return arrow API config with braces.

A quick recap on functions. The private

keyword restricts this functions access to only within the API config file. This

ensures the logic for loading can't be called directly from outside. Our

singleton shared is static which means it could be called anywhere in the project. Therefore, the function must

project. Therefore, the function must also be static since it'll be going inside the singleton. Load config is the name of the function. Throws allows a function to throw errors if something

goes wrong, such as a missing file.

Finally, the return arrow indicates the function returns an instance of API config once everything is loaded and decoded correctly. Next, let's move the

decoded correctly. Next, let's move the loading and decoling logic into our function. Start from the last catch

function. Start from the last catch brace and highlight everything up until the guard statement. Command X to cut.

Click inside our function and press command V to paste.

Replace the first fatal error with throw API config error file not found.

In the catch statement, add let error as decoding error.

Replace the second fatal error with throw API config error dot decoding failed

underlying error error.

We specify the error as decoding error to know exactly what went wrong with the decoding process. Decoding errors are

decoding process. Decoding errors are really specific and this helps us better debug if needed.

Add another catch statement with braces.

Inside type throw API config error dot data loading failed underlying error error.

With this we successfully integrate our enum to handle potential issues gracefully. Instead of crashing the app

gracefully. Instead of crashing the app with fatal errors, we now catch and safely return descriptive errors.

To finish this file, update our share singleton to be an optional.

Inside the braces, write a do catch block.

Inside do, return try load config. Try is needed because load config can throw errors.

In the catch block print failed to load API config error.localize

description.

After the message, return nil.

This is why we made our share singleton optional. If there's an error during

optional. If there's an error during setup, share will be nil, allowing the app to handle the situation without crashing.

All that's left is to test. Go to our content view.

Delete the old print statements.

Type if config equals API config.shared

with braces.

Print config.

API key. Print

config.

base URL because API config.shared is an optional we have to unwrap it to use it.

Unwrapping means we check if it's empty or nil. The if let statement checks if

or nil. The if let statement checks if it's nil and if it isn't we print the properties. Command R to run.

properties. Command R to run.

If you're showing your API key with the base URL, everything went fine. But very

quickly, let's simulate an error. Go to

API config and in the width extension argument, add an extra N.

Command R again to run.

This time we're showing the error we defined. The APA config file can't be

defined. The APA config file can't be found because the extension is mistyped.

Most importantly, our app didn't crash.

Very good. Please delete the extra N so everything works as expected.

That's it for this video. Thank you all so much. Can't wait to see you in the

so much. Can't wait to see you in the next one.

In the last lesson, we focused on handling errors gracefully to make our app more reliable. With that foundation in place, the next step is to start building functions that will decode our

server data.

To begin, head over to content view and delete the onappear block.

This was a placeholder for testing and no longer needed. Next, navigate to our errors file.

Here, we'll create a new enum to manage and categorize any network related errors. Make some space after the

errors. Make some space after the closing brace. enum network error colon

closing brace. enum network error colon error comma localized error brace inside

the braces case bad URL response parenthesis underlaying

error colon error enter case missing config enter twice

bar error description if your suggestion from the editor matches is mine. Press

tab to autocomplete.

Once again, the editors autocomplete gives us the correct structure. Press

tab to save some time.

This is similar to our API config error above. The case bad URL response will

above. The case bad URL response will trigger if the application encounters an issue with the server response. Some

examples include a wrong website address, a server error, or a response that doesn't match the expected format.

case missing config triggers if something goes wrong with the configuration of our API config file.

We'll need this when we unwrap our API key and base URL. By handling these scenarios, we ensure our app can gracefully manage unexpected issues

during network request.

With our enum ready, we can make a new file to hold our network functions.

Press command N. Swift file. Next. Data

fetcher.

Create.

Our file is ready. Hit enter once after import foundation.

Let tmdb base URL equals API config.shared.

config.shared.

TMDB base URL. Enter. Let TMDB API key equals API config.shared.

TMDB API key. We define the variables globally so that all of our functions can access them. Since the singleton API configurer is optional, these variables

are optionals as well. This ensures that if there's an issue decoding the API config file, our app won't crash but handle the missing configurations.

Hit enter a couple times after API key.

Bunk fetch titles parenthesis 4 media colon string close parenthesis async throws

return arrow bracket title bracket braces.

Let's break down this function. We know

that fetch titles is the name. It takes

one parameter a string called media. The

four in front of media is called an argument label. Argument labels make the

argument label. Argument labels make the function call more descriptive and easier to understand. We used four here but you can use any word that fits your

use case. Async throws means this

use case. Async throws means this function will run on a different thread than the main thread and might throw an error. This is important because as a

error. This is important because as a network call, we don't want it running on the main thread with the UI. Doing so

can lead to performance drops and a less responsive app.

Finally, the return arrow indicates that this function will return an array of our title model. The purpose of this function is to connect to the URL and

attempt to fetch the titles inside.

The first step in this function is to build the URL where our data is located.

To give you a better idea, I'll paste an example of what the URL should look like for trending movies.

It starts with our base URL followed by several directories and ends with a query that includes your API key.

To continue, we need to unwrap our TMDB base URL and TMDB API key inside the

function guard. Let base URL equals TMDB

function guard. Let base URL equals TMDB base URL else with braces inside the

braces throw network error dot missing config.

below that guard let API key equals TMDB API key else with braces same thing

throw network error dom missing config these checks ensure that TMDB base URL and TMDB API key are not nil if they're

available they'll be assigned a base URL and API key respectively however if either of them are nil the function will stop and throw the

network error.m missing config case.

network error.m missing config case.

This will signal an issue with the configuration.

With our base URL and API key values unwrapped, we can build the URL. Make

some space. Let fetch titles URL equals URL parenthesy string colon base URL

question mark enter dot appending parenthesis path quotes 3 slash trending

backslash parenthesis media parenthesis slash slash day

enter dot appending parenthesis query items

bracket enter URL query item name parenthesy API key tab API key

this builds the exact URL we have at the top it adds a path including the media string and the parameters. Then it

appends the query items including our API key as an argument. Query items are pairs of information added to the URL to send data to the server. Quick note, we

use a question mark after the URL because the URL string initializer returns an optional. This means that the string is invalid. The code will safely

return nil instead of crashing.

At this point, our URL is complete and ready to use. You've done an excellent job laying the groundwork for fetching data. In the next video, we'll build on

data. In the next video, we'll build on this and complete the function. As

always, great work. In the last lesson we started building our fetch titles function and created the URL containing training movies. In this lesson, we'll

training movies. In this lesson, we'll complete the function by handling the API response and parsing the JSON data.

First, let's fix a sneaky mistake in our URL. We forgot a forward slash after

URL. We forgot a forward slash after trending.

Without the slash, trending movie would be combined and the URL won't work. To

help us debug when we run the code, add print fetch titles URL.

This will print the URL in our console and allow us to inspect it. We do get a warning because fetch title URL is an optional to safely unwrap it and handle

potential failures. We need to update

potential failures. We need to update our network errors enum. Open the errors file

inside network error under missing config case URL build failed.

After adding your up build failed, we'll see an error because any switch statement handling network error now requires this new case. Click on the

error fix.

The code for our URL build failed can simply return failed to build URL.

With our new case added, we can go back to data fetcher.

Add guard in front of lat to safely unwrap our URL.

At the end of the parenthesis else with braces inside the braces, type throw network

error dot URL build failed.

This ensures that if our optional URL is nil, we throw a new case, preventing potential issues down the line. We can

finally start parsing this response.

Make some space after our print statement.

Let parenthesy data comma URL response parenthesis equals try awaits URL session.shared

session.shared data from batch titles URL. Let's break

this code down. This function returns two values. Data, which is the actual

two values. Data, which is the actual raw JSON data from the API, and URL response, a URL response object that

contains metadata about the request.

This could include an HTTP status code which tells us whether the request was successful or not. These values are assigned to the variable stata and your

our response respectively. Try is needed because this function can throw an error. For example, if the network is

error. For example, if the network is down, await means this is an asynchronous operation. So the function

asynchronous operation. So the function completes in a separate thread without blocking the app. URL session is a built-in class in Swift that handles

network request. Dot shared is a

network request. Dot shared is a singleton instance of URL session meaning we don't have to create a new

session manually. Finally, data from

session manually. Finally, data from fetch titles URL makes a network request to the URL we built earlier. It

downloads the data from the API and also gets a response from the server. Next

we need to check if the URL response is valid. I'll scroll down and hit enter a

valid. I'll scroll down and hit enter a couple times. Guard let response equals

couple times. Guard let response equals URL response as question mark http URL

response comma response status code double equals 200 else braces. We first

check if the URL response above is a valid HTTP response. If it is, the HTTP URL response is assigned to response.

Then we check whether our response variable has a status code of 200. A 200

status code means that everything went well and the request was successful. If

either of these checks fail, we can throw network error bad our response underlying error any error. Since we

don't have an existing error to pass, we need to create our own using NS error.

NS error stands for nextstep error. an

Objective C class used to represent errors in Apple's framework. Select the

NS error that includes domain code and user info. We can put domain

user info. We can put domain code and user info on their own line to avoid clutter.

Domain represents where the error is coming from. In this case, parenthesis

coming from. In this case, parenthesis data fetcher works because the error comes from this function. Code is a status code from the

function. Code is a status code from the URL response. We can attempt to get this

URL response. We can attempt to get this code using parenthesis URL response as question mark http URL response

parenthesis status code question mark question mark negative one. If a status code exists

negative one. If a status code exists we use it. If there is no status code found, we default it to negative one indicating an unknown issue.

User info is a dictionary containing additional error details such as a descriptive error message. We can use

brackets ns localized description key colon quotes invalid HTTP response.

This ensures that if the response is invalid, we throw a readable error that helps identify the issue. I'll scroll

down for more room. We check to make sure the response was valid. Now we can define our decoder. Click outside the guard statement. Let decoder equals JSON

guard statement. Let decoder equals JSON decoder.

A quick note, our variable poster path follows camel case, meaning the second word starts with a capital letter. In

the API, they use snake case, meaning words are separated by an underscore like poster path. To account for this

difference, we can add decoder key decoding strategy equals convert from snake case. This tells the decoder

to automatically convert snake case keys from the API to camelc case. Finally, we

can complete the parsing. Type return

try decoder decode API object self tab from data parenthesis results escape.

Since decoding can fail, we need to use try in case an error is thrown. The

decode function takes an object type and tries to convert the raw URL data into that object. In this case, we're

that object. In this case, we're decoding the data as our API object strct. Remember that API object contains

strct. Remember that API object contains an array called results. This results

array is made up of multiple tile strrus where each str represents an individual movie or show with its properties. By

returning results, we get a fully decoded array of titles that we can now use in our app. I know this was a lot to take in and some parts might have been a

bit confusing. Feel free to rewind and

bit confusing. Feel free to rewind and revisit any explanation if you need to.

Also, if you have any questions, don't hesitate to ask. I'll do my best to help. You're doing an amazing job, and

help. You're doing an amazing job, and the fact that you made it this far speaks volume about your dedication.

That's it for this lesson. Thank you so much. I can't wait to see you in the

much. I can't wait to see you in the next one.

View models are a key concept not just in Swift UI but across many frameworks.

Their job is to handle data while the UI focuses on managing the interface.

Separating these responsibilities is a common practice and an essential skill for any developer. In this video, we'll demonstrate how to build a view model.

The first step, of course, is to create a new file. Make sure to select the Blossom movie folder. Press command N.

Swift file. Next. View model. Create.

Hit enter for space.

It's important that our view model instantly updates the UI whenever the data changes. Therefore, we need to

data changes. Therefore, we need to include the at observable macro.

At observable allows this view model to automatically notify the UI of any data changes. We typically can't use a struck

changes. We typically can't use a struck with at observable because strrus are value types. This means a new copy is

value types. This means a new copy is created whenever they change. Instead

we can define our view model as a class.

Now we can share and modify data without creating new copies. Inside our view model class, we'll define an enum to represent the different stages of the

fetching process. Enum fetch status with

fetching process. Enum fetch status with braces case not started case fetching

case success case failed parenthesis underlaying error colon error.

Before making a request, we start with case not started where the UI remains empty. Once fetching begins, the state

empty. Once fetching begins, the state switches to fetching displaying a progress view. If fetching succeeds, we

progress view. If fetching succeeds, we show the normal UI. However, if an error occurs, we'll display a view with the

error message.

With our enum created, we can now use it in the view model. down arrow after the enum closing brace and hit enter.

Private parenthesy set parenthesy bar home status colon fetch status equals dotn not started

private set ensures that only the view model can update home status. Other

parts of the app can only read its value.

This is important because only view model will know the value for home status based on the response.

We initialize it with dot not started because no request has been made yet.

The next step is to define our data fetcher. Before that we need to navigate

fetcher. Before that we need to navigate back to the data fetcher file and convert it into a strruct.

At the top after import foundation hit enter strct data fetcher open brace. Scroll to the

bottom and add a closing curly brace to complete the strct.

This was something I forgot to do in the earlier video and I apologize for that.

The good news, you get to see how we fix mistakes in real time.

With data fetcher fixed, we can return to our view model file.

Press enter for a new line.

private let data fetcher equals data fetcher. A

quick note, private isn't required here but since no other file needs access to data fetcher, it's good practice to include it. That way, when we call

include it. That way, when we call strrus and other files, we're only presented with relevant options.

Next, we'll create an array to store training movie titles. New line bar trending movies

colon bracket title bracket equals brackets.

The trending movies array will hold a list of title strrus. We initialize it as empty to ensure our app doesn't break if no data is returned. With the

property set for now, we can move on to creating our functions. Make some space and add funk. get titles parenthesis async with braces.

Async is used because this function will make a network call. This allows it to run on a separate thread, preventing it from blocking the main UI. At the start

of the get titles function, we could type home status equals fetching.

When this function is first called, it will allow us to display a progress view to show loading.

Next, we can add a do catch block since the function we're about to call can throw errors. Inside do trending

movies equals try awaits data fetcher.betch titles for movie.

data fetcher.betch titles for movie.

Remember data fetcher.betch titles

returns an array of decoded title strrus. We pass a string argument movie

strrus. We pass a string argument movie to build a URL for training movies. This

is useful because in the future we'll need to fetch training TV shows the same way. If this completed successfully, we

way. If this completed successfully, we can say home status equals success.

This tells our UI to show the home view.

We just need to handle any errors. I'll

scroll down first and add print error.

This prints the error to log for debugging.

Now we set home status to failed passing error as underlying error.

This gives us an error message that we can have the UI show. This is a simple view model but we covered the core concepts of how it works. We just have a

few more steps before we can see it in action but we're almost there. Thank you

very much for spending some time with me today. I can't wait to see you in the

today. I can't wait to see you in the next one.

In parameters are useful when you need to modify a value inside a function and have that change persist. Normally

function parameters in Swift are constants, meaning they can't be changed. With an out, you can pass a

changed. With an out, you can pass a variable into a function and have it change directly. In this video, we're

change directly. In this video, we're going to apply this concept to complete our view model.

inside our view model. Right now, we're successfully getting the training movies. However, each image from the API

movies. However, each image from the API is only half complete. Let's jump into the constants file to see what's going on. The API only returns the last part

on. The API only returns the last part of the image URL.

To display the full image, we need to add the URL beginning part ourselves.

We're going to create this in function inside our constants file.

Let's first define the poster path beginning. I'll scroll down.

beginning. I'll scroll down.

I'll also make some room after test title URL 3.

Type static let poster URL start equals tab.

The editor might recommend it for you but if not, you can just copy and paste it from above.

We can delete the ending forward slash.

It's already included in the API response.

Right arrow, hit enter a couple times for space. Now we can define the

for space. Now we can define the function static funk add posture path parenthesis 2 titles

colon and out bracket title bracket parenthesy with braces.

This function is static because we need another part of the app to access it.

The function name is add poster path. We

use the argument label two making it clear that we are adding the poster path to the titles. The titles parameter is an array of our title strct. The nl

keyword signals that this array goes into the function gets modified and comes back out with the changes applied.

I'll scroll down a bit more. Inside the

add poster path function, type four index in titles indices with braces.

This loops through all the titles in the array and gives us the index of each one, allowing us to modify them directly.

Inside the for loop, add if flat path equals titles bracket index.polster path

with braces.

This checks if poster path is nil. If it

has a value, we assign it to the variable path and modify it inside the braces. Otherwise, we just leave it nil.

braces. Otherwise, we just leave it nil.

We can finally complete the modification.

Inside the ifat statement, enter titles bracket index.poster

bracket index.poster path equals constants.poster

URL start plus path.

This takes the existing poster path combines it with the base URL, and updates this value for each title in the array. Our function is complete. Let's

array. Our function is complete. Let's

use it in our data fetcher file.

Scroll down to our return statement.

Instead, type var titles equals.

This stores the titles in an array allowing us to modify it before returning the data.

Now we can apply our in function to complete the poster path for each title before returning the array. Make a new line and add constants

dot add poster path to titles.

Return titles. The and before titles tell Swift

titles. The and before titles tell Swift modify the actual array, not a copy.

This allows our function to apply changes directly. As we discussed

changes directly. As we discussed earlier, we're complete with the in function, but we have time for one more thing. Navigate to our title file.

thing. Navigate to our title file.

We're going to create an array of sample data. This sample data will help us when

data. This sample data will help us when we go back to the UI and work with our previews.

Make some space after var poster path.

Type static var preview titles equals brackets.

Inside the brackets, type title down arrow to our strct. Right arrow.

Select the one that has all the properties.

This will ensure our sample data matches the actual structure of our API response.

I'm going to space this out by pressing answer before title and after the closing parenthesy.

This makes the code easier to read.

Now copy this line.

At the end of the line, type comma enter, and command B to paste.

Do it one more time.

This gives us three tile strokes to work with.

We just need to fill in our data. It's

going to be pretty simple. Just follow

along. Click on the first ID one. Tab.

Beetlejuice.

Tab Beetlejuice tab a movie about Beetlejuice

tab constants dot test title URL I'll do the same for Pulp Fiction start at ID two

P fiction P fiction a movie

about old fiction constants test title URL 2.

Finally, we just need the Dark Knight three. The Dark Knight.

three. The Dark Knight.

The Dark Knight.

A movie about The Dark Knight.

Constants test title URL3.

That wraps up this video. We learned

about inout functions and even got our sample data ready. I know we stepped away from Swift UI for a bit, but in the next lesson, we're coming back with an

important topic. the infamous depending

important topic. the infamous depending on who you ask model view view model or MVVM pattern. Thanks for spending some

MVVM pattern. Thanks for spending some time with me today.

The model view view model pattern is a popular design choice for many reasons.

It helps keep your code organized by separating UI logic from business logic making it more maintainable and easier to test. Like most things in coding

to test. Like most things in coding it's a bit controversial as some developers argue it's unnecessary in Swift UI. In my experience, MVVM shines

Swift UI. In my experience, MVVM shines in complex projects, helping clean uploaded files and making code more structured and readable.

Even though Blossom Movie is small applying MVVM will give us experience so you can decide if it's the right pattern for your own projects. To recap, our

model is represented by the title file.

This file defines the API object and is used for decoding our API response.

There's also sample data to help us with our Swift UI views. While we're here, it doesn't hurt to fix a typo.

Moving on, we have the data fetcher file.

This file acts as a service that holds our network logic. It contains a batch titles function which connects to our API,

retrieves the data and decodes it into a title array. Next, we have our view

title array. Next, we have our view model.

It includes an enum to track the fetch status, initializes a data fetcher, and stores an array of training movies.

Inside the get titles function, it calls fetch titles to populate the trending movies array. Thanks to the at

movies array. Thanks to the at observable attribute, the view model automatically updates the UI whenever the data changes. To complete the MVBM

pattern in this project, we need to connect the view model to our UI. First

open the horizontal list view file.

Right now, it contains a static array of poster strings.

Remove everything to the right of bar titles.

replace it with colon bracket title.

This allows us to define a title array when calling this view making it dynamic.

We get a few errors. The first one being because we use self as the ID in our for each loop. Our title struck already

each loop. Our title struck already conforms to the identifiable protocol.

So we can remove ID do self.

To fix the next error, use the title poster path property for the async image URL.

Remember, all the title properties are optionals in case they're known in the API. Therefore, use a double question

API. Therefore, use a double question mark operator to provide an empty string if the value is missing. A default URL will be a better approach, but an empty

string works for now. We have one more error in this file. The preview requires an array of titles to compile. Scroll

down, click on the error, and select fix.

This adds the titles parameter, which we can set to title.review

titles to resolve the issue. Our sample

data coming in clutch. We're ready to go with this file. There are a few more errors to address. But before we fix them, open API config.json.

Replace your key with your actual API key from the movie database. I'll be

doing this off camera, but please make sure to do this as the code will work without it. If you need help getting

without it. If you need help getting your key, check out my API keys video for guidance. With that done, navigate

for guidance. With that done, navigate to home view. Under hero test title, add let

view model equals view model.

This creates a new view model that will control the UI. Press enter after the scroll view brace.

Type switch.

Pause for a second to see if Xcode auto completes it. If it does, press tab to

completes it. If it does, press tab to start the switch statement. Pause. Then

press tab again to autocomplete the rest.

It's always nice when Xcode helps us out. However, my switch statement isn't

out. However, my switch statement isn't quite right. I need to change the third

quite right. I need to change the third case to success.

For now, I'll leave an empty text in there.

Next I need to add the last case failed let error

inside this case I'll simply add text error back slash error to break it down we executed a switch

statement on the view modelhome status enum when cases do not started we We show an empty view with fetching a

progress view appears in success. We

currently have a text placeholder, but we'll add our UI here soon. Finally, in

failed, we display the error as a text on screen.

Let's scroll down to fix the error that's showing.

Click on the error and select fix.

This adds a missing titles array.

Inside that type view model trending movies select the other three horizontal list views and comment them out using command forward slash.

We pass the view model training movies to our horizontal list view which will display them. Although right now the

display them. Although right now the training movies are empty. Now scroll up and double click on the lazy vstack brace.

Reselect everything from the ending brace back to lazy vstack.

Press command X to cut.

Inside the success case select the text and paste with command V.

If the case is success, the UI will not be displayed.

The last step is to make sure the view model populates a training movies array.

Double click on scroll view's open brace.

On the closing brace add dot task braces. Inside task type await view

braces. Inside task type await view model.get titles.

model.get titles.

The task modifier runs an asynchronous task when the view appears allowing await view model.get titles to fetch data without blocking the UI. This is

the moment of truth. If your preview builds and you can scroll down to see the trending movies list, you're good.

Scroll to the right and take a moment to appreciate how fast and smooth it scrolls.

This whole section has been leading up to this moment. If you're not so lucky we'll troubleshoot. Now, press command R

we'll troubleshoot. Now, press command R to run the app.

I got the simulator up and the list is working great.

Open your log and the first thing to check is the API address. Copy and paste it to an empty browser window.

You should see data returned just like mine.

If you don't see data, check your API address to make sure it wasn't mistyped.

Also verify that your API key is valid.

You might also have configuration issues. Compare your code to the

issues. Compare your code to the completed version to find any mistakes.

Misspellings in the title strct or API config.json are also common issues. This

config.json are also common issues. This

wraps up the video. Great job making it this far. We've learned a lot in the

this far. We've learned a lot in the course so far and even implemented a clean MVVM design. I'll end it here, but I can't wait to see you in the next one.

If you're not careful, a function can quickly get out of control, especially when you start reusing it for different tasks. To help combat this, helper

tasks. To help combat this, helper functions in Swift can keep your code clean, organized, and easier to manage.

We'll cover an example of using a helper function in this video to show how it can help simplify your code.

Currently, we're fetching training movies from our API using the fetch titles function. Since training TV has

titles function. Since training TV has the same URL, the only change needed is swapping movie for TV. But we also need to fetch top rated movies and TV shows.

These calls have slightly different URLs, meaning modifying our fresh titles function.

Let's open data fetcher and take a look.

I'll paste the URL for top rated media right after the training URL.

The URLs are similar with the main difference being the order of media and type. For example, the first URL uses

type. For example, the first URL uses trending forward/mov while the second uses movie/top rated.

Additionally, trending includes day before the API query whereas the latter places top rated before the query.

Since only the URL changes, we can keep the same function, add a parameter, and update the logic to build the appropriate URL. We could add the logic

appropriate URL. We could add the logic directly inside this function, but it will get big fast. Looking ahead, we'll also need this function for our search

screen. Therefore, using a helper

screen. Therefore, using a helper function now is a better approach.

First, let's add the new parameter.

After four media string, put a comma add by type colon string.

This lets us specify the media as either movie or TV and choose between trending or top rated as a type. Now we can make

our helper function. Double click on the fetch titles closing brace.

Add a couple lines.

Type private funk build URL parenthesis media colon string comma

type colon string parenthesis throws return arrow URL question mark with braces.

We mark the function as throws allowing us to throw error enums when needed.

Inside the helper function, we need to unwrap base URL and API key. Lucky for

us, this is complete already. Scroll up.

Cut the first two lines from fetch titles.

Paste them inside the build URL function.

Make some space and add var path colon string.

This path will be used to construct the correct URL based on the type.

I'll scroll all the way down for more room.

Add a couple lines.

If type double equals parenthesis trending parenthesis braces

path equals parenthesis 3/ trending slash backslash parenthesis media

parenthesis slashday else if type double equals top underscore rated braces

path equals parenthesis 3/backslash parenthesis media parenthesis for slash top_rated

else parenthesis throw network URL build failed.

With the path correctly made, we just need to build the URL. We got lucky again and already completed this. Scroll

up to the fetch titles function and cut the guard let statement.

Paste it under the else statement and build URL.

Let's rename bash headles URL to just URL.

Also replace the hard-coded path and appending with our path variable below the guardlet statement

add return URL.

This completes the build URL function.

Let's put this new function to work by scrolling up one more time. Before the

print statement, type let fetch titles URL equals try

build URL media media type type.

We just need to unwrap since the build URL function returns an optional.

Guard let fresh titles URL equals fetch titles URL else braces throw network error dot URL build

failed.

I'll fix our spacing.

That completes our fetch titles function, allowing it to fetch both trending and toprated titles. Without

the build your all helper function, this logic would stay inside fetch titles making it messy and harder to maintain.

Please try to use helper functions whenever they make sense. To wrap this up, go back to view model.

Add the missing parameters for our fetch title call.

We can put parenthesis trending.

That's it for this video. Thank you for spending some time with me today. I'll

see you in the next one. When making

multiple async calls, using try wait for each one can slow things down. This is

because each call must finish before the next one starts. To improve speed, we can use async let allowing each call to run parallel instead.

In this video, we'll demonstrate how to implement async using blast movie as an example. Our view model currently only

example. Our view model currently only fetches training movies. We still need to get training TV shows, toprated movies, and toprated TV shows. The hard

part is already done since our fetch titles function is set up to handle different URLs. Now, we just need to put

different URLs. Now, we just need to put it to work. We can start by defining our new arrays. Make a line after training

new arrays. Make a line after training movies.

Type bar trending TV colon bracket title equals empty brackets enter bar top

rated movies colon bracket title equals empty brackets enter bar top rated TV colon bracket title equals empty

brackets like the training movies we create these list as empty in case the API returns them no next scroll Scroll down and press enter after the try await

closing parenthesis.

We'll call data fetcher to populate our new arrays. at trending TV equals try

new arrays. at trending TV equals try await data fetcher fetch titles for TV

by trending top rated movies equals try await

data fetcher fetch titles or movie by top_rated Top rated TV

equals tryate data fetcher fetch titles or TV by top_rated.

From here we can open home view. Go

down.

Uncomment the horizontal list views by selecting them and pressing command forward slash.

Errors appear because we need to pass an array for the different views.

Click on the first error and press fix.

Add view model dot trending TV.

Click on the next error and press fix.

Add view model dot top rated movies.

We just see the last error.

view model toprated TV.

Our preview updates and if we scroll down, the rest of our titles appear.

We've put in some work and our app is pretty well organized, making this addition seamless. Press command R to

addition seamless. Press command R to run the code.

Scrolling down to the right all the titles load pretty quickly.

I'm going to close the app a couple of times and reopen it to test loading speed.

Not going to lie, it's actually pretty fast.

Keep in mind this is a small app with only four network calls going so far. As

we add more to it, it might slow down with our current async setup. Let's see

if we can improve it by going back to view model.

I'll close the log.

Right now, we're using try wait for each call. This means training movies runs

call. This means training movies runs first. Training TV waits until it's done

first. Training TV waits until it's done to start and so on with the other calls.

To speed things up, we can use async let.

Starting with training movies, highlight everything to the left of data fetcher.

Replace it with async let t movies equals.

We can do the same for training TV.

Async let tv equals.

Moving on to top rated movies async.

Let TR movies equals.

And finally as sync let R TV

equals. Now we can update training

equals. Now we can update training movies to try movies.

Training TV to try TV. Top rated movies to try movies. And finally, top rate TV to try

movies. And finally, top rate TV to try 08 TRTV.

By using async let the calls run in parallel instead of each one waiting for the previous one. Press command R to run the app.

We have our app ready. I'll do the same test a couple of times. Closing the app and reopening it.

The app is slightly faster, but as mentioned, this could really make a difference with more network calls and a bigger app.

We'll stop here. Thank you for spending time with me today. I can't wait to see you in the next one.

API network requests are one of the most performance- taxing and expensive things your app can do. It's important to think about how often you need to make a request and adjust your code

accordingly. In this video, we'll

accordingly. In this video, we'll rethink how our app is connecting calls and modify to fit the correct profile.

In our view model class, we have our get titles function retrieving our data from the API.

The issue is that the titles are retrieved even if the list is already populated. This results in more network

populated. This results in more network calls than we need since our data doesn't update consistently.

If this was a chatting app, it would make sense. Let's run the code to see.

make sense. Let's run the code to see.

We can scroll down to see the titles populate. If we change the tab to

populate. If we change the tab to upcoming and go back to home the view is back to the top. This is

because the get titles function repul the titles. With this API, it's unlikely

the titles. With this API, it's unlikely the data changes that fast. When the

user opens the app, populating the titles there is enough. We can fix this in our view model class.

Scroll down right after home status equals stop fetching. Make a new line.

fetching. Make a new line.

If trending movies do is empty open brace.

Scroll down to the catch statement.

Enter.

Add a closing brace.

This makes sure the API is called only if trending movies are empty.

Type else with braces after the if statement. Home status equals dots

statement. Home status equals dots success. This lets our view model know

success. This lets our view model know that the list are populated and to set the UI normally. Hit command R to run.

Now scroll down to load the titles.

Press upcoming and go back to home.

the titles didn't repopulate, making our API calls more efficient. It's a good idea to think about the calls you're making and how you need them. There's

one thing left before wrapping up. Go

back to the view model.

Make sure you're inside the is empty block.

Change the home status to fetching.

This will keep the UI loading.

Run the code.

The progress view is sitting in the corner looking odd. To fix it, open home view.

Select progress view.

Add the dot frame modifier width and height.

Set the width to go.

Dow width. Tab. Gio.

Run.

Much better. Let's reset our status to do success in the view model.

Run the code one more time.

This is a big moment. Section two of the course is officially completed. We've

been uploading this section for about 2 months. I want to thank you all for the

months. I want to thank you all for the support. It really means a lot to us.

support. It really means a lot to us.

Section three will cover navigation.

Can't wait to see you there.

Most of the time when you're working with navigation, you'll go into some kind of detail screen as your second view. That usually means showing more

view. That usually means showing more information about whatever you're displaying on the main screen. In our

case, we've got a list of titles.

Therefore, we'll navigate to a screen that shows more details like the name and description. In this video, we're

and description. In this video, we're going to create this detail screen. Then

we'll be ready for navigation in the next one.

The first step is to create a new file.

Hit command N. Swift UI view. Next

title detail view. Great.

Make some space after line 10. We want

to define a title variable to build our view. Let title colon title make more

view. Let title colon title make more space.

Our preview needs a title argument.

Click on the error fix.

We can put in title preview titles bracket zero. This takes

the first title from our preview titles array. Our sample data coming in clutch.

array. Our sample data coming in clutch.

I'll also show the canvas to help us build the view.

Now we can build how the information will be displayed. Delete the famous hello world and set type geometry reader geometry

in. We're going to display the title

in. We're going to display the title image for now but move on to YouTube video later. Geometry reader allows us

video later. Geometry reader allows us to get the size of the screen for this view.

Next, type scroll view with braces. Some

of the titles have pretty long descriptions. In case this happens

descriptions. In case this happens scroll view will allow us to scroll down. Moving on, type lazy be stack

down. Moving on, type lazy be stack alignment and content leading tab. Press enter for braces.

leading tab. Press enter for braces.

This lazy vstack aligns its elements to the left of the screen and loads them when needed. Now we can declare our

when needed. Now we can declare our async image. Select the one with URL.

async image. Select the one with URL.

URL parenthesis string title poster path question mark question mark quotes.

We unwrap the title poster path and give an empty string in case it's nil. Add an

open brace image in press enter a few times. after the closing brace

times. after the closing brace placeholder progress view.

This adds a nice progress circle as the image loads. Inside the async image

image loads. Inside the async image braces, we can add modifiers image.resizable.scale

image.resizable.scale

to fit.

Finally, add a dot frame modifier after the closing placeholder brace frame

width and height geometry sizewidth tab geometry size.height

times 0.85.

This frame keeps a place for the image as it loads.

With the image ready, we can show some text. text double parenthesis title name

text. text double parenthesis title name question mark question mark title title

question mark question mark parenthesis dobbold dot font dot title 2 padding

five we try both the name and title first if they are both nil then we Just leave it as an empty string. After we apply

modifiers to make it look better. I

would like the title closer to the image.

Padding top, -20.

Now we just need to display the description. Text title overview

description. Text title overview question mark question mark quotes padding five.

This is a very simple detail screen, but it shows everything in the title strct except for the ID. As we move on, we'll add a download button and the YouTube

video in place of the image. Fun fact

to make this screen, we had to use a little bit of everything from the course we've learned so far. Thank you for joining me. See you in the next one.

joining me. See you in the next one.

Navigation is extremely important in Swift UI. This is because you'll want

Swift UI. This is because you'll want users to go to different screens in your app. Currently, the latest and greatest

app. Currently, the latest and greatest way to achieve this is with navigation stack. In this video, we'll show how it

stack. In this video, we'll show how it works by implementing it in Blossom Movie. We're going to start by seeing

Movie. We're going to start by seeing how buttons can drive navigation in our app. We have a hero title showing on the

app. We have a hero title showing on the home screen. The play button is meant to

home screen. The play button is meant to take the user to our title detail screen. Right now, the title is just

screen. Right now, the title is just static. So, the first step is to show a

static. So, the first step is to show a different title every time the user opens the app. Since the app is called Blossom Movie, we'll use a different title from our trending movies list.

Make sure you're inside view model.

Under top rated TV, add a line.

Type var hero title equals title preview bracket zero.

This defines a new title called hero title. We initialize it using the first

title. We initialize it using the first title in our sample data. By doing this we'll have a fall back and we'll need to

unwrap it later. Scrolling down

make a couple lines after top rated TV if flat title equals trending movies.

Random element braces hero title equals title.

This makes sure trending movies.rand

random element isn't nil before assigning it to hero title. We're all

done here. Open home view.

Delete hero test title with command delete.

There's an error inside async image because hero test title doesn't exist anymore.

Replace hero test title with view model hero title. Poster path question mark

hero title. Poster path question mark question mark quotes poster path is an optional so we do need to unwrap it just an empty stream works

for now the picture will just be a progress if nil press command R to run nice title changed I'll close and reopen

the app to make sure it changes each time.

Keep in mind, we're getting a random title from trending movies. Therefore

we might have some repeats.

Step one is complete. Now we can get into the navigation.

After we define our view model type add state private

bar title detail path equals navigation path.

Add state means the UI can change this variable. Navigation path helps us

variable. Navigation path helps us manage where the user goes in our app.

I'll close the log.

Now we can embed our geometation stack. Make sure your canvas is open so

stack. Make sure your canvas is open so we can use a shortcut.

I'll adjust the canvas.

Right click on geometry reader.

embed navigation stack parenthesis path colon dollar sign title detail path.

Navigation stack starts with your main screen and let you navigate to new ones.

Kind of like stacking pages on top of each other. Path dollar sign title

each other. Path dollar sign title detail path tell Swift UI which screens be visited so it knows where to go and how to go back.

Moving on, inside the play button action, we can put title detail path.append

view model her title.

This adds a new screen to the stack using hero title as a data for that screen. We do get an error because our

screen. We do get an error because our title struck doesn't conform to hashable. To fix, open title.sswift

hashable. To fix, open title.sswift

Swift.

After identifiable put a comma hashable.

Hashable let Swift tell if two items are the same. This is required when using

the same. This is required when using navigation path. Let's go back to home

navigation path. Let's go back to home view.

And the error is gone. We can close the canvas.

The last thing we need to do is add a navigation destination. Find the closing

navigation destination. Find the closing brace of our lazy vstack.

Make some space dot navigation destination.

Select for destination title self tab enter title tab tab title detail

view title title.

This tells Swift UI when we push a title onto the stack, show the title detail view with that title. It tells Swift UI knows what screen to show when we use

that appending with the title. Hit

command R to test.

All right, our app is ready. Let's press

the play button.

Nice. It works. We can scroll down for the rest of the description.

Our layout isn't the best. That's what I get for listening to the canvas and not testing. It's an easy fix. Open title

testing. It's an easy fix. Open title

detail view.

Delete the negative padding.

Run it again.

Now press play again.

Much better.

That's all for this video. Thank you for watching.

There's a good chance you'll be showing some kind of list in your app. When an

item of that list is tapped, it's common to navigate to another screen passing the item data. In this video, we'll show how to achieve this using navigation

paths.

We want to keep the navigation logic inside home view. To do this, the tapped item needs to be accessible here. Open

horizontal list view.

Press enter on line 12.

Type let on select colon title. Return our void.

colon title. Return our void.

On select is a closure property that takes a title and returns nothing. The

title we pass will be accessible to home view allowing it to handle navigation.

Keeping that logic inside the parent view helps prevent nasty bugs. Now we

need to pass the tab title to our on select property. The best place to do

select property. The best place to do this is right before the for each loop ends. Here we still have access to the

ends. Here we still have access to the title that was tapped. Click the end of our dot frame modifier.

Make a new line. The ontap gesture on select title.

We just need to fix our preview. Scroll

down after the closing parenthesis. Open

brace title in. Press enter a few times.

This tells a preview do nothing with the tab title.

With this completed, we can go back to home view.

Scroll down.

We have some errors to fix due to our new on select property.

After the closing parenthesis, add an open brace title in enter times two inside the braces title detail

path.append

path.append title.

As a quick reminder, title detail path is a navigation path we defined on top.appen

top.appen puts our tap title strruct into the title detail path. This allows it to be displayed in title detail view defined

below with navigation destination.

We just need to fix the other three horizontal list errors. Let's copy the closure we use with trending movies.

Paste it for the next three.

Always good to save some time.

Before we test, we can cut our navigation destination modifier.

Paste it right under our task modifier.

It's good practice to have this modifier as close to the navigation stack as possible.

This helps us see the navigation more clearly. Go ahead and run the code with

clearly. Go ahead and run the code with command R.

Let's click on our first trending movie.

Nice. It works. Let's go back and do some more testing. I'll just click on a few Good. We're able to click around and go

Good. We're able to click around and go back to our home screen.

Let's also make sure the hero play button works.

Do it a few times.

No issues here as well.

a slightly shorter video, but if you're not careful, nasty bugs happen with navigation. These can include duplicate

navigation. These can include duplicate screens and views not being dismissed.

That's it for this one. Thank you for watching.

You might want to display a web page or maybe a YouTube video in your iOS app.

WebKit is a perfect tool to help accomplish this. This is a UI kit tool

accomplish this. This is a UI kit tool and at the time of this recording there's no native way to do this in Swift UI. In this lesson, we'll set up a

Swift UI. In this lesson, we'll set up a web view to display a YouTube video.

This will get us one step closer to fetching and displaying trailers for each title.

Before we get started, I want to point out it's not a good idea to keep sensitive information directly in the client or production apps. Firebased

cloud storage or a secure backend would be a safer option. However, this is a practice app that won't be published.

The goal here is to focus on learning and get better with Swift UI. With that

out of the way, let's open our API config.json file. Add a comma after TMDB

config.json file. Add a comma after TMDB API key.

Enter. Type parenthesis youtube base URL parenthesis colon parenthesis https

colon YouTube.com slashmbbed.

This will be the URL we use to show our YouTube videos.

Next, open apicconfig.sswift. Swift

after TMDB API key press enter. Let

YouTube base URL colon string. This

gives us access to the YouTube base URL string throughout our app. Please make

sure your spelling matches with mine before moving on.

Make a new file with command N. Swift

file. Next. YouTube

player. Great.

We don't need import foundation here. I

can replace it with import swift UI.

Also add import web kit. Add a couple lines. Type strruct

lines. Type strruct YouTube player colon ui view representable with braces.

UI view representable lets us wrap a UI kit view and use it inside Swift UI.

Inside the braces, type lat web view equals UK web view enter let video ID

colon string enter let YouTube base URL equals API config.shared shared

YouTube base URL UK web view creates an instance of a web browser but inside the app we'll use the video ID and YouTube base URL to load

our videos inside it make some space and type funk make UI view context context return some view UI view inside the

braces type web view this function is part of the UI view representable protocol It's called once our Swift UI view is created and tell Swift UI this is a UI

kit I want you to use. In our case, show the web view we just made.

Down arrow once. Add a couple more lines. Funk update UI view. UI view UI

lines. Funk update UI view. UI view UI view type context context with braces.

Another part of the UI representable protocol. This one gets called whenever

protocol. This one gets called whenever Swift UI updates the view. We'll load

our video here into the web view.

Inside the braces, type guard lat base URL string equals YouTube base URL

comma, enter, let base URL equals URL parenthesis string base URL string

else bracket return.

This first unwraps the YouTube base URL string from our API config ensuring it's not nil. Then we convert the string into

not nil. Then we convert the string into a URL. Also making sure it's not nil. If

a URL. Also making sure it's not nil. If

either of them are nil, we safely exit the function.

Below that add let URL equals base URL appending path video ID.

Enter web view.load

URL request parenthesis URL full URL.

We complete our YouTube URL by appending the video ID. Then we have web view load this URL.

This completes the file. Let's now open title detail view.

Delete async image.

Add YouTube player video ID.

Paste this ID in there. It's one of our most popular YouTube shorts.

Add the modifier aspect ratio. Select aspect ratio and

aspect ratio. Select aspect ratio and content mode. 1.3 for aspect ratio. Tab.

content mode. 1.3 for aspect ratio. Tab.

Do fit for content mode. This modifier

makes sure the video keeps the correct shape and scales proportionally.

4x3 or 1.3 is a YouTube classic embedded video ratio. Hit command R to run.

video ratio. Hit command R to run.

The simulator is up. Click on any movie title.

Perfect. We have our web player showing front and center. Press play. Need an SF symbol.

And it works.

Right now, all the titles will have the same video, but the next step is to work with the YouTube API to get the trailers for each title. Thank you for watching

this video. See you in the next one.

this video. See you in the next one.

Google along with Apple is one of the main forces in the mobile development space. As an iOS developer, there's a

space. As an iOS developer, there's a good chance you'll use a Google product at some point. This could take form with Google O, Firebase or Google API.

In this video, we're going to start working with the YouTube API. This

includes getting our API key from the Google Developer Console as well as modeling the return JSON data. The first

step in our journey is to get our credentials. Open your favorite browser.

credentials. Open your favorite browser.

Type Google Developer Console in the search bar. Open the first link.

search bar. Open the first link.

If you haven't already, please make yourself a Google account. Once you have one, click on create project.

I'll call this project Blossom Movie.

Create.

With the project created, navigate to library.

Find YouTube data API v3.

Open it.

Enable.

Now we just need to create credentials.

We're going to be using public data.

Next, we have our key ready to go. Go ahead

and copy it.

Let's go back to Xcode.

Open APIconfig.json.

Put a comma after YouTube base URL.

Enter parenthesis YouTube API key parenthesis colon double

parenthesis. Please paste your API key

parenthesis. Please paste your API key in here. I'm going to put in your key as

in here. I'm going to put in your key as a placeholder.

While we're here, we might as well define the YouTube search URL.

Add a comma after the YouTube key. Enter

parenthesis YouTube search URL parenthesis colon parenthesis.

I'll paste the URL here to save some time. It'll be in the description if you

time. It'll be in the description if you want to grab it. With our new properties in the JSON file, we just need to find them in API config.sswift. Swift.

Make a new line after YouTube base URL.

Let YouTube API key colon string. Enter.

Let YouTube search URL colon string. Please make

sure your spelling matches with mine before moving on. The next step is to model our JSON data. I'll bring up an example of what a YouTube search car returns.

This is the data we're working with.

We're going to conduct a YouTube search on the title name and the word trailer.

The only element that enters us here is video ID inside the items array. This

video, ID, will, be, passed, to our, YouTube player to show the trailer. We'll simply

use the first ID as it's most likely to be the correct video. Let's head back to Xcode. Command N for a new file.

Xcode. Command N for a new file.

Swift file. Next

YouTube.

Search response.

Create.

I find it easier to start from the bottom when modeling NSA data set ID properties

colon codable with braces let video ID

colon string question mark. This models

the last layer getting our video ID.

It's a good idea to make these properties optionals in case the database returns null.

We can go to the next layer up which is an object named ID. Make room at the top of ID properties strct item properties

colon codable let id colon id properties question mark.

We just need the top layer strct YouTube search response colon codable.

The top layer has an array that contains item properties. Therefore, we can type

item properties. Therefore, we can type let items colon bracket item properties

bracket question mark.

The str names themselves don't matter as much as the property names. Property

names have to match the JSON data or else the app will crash. I'll bring up our working data one more time.

Starting from the top, we have an array of items that contains an object called ID.

Inside ID, we're able to get the video ID.

We're all done here. Everything is set up and the next step is to parse the data. Thank you for watching.

data. Thank you for watching.

Something about programming is that although rewarding, it tends to be a bit repetitive.

For example, in our app, we already saw how to model an API response, build a URL, and parse the response. In the

pursuit of working with the YouTube API we already modeled the response. Now, we

must complete the next two steps building the URL and parsing. This will

be the main topic of this video. It's

good to remember that practice does make perfect. To start, open dataf

perfect. To start, open dataf fetcher.sswift. Swift.

fetcher.sswift. Swift.

We need to define our new variables under TMDB API key.

Let YouTube search URL equals API config.shared

YouTube search URL. Enter. Let YouTube

API key equals API config.shared shared

that YouTube API key.

To recap, our end goal is to show the YouTube trailer for each title in the detail screen. By using the YouTube API

detail screen. By using the YouTube API we can get the trailer video ID and pass that in the YouTube player we made earlier.

Now, we can create a function to build the URL and parse the data. Scroll all

the way down.

Click after the second to last curly brace. Hit enter a couple times. We'll

brace. Hit enter a couple times. We'll

make this its own function since it's a different API.

Punk fetch video ID parentheses for title colon string close parenthesis

async throws return error string.

This function takes in a string called title. It also makes a network call and

title. It also makes a network call and could throw an error. Therefore, async

and throws are needed. The function

returns a string at the end.

Inside the braces, we need to first unwrap the YouTube URL and key guard let base search

URL equals YouTube search URL else braces throw

network error. Missing config. Down

network error. Missing config. Down

arrow. Enter twice. Guard flat.

Search API key equals YouTube API key else braces throw network error

missing config.

The next step is to build our URL. I

actually want to show a different way to accomplish this without hard coding like we did in the movie database. Open

constants right after the closing brace. Make some

space and scroll down.

enum YouTube URL string colon string with braces case trailer

equals parentheses trailer case query shortened equals parenthesis q

space equals parenthesis space

case key equals parenthesis key.

Using enims this way to group related strings helps prevent typos, encourages reusability, and keeps things neat. I

wanted to show a better way than just hard- coding the values. I used this method in another project, and it's been pretty good.

It should actually be YouTube. You're

all strings.

Going back to data fetcher under search API key.

Let trailer search equals title plus YouTube URL strings.space.

strings.space.

Value plus YouTube all strings.tra.value.

strings.tra.value.

value. This is one query we'll make to the API.

Make some space. Time to finally build our URL.

Guard lamp fetch video URL equals URL parenthesis string base search URL

appending query items URL query items. Press enter. After

the first bracket, press enter.

Press enter again after the closing bracket.

This spaces it out, making it easier to read. We can now add our query items.

read. We can now add our query items. I'll scroll down a bit first. Add a

parenthesis after URL query item.

Select name and value for name. YouTube

URL strings dotquery shortened raw value tab trailer search right arrow add a comma to add another

query item press enter first for better spacing URL query item name and value YouTube

url strings key dot raw value tab search API key.

We just need to complete the guardlet statement after the closing parenthesis else with braces throw network error dot URL build

failed.

After we build the URL add print fetch video URL.

This will help us check the URL in the console.

To help us see this more clearly, I'm going to paste the URL that we just built using Breaking Bad as an example.

The first part is our base URL.

We then add our first query represented by the Q and the trailer search string we built. The second query is our API

we built. The second query is our API key.

I did want to tackle parsing in this video, but I actually think this is a good place to stop for now. Thank you

for watching.

Parsing API JSON responses is one of those things that keeps coming back.

We've already seen how to do it with the movie database, but this time we're taking it a step further. In this video we're going to refactor that code and

make it reusable using Swift Generics.

With the refactored code, we'll be able to parse our YouTube API response.

We can open data fetcher to start.

Parsing the response and our fetch titles function starts from let data you response to var titles. This code will be similar to parsing the YouTube

response. The only difference being what

response. The only difference being what object we return. We could just copy and paste the code down to our fetch video ID function. However, this is the

ID function. However, this is the perfect moment to introduce Swift generics. This can also be good code for

generics. This can also be good code for later projects.

Let's make some space after the fetch title closing brace.

We'll make the function here first. Test

it. Then call it for the YouTube response.

Funk fetch and decode.

Left arrow T colon decodable right arrow parenthesis URL colon URL

comma type colon type parenthesis async throws return arrow

t with braces.

T col decodable lets the function know that the struck we're working with must conform to decodable. We pass in two arguments a URL and a strruct using t.type.

t.type.

t.type means we're passing a generic strruct that we want to decode like API object self. The function will perform a

object self. The function will perform a network request and can throw an error which is why async and throws are needed. Finally, it will return a strct

needed. Finally, it will return a strct represented by T.

Select from lat data UR response to V titles cut with command X.

Paste it inside our fetch and decode function.

We just need some adjustments. Replace

fetch titles URL with URL.

Delete bar titles equals at the end.

replace with return API object should be tel finally delete results.

We replace our hard-coded values with the arguments we pass into the function.

Here's a quick recap of what this code does. We use try await URL sessions that

does. We use try await URL sessions that share that data to fetch data from the URL passed into the function. This gives

us both data and a URL response.

Then we unwrap the response and make sure it's a HTTP URL response with a 200 status code. If it's not, we throw a

status code. If it's not, we throw a custom nextstep error that describes what went wrong. After that, we define a JSON decoder and tell it to use doc

convert from snake case. This is needed since our JSON keys are snake case, but our swift strrus use camel case. Lastly

we use that decoder to try and return the object type T from the URL data. T

is the generic type we pass into the function. We can actually replace this

function. We can actually replace this with type since we already defined the variable.

Now we just need to call this function and fetch titles. Click under print fetch titles URL bar titles

equals try08 fetch and decode fetch titles URL

API objects self dot results.

Going back to title.sswift

remember that API object has a variable called results and that's where the titles live.

I think a rename is in order because we're now working with two APIs.

Right click API object refactor rename.

Add TM DB at the front. Enter.

Navigate back to data fetcher.sswift.

To clean up our strruct a bit more let's move the helper function to the end. Select fetch and decode and build

end. Select fetch and decode and build URL.

Command X.

Paste them under fetch video ID.

The final step is to use our new function to finish fetch video ID.

Select after the print statement.

Make some space.

Return try 08. fetch and decode fetch video URL YouTube search response

self do items do first id dot video ID question mark question

mark quotes we pass in the YouTube search response struck this time remember this strct is pretty nested It returns a list of items. So, we grab

the first item in the array. From there

we drill down to video ID, chaining each level with a question mark to safely unwrap. If anything in the chain is nil

unwrap. If anything in the chain is nil we return an empty string using a double question mark operator. We're done with parsing the API responses. The fetch

video ID funk isn't hooked up anywhere but we can still test to make sure our new function works for fetch titles.

Please update your TMDB key inside apeconfig.json before running. Hit

apeconfig.json before running. Hit

command R.

Our simulator is up and everything loaded correctly. Click around to make

loaded correctly. Click around to make sure everything is working.

Good job with this. Thank you for watching and I'll see you in the next one. In our app so far, we've done a lot

one. In our app so far, we've done a lot of leg work to get the YouTube API going. After modeling, URL building, and

going. After modeling, URL building, and parsing, all that is left is to hook everything up. Although slightly

everything up. Although slightly controversial, we'll be using model view, view, model to get everything up and running.

Lucky for us, we've already built the MVVM structure in our app. That was the hard part. Adding more logic is much

hard part. Adding more logic is much easier. Open view model.sswift.

easier. Open view model.sswift.

Press enter after the home status enum.

We'll make a new enum to keep track of the video ID fetch state. private

set bar video ID status colon fetch status equals dot not started.

Let's press enter to space it out better.

Make a new line under hero title bar video ID equals quotes.

We'll use this property to hold the string that our YouTube API fetches.

Find the closing get titles brace.

Press enter a couple times after it.

Funk get video ID or title colon string

async with braces.

This function will be called in the UI to retrieve the video ID.

We can set the status to fetching when called.

Video id status equals batching.

Create a do catch block under that.

This will help us handle errors. Inside

do we can call the API video ID equals try8

data fetcher. Fetch video ID for title

data fetcher. Fetch video ID for title video ID status equals success.

We set this if everything goes well.

Inside catch, we can print the error to our log.

In addition, set the status to failed.

Don't forget to pass the error for underlying error.

This completes the view model. All

that's left is to hook it up to our UI.

Open title detail view.

There are a few things I want to do here to make our code better on top of adding the feature. To start off, let's create

the feature. To start off, let's create a computer property of our title name.

Make a new line after title bar title name colon string with braces.

Inside the braces, type return parenthesis title.name

parenthesis title.name question mark question mark title.title

parenthesis question mark question mark quotes.

This is the first time we've seen computer properties. It's a great way to

computer properties. It's a great way to add custom logic to your models without storing extra data.

This logic tries to return their name or title first. If they are nil, an empty

title first. If they are nil, an empty string is returned instead.

Our API uses names for movies and titles for TV shows. So, we test for both.

Under this computer property, make a new line. Let

line. Let view model equals view model.

Our view model will control the UI depending on the API call result. Find

the closing brace and scroll view from the closing brace through the open brace. Press command X to cut.

brace. Press command X to cut.

This code will be used if the call succeeds.

Inside the geometry brace type switch view model dot video id status

Xcode completes the cases for us inside put empty view.

We can tab over do fetching will just show a progress view.

Add a line and add dot frame width and height for width. Geometry dots size dowidth

for width. Geometry dots size dowidth tab geometry dots size doheight.

This puts the progress view in the center inside case.

Paste the UI code we cut.

Replace video ID inside the YouTube player with view model

dot video ID.

We can also use title name inside text.

Finally inside case.failed

type text underlying error.lo

description.

For now, we just showed what error took place, but we'll make this more user friendly later.

The last thing to do is to call a view model to fetch the video ID.

After the geometry reader brace make a new line.

Task with braces view model get video ID or title name.

Before we test, please make sure you update your API keys in API config.json.

Hit command R to run.

The app is ready. Click on the play button from the hero title.

Nice. The video loads and it looks like it grabbed the correct trailer. Press

play and it works.

Go back.

Let's try a train TV title.

Perfect. Also looks great. Just a heads up with the YouTube API. Google limits

the API calls per day. If you open enough titles, you'll get an error at some point. That's it for this video.

some point. That's it for this video.

Thank you so much for watching.

With making iOS apps, there's a good chance you need different style of list.

We already saw how to make a horizontal list. In this video, we'll make a

list. In this video, we'll make a vertical list in Swift UI, expanding our tool set. Let's start by making a new

tool set. Let's start by making a new file with command N. Swift UI view.

Next, vertical list view. create.

Click on line 10. Enter.

We can define an array of titles to use for the list. bar titles colon bracket title.

We get an error because the preview needs a list of titles to use. Select

the error fix pass title.

Preview titles.

Make a new line after titles.

Delete the famous hello world list parenthesis titles parenthesis bracket title n inside the

list braces async image URL URL parenthesis string title poster path question mark question

mark quotes.

We make a new list and pass our titles in. Then we show each title poster with

in. Then we show each title poster with async image by converting the poster path to a URL. Modifiers are needed on

our async image. Right arrow twice bracket image in enter. Enter.

Right arrow placeholder progress view.

This shows a progress circle as the image loads. If the poster path is nil

image loads. If the poster path is nil we pass an empty string. That just keeps the progress view going. Go back in the

async image brace. Image entertizable

dots scale to fit dot clip shape.

Rect corner radius 10 dot padding five.

These modifiers allow the image to be resized to fit within the space given.

Make the corners round by a radius of 10 and add space around at five points. We

want the title names to be next to the poster. Therefore, right click on image

poster. Therefore, right click on image embed h stack.

Make some space after padding five text parenthesy parenthesy title.name

name question mark question mark title.title

parenthesy question mark question mark quotes.

A title could have a name or title property depending if it's a movie or TV show. We try both and if they're null

show. We try both and if they're null an empty string is passed. Let's add a couple of modifiers to the title.

font.system

size 14.bold.

By putting our title inside the async image closure, it won't show unless the poster loads.

Click after the placeholder brace. Enter

frame height 150.

This keeps the list items no more than 150 points tall. Since no width is specified, each item takes up as much space needed horizontally.

The list looks great, we can use it in our app, but only with sample data for now. Make a new file with command N.

now. Make a new file with command N.

Swift UI view. Next, upcoming view.

Create.

Delete. Hello world.

Vertical list view. titles title

preview titles.

This seems like overkill to just call this in our content view. In the future there will be logic here that can make our content view messy.

Navigate to content view.

Delete text contents upcoming string.

Replace with upcoming view.

Press command R to run.

Our app is up and running. Click on

upcoming.

Nice. We have our sample data showing as a vertical list. We still need to add navigation and of course real API data.

This is the first time we work on another tab in the app. So, great job.

We're done here. Thank you for watching.

At this point in the tutorial, our vertical list is built. However, it's

still only showing test data. In this

video, we'll populate it using our API and display a list of upcoming movies.

Open data fetcher.

I'll paste the URL we'll populate our vertical list with this URL is identical to our top rated URL with the only difference being

upcoming instead of top rated.

We'll have to adjust our build your function to construct this correctly.

Scroll down inside the else if statement. Type or

type double equals upcoming.

Replace top rated with backslash parenthesis type.

This will build the correct URL when top rated or upcoming are passed for the type. To make our code a bit better

type. To make our code a bit better delete trending in the first if statement backslash parenthesis type.

The less hard-coded strings, the better.

Our build URL function can now make the upcoming URL.

With this, we can open our view model.

Make a new line after video ID status private parenthesis set bar upcoming

status fetch status equals dotn not started.

We'll use this enum to keep track of the upcoming titles fetch state.

A new array is also needed to hold the upcoming movies. This can be placed

upcoming movies. This can be placed after top rated TV.

bar upcoming movies colon bracket title equals empty brackets.

By making this array empty, we won't need to unwrap it later.

Since this call will not be on the home screen, we can make a new function for it. Scroll all the way down. Make some

it. Scroll all the way down. Make some

space after the get video ID function.

funk get upcoming movies parenthesis async with braces.

Inside the braces, we can set upcoming status equal to fetching.

Then we can make a do catch block inside do upcoming movies equals try await data fetcher. Match titles for

movie by upcoming try as needed because this function can throw errors and a wait allows it to run

in the background not blocking the UI.

If this call completes upcoming status equals success.

Now we just need to complete the catch statement.

print error upcoming status equals failed underlying error error. We print the error in our

error error. We print the error in our console for debugging and then pass the error in our enum for the UI. Everything

is ready to go here. We just need to hook it up to our UI. Open upcoming

view.swift.

Make a couple of lines after line 10.

Let view model equals view model.

Delete the preview titles.

Replace with view model.upcoming

movies.

Make some space before this line.

Switch view model upcoming status.

This allows us to define a view for each state.

In do not started. We can put empty view dot fetching is progress view cut the vertical list view code

paste it inside dots success.

Lastly text underlying error.lo description works

underlying error.lo description works great for case.failed.

The progress view should be in the center of our view when it loads. Before

the switch statement geometry reader brace go n delete the closing brace put it after

the switch closing brace.

Add a frame in progress view frame width and height go.

Dow width go. size the height.

go. size the height.

This puts the progress view in the center of the screen.

All that's left is to populate the data.

Bind the geometry reader closing brace entertask with braces.

Inside the braces view model get upcoming movies.

This populates the upcoming movies array.

Before running, please make sure to update your API keys inside API config.json.

config.json.

Hit command R to run.

Our app is up and running. Navigate to

upcoming.

Nice. The data populates.

We can scroll down and everything is in working order. If

you're thinking it's getting easier to set everything up, you'd be right. A lot

of the infrastructure was done in section 2, allowing our new features to be added faster. That's it for this video. Thank you for watching.

video. Thank you for watching.

Navigation is a critical aspect in any app. Like most things, there are a few

app. Like most things, there are a few ways to accomplish this. We already saw one way by using navigation stack and navigation path in the home screen. In

this video, we'll demonstrate how to use navigation links with navigation stack for the upcoming screen. To start, let's open the parent view, which would be

upcoming view.

We need to embed this view into a navigation stack. Right click on

navigation stack. Right click on geometry reader embed, navigation stack.

This allows us to enable navigation within our view. For example, tapping a list item can now push a new screen into

the stack. The next step is to define a

the stack. The next step is to define a navigation link inside the child view.

Open vertical list view.

Make some space before async image.

Navigation link with braces.

Define a label after the closing braces.

Inside the navigation link braces, we need to define our destination.

Title detail view title title. This

passes the title tapped before going to title detail view. Label defines how the navigation link is shown. The UI for the vertical list will do. Great. Select

from async image till frame. Cut. paste

it inside label.

To recap, we add a navigation stack to the parent view. Then we add a navigation link inside the child view.

Navigation link lets us define where the user goes and how the link looks using label. Please make sure the API keys are

label. Please make sure the API keys are updated inside APIconig.json.

Press command R to run.

The app is up. Select upcoming.

Notice there are arrows on the right showing a clickable list. Click on the title.

The title detail view opens correctly.

I'll test a few more titles.

Everything seems to be in working order.

Let's go to the home tab while on a title detail in the upcoming tab. Click

on the title.

Go back to upcoming the detail screen maintained and upcoming. If we go back to home

upcoming. If we go back to home the detail screen also maintained there.

I do want to talk about navigation path versus navigation link to finish the video. Although navigation link is much

video. Although navigation link is much simpler to use compared to navigation path, it comes with a few drawbacks.

First, it's a little too eager to make the views. This can happen sometimes

the views. This can happen sometimes even when an item or button isn't tapped. Therefore, performance issues

tapped. Therefore, performance issues can occur.

Second, navigation link only works with user interaction. You can't use it to

user interaction. You can't use it to navigate after loading data or handling things like deep links or push notifications.

That means no async navigation and no programmic control. All that being said

programmic control. All that being said navigation link does work and it's really simple to use. I think it's good for practice apps where the point is to

work on other big features and keep navigation simple. However, for

navigation simple. However, for production apps, I would lean towards navigation path to keep performance and overall user experience tight. I wanted

to show you both ways to give you more options.

That wraps up this video. Thank you so much for watching. We officially

finished the second tab on the app and are pretty close to completion. I can't

wait to see you in the next one.

We've seen how to build horizontal and vertical lists. In this tutorial

vertical lists. In this tutorial sometimes your app might instead need a grid layout with rows and columns of content. That'll be the main topic for

content. That'll be the main topic for this video, creating the UI for grid style list.

This is going to be the third tab of our app that will contain the search feature. Therefore, we'll need to create

feature. Therefore, we'll need to create a new file. Press command N. Swift UI

view. Next, search view. Create.

Make some room. After line 10 we'll define an array of titles here that will populate the list. bar titles

equals title preview titles. For now

our sample data works great.

Go ahead and delete the famous hello world.

Instead, type scroll view with braces.

This screen will later return a list of titles from a search. Therefore, we want to scroll view in case the search

results exceed the screen capacity.

Inside the scroll view, type lazy v grid.

Down arrow and right arrow to see more options. Select the option with columns

options. Select the option with columns and content.

Lazy V-grid allows the items to stack vertically into columns. Lazy means this grid will load elements only when they are needed on screen.

Columns is an array of grid items that define how many columns our grid has.

For this grid, three is perfect. Meaning

we need to add three grid items. Select grid item. Press enter. Add a set of

grid item. Press enter. Add a set of parenthesis before the closing brace

comma, grid item, parenthesis, comma grid item, parenthesis.

We could have to find different spacings and other properties for the grid items. However, just a simple version is good for our needs.

Tab over to content. Press enter to complete the closure.

The content of our lazy V grid can go inside these braces or each open parenthesis titles

close parenthesis bracket title in enter twice. This loops

through our titles list and gives us access to each element.

For each element, just the movie poster is good. Put your cursor inside the for

is good. Put your cursor inside the for each braces async image URL URL parenthesis string

title poster path question mark question mark quotes.

We convert the poster path string into a URL and also safely unwrap it. If it's

nil, an empty string is passed.

A placeholder is needed for our async image as it loads. Add an open brace at the end.

Image in. Press enter a couple times.

After the closing brace, place holder.

Enter. Progress view.

They show the progress circle while the image loads.

Now we can add modifiers to our async image inside the braces. Image

entertizable dots scale to fit dot clip shape rect parentheses corner radius

10.

These modifiers allow the image to resize into the lot space while keeping good aspect ratio and rounding the corners.

To finish this off, we can add a frame for each of the images. Select the

closing placeholder brace. Enter. Dot

frame.

Select the one with width and height.

Width will be 120 points and height will be 200 points.

This allows the images to only be within this frame.

Our lazy V-grid is complete. We can

scroll down on the list if more elements get populated.

It's a shame our sample data only has three titles, so we can't see how the next row looks like. Don't worry, real data will populate here soon.

We can run a quick test. Open up content view.

Delete the text inside the search tab.

Search view.

Make sure your API config has the movie database key. Command R to run.

database key. Command R to run.

Our simulator is up and running. Click

on the search tab.

We have the grid showing. That wraps up this video. Thank you so much for

this video. Thank you so much for watching.

There's a good chance you'll need a toolbar at some point in your app. It's

perfect for storing buttons, setting a title, or even housing a search bar. In

this video, we'll build a Swift UI toolbar and add simple logic to make it dynamic. We can start by adding a title

dynamic. We can start by adding a title first. For this feature, the main view

first. For this feature, the main view needs to be inside a navigation stack.

Inside search view, right click on scroll view, embed navigation stack.

This is a great first step as we'll need navigation later for this view. The rule

of thumb when adding a navigation title is to be as close to the navigation stack as possible. The closest we can get is after our scroll view. Find the

scroll view closing brace. I'll scroll

down. Press enter after the brace dot navigation title.

It's tempting to hardcode the title in there. However, a good habit is to have

there. However, a good habit is to have the strings in one place where we can manage them better. It'll also make adding logic easier. Open

constants.sswift.

Make some space after the top rated TV string.

static bl string equals quotes movie search.

Make a new line static let TV search string equals quotes TV search.

In our view, we'll be able to search for either a movie or TV show. Therefore, we

want the title to change depending on what's selected.

In addition, we'll also need two icons.

Scroll down.

Hit enter after the download icon string.

Static lamp TV icon string equals quotes.

Copy and paste it below.

Change the second TV to movie.

Click inside the TV icon string quotes.

The SF symbol name goes inside here. I

happen to know the name is just TV.

Select inside the movie icon string quotes. Command shift L. The icon for

quotes. Command shift L. The icon for movies is going to be the clapper that says we'll take their in. Search for

clapper.

Press enter and the SF symbol name gets filled out. A good trick if you ever

filled out. A good trick if you ever need to search for SF symbols.

Our strings and icons are ready. Go back

to search view.sswift.

Inside navigation title, put constants dot movie search string.

The title looks good. We also need a button to toggle between a movie and TV search. Press enter after navigation

search. Press enter after navigation title dot toolbar with braces.

I'll scroll down.

Inside the braces, we can define a tool bar item.

Select the one that has placement and content.

Placement is where the toolbar item will be. dot top bar trailing.

be. dot top bar trailing.

This puts the item on the right of the bar. Tab over to content and press

bar. Tab over to content and press enter.

Inside these braces, we can define what's in the toolbar. Type button with braces.

After the closing brace label, press enter to autocomplete.

Label lets us define how the button looks. In this case, the button will be

looks. In this case, the button will be an SF symbol.

Type image system name constants dot movie icon string.

Nice. We get the image on the top right.

Our toolbar is looking good. We just

need to add some logic. When the movie icon is tapped, the title and icon should switch to TV. For this, a new

variable is needed. Scroll up.

Make a new line after v titles at state private bar search by

movies equals true.

Add state allows a UI to change this variable. Private allows only this view

variable. Private allows only this view to access the variable. Private isn't

absolutely needed here, but it's good practice to keep everything scoped to what they need. With our new boolean, we

can scroll down to the tobar item.

Inside button, type search by movies toggle.

Now, when the button is pressed, search by movies will go from true to false and vice versa.

To finish the logic, we need both the navigation title and icon to depend on a boolean before constants that movie search string inside title

search by movies question mark

after constants dot movie search string colon constants.

TV search string we use eternity operator where if search by movies is true then show the movie search string if it's false then show

the TV search string for the navigation title copy, the, it, turn itary, operator, logic

paste it as a system name variable just use movie icon string and TV icon string.

This completes the logic. We can test this inside our preview.

Nice. Everything is in working order.

This is a great place to stop. Our UI

has an awesome toolbar and some logic to help in the later stages. Thank you for watching. I'll see you in the next one.

watching. I'll see you in the next one.

Search bars are a common feature in iOS apps. They can help filter and organize

apps. They can help filter and organize data for the user. In this video, we'll add a search bar to our UI and construct the API search URL.

To start off, we'll need new string constants for our search bar prompt text. This text will be displayed when

text. This text will be displayed when the bar is empty. Open constants.

Make a new line after TV search string static lat movie placeholder

string equals quotes search for a movie.

Copy and paste the line we just made.

Switch out movie for TV.

Search for a TV show.

We'll need these two since we'll be switching between a movie search and TV search.

Go back to search view.

In addition to these new strings, we also need a variable to hold what the user searches. Make some space after

user searches. Make some space after line 12.

at state private bar search text equals quotes at state allows a view to modify the

variable. We use private because no

variable. We use private because no other file will need to access this variable directly.

All the precursors are ready. Scroll

down and find the toolbar closing brace.

Enter dot searchable.

Choose the one with text and prompt.

For text, we can put dollar sign search text.

This creates a binding. Whatever the

user types in the search bar gets synced into our search text variable. Tab over

to prompt. Search by movies question mark constants dot movie placeholder string colon

constants dot TV placeholder string.

We see the turnary operator here allowing us to display the correct prompt depending on the type of search.

Please update your API keys in APIconig.json.

APIconig.json.

Press command R to run.

Our app is up. Navigate to the search tab.

Nice. Our search bar is showing. We can

toggle it back and forth.

Everything is working correctly.

I do want to point out how easy it was to add the search bar thanks to Swift UI. In addition, we were able to use

UI. In addition, we were able to use searchable because our view is inside a navigation stack.

If we didn't use a navigation stack, for example, if we were in a sheet, then we would have to make the bar manually which isn't too bad.

We can get the bar rolling on hooking up the search bar to our API.

A good place to start is to build the URL. Open data fetcher.

URL. Open data fetcher.

I'll paste what a search call looks like.

This one is a bit different than the rest of the URLs we've built so far. The

word search is before the media type.

And there are two queries, the API key and the movie or TV show we are searching. In this example, it's the

searching. In this example, it's the movie Pulp Fiction.

With this accounted for, we can scroll down to our build URL function.

Click after the else if closing brace.

Hit enter a couple times.

Add else if type double equals quotes search with braces.

Copy the path from the first else if statement.

Paste it inside our second else if.

Switch around type and media.

I'll backspace else so it chains correctly.

Admittedly, these chaining if statements aren't the prettiest. It would probably look better with a switch case statement. Maybe we'll go back and

statement. Maybe we'll go back and tighten up our code in a future video.

To wrap up, we just need to handle our second query item for the search URL.

In the arguments add a comma after type search phrase colon string question mark equals

nil.

This argument is optional with a default value of nil allowing the function to be used without a search phrase.

Scroll down before we make the URL.

makes room before the guardlet statement.

Bar URL query items equals brackets.

Copy the API key query item and paste it inside our new array.

Every API call needs this API key query item to work. Now we can add the second item only if search phrase isn't nil.

Make some space if search phrase with braces URL query items append URL query

item name and value name will be quotes query value will be search phrase.

Now we can pass the new URL query items in our URL.

This successfully puts the search query into our URL only if it's needed.

Unfortunately, we can't see the search URL in action just yet, but we are getting closer to adding the feature.

We'll continue the next video. Thank you

for watching.

View models in Swift UI are surprisingly controversial. That's because Swift UI

controversial. That's because Swift UI was designed with state management baked right into the view. In my experience as the app grows and networking calls

come into play, view models can help manage responses and keep the UI focused. We already have a view model in

focused. We already have a view model in our current app. However, this view model is working with the home title detail and upcoming screens. To keep

things better organized, we can make a new one to just focus on the search screen. Hit command N. Swift file. Next.

screen. Hit command N. Swift file. Next.

Search view model.

Great.

Like our first view model, this one also needs to observe changes to its properties and communicate them. To do

this, we can type at observable class search view model with braces.

We're using a class here because it's a reference type, meaning changes to its properties reflect everywhere it's used.

We've used enums to keep track of the different fetching states so far in the course. For this view model, we'll keep

course. For this view model, we'll keep it simple and start with an optional property in case an error occurs.

private set bar error message colon string question mark

private set allows only this file to set the value of error string while still allowing others to read it. We'll also

need an array of titles to store the results.

private set bar search titles colon bracket title

equals empty brackets.

Making the array empty ensures it's ready to use and won't need to be unwrapped later if something fails. The

last property we need is an instance of our data fetcher to make the API calls.

private let data fetcher equals data fetcher.

It's completely private here as no other file will need access to it. Let is also used because it won't change. Our

properties are ready. We can now make our function bunk get search titles

by media colon string or title colon string async with braces.

Media will change depending if you are in a TV search or movie search.

Title is a search term the user will enter in the search bar.

Async is used as this function will be making a network call. Inside our

function, write a do catch block.

Let's go reverse and fill out the catch block first. Print error

block first. Print error error message equals error.lo

description.

This prints the error to our log and assigns a user friendly message to our error message. We'll display the error

error message. We'll display the error message in our UI later. Inside the dup block, error message equals nil.

If title is empty search titles equals try awaits

data fetcher fetch titles for media by trending.

We first clear out any error messages.

Title is going to be what the user types into the search bar. If the search bar is empty, we'll return a trending list of movies or TV shows depending on what

the user is searching. After the if statement, type else with braces. Before

we can fill out this else statement, we do need to make an adjustment. Open data

fetcher.

Currently, our fetch titles function doesn't have a way to add a search term.

We can fix this by adding another parameter. After string, add a comma

parameter. After string, add a comma with title colon string question mark equals nil.

This creates an optional parameter called title and assigns nil if it's not used. Title will be how we pass our

used. Title will be how we pass our search term. With our new parameter, we

search term. With our new parameter, we just need to adjust the build URL call.

Add a comma after type search phrase colon title.

Our build URL function was made to complete the search URL only if search phrase is not new. With this adjustment

go back to search view model.

Inside the else block, search titles equals try await data fetcher fetch titles for by width

or will be the media pass in the arguments.

We can pass search in for by width will be title.

In hindsight, it would probably be better to group our parameters with enums. If you're working with a team they most likely wouldn't know to pass in the string search or trending for the

different API calls. Just some feedback for our own code. This completes our search view model. All that's left is to wire it up to the UI. We'll tackle this

in the next video. Thank you for watching. Delaying an API call or

watching. Delaying an API call or debouncing is a great way to build an efficient search feature. Using this

method can help prevent unnecessary requests every time user types. In this

video, we'll demonstrate how to complete an async search bar using debouncing.

With our view model ready, we can now hook it up to our view. Open search

view.

Make a new line after search text.

Private blatant search view model equals search view model. This view model will hold our titles and error if there is

one. Let's complete the search bar logic

one. Let's complete the search bar logic first. Scroll down.

first. Scroll down.

After searchable, add task. Select the

one with ID and action.

For ID, add search text.

Tab. Press enter to complete the closure. This task modifier will rerun

closure. This task modifier will rerun the task inside anytime search text changes. Search text is bound to the

changes. Search text is bound to the search bar. Therefore, anytime the user

search bar. Therefore, anytime the user types, a new API search will trigger.

Inside the code block, type try question mark08 ask.

4 milliseconds 500. This adds half a second delay after

500. This adds half a second delay after the user stops typing. Without the

delay, our API will get called after every keystroke, which isn't ideal. Make

some space.

If task is return here, we exit early if the task was cancelled to avoid running outdated

searches. That can happen if the user

searches. That can happen if the user keeps typing after stopping causing the text to change midtask.

Now we can add our API call await search view model dot get search titles by search

by movies question mark movie colon TV

or search text. We use a wait because the function is asynchronous meaning it runs in the background.

The call will look for either a movie or TV show depending on the mode with the search text.

This completes our search bar logic. We

also need to add the API call when toggling between a movie or TV search.

Highlight and copy line 51.

Scroll up to the trailing toolbar icon.

Add a task block after toggling the boolean.

Paste the API call in it.

The task block allows us to call an asynchronous function inside a place that usually doesn't support a wait. In

our case, inside a button tab.

Just a couple of more things before we can test. Scroll up.

can test. Scroll up.

Delete the titles inside for each.

Replace instead with search view model search titles.

We can use these titles as they will get populated with our API calls. Don't

forget to delete the titles property as our sample data is no longer needed.

The last thing is to display our error if one happens. The best place to have this error is right before lazy B grid inside the scroll view. Add a couple

lines before then.

If let error equals search view model dot error message inside the braces text error

dot foreground style red padding dot background

dot ultra then materialclipip shape direct corner radius 10. We added some styling to our error

10. We added some styling to our error text. If let is great here, as if no

text. If let is great here, as if no error exists, the view will get drawn like normal. That completes this view

like normal. That completes this view and we're ready to test. Please update

your API keys inside APIconfig.json JSON

and hit command R.

Our app is up and running. Click on the search tab.

Nice. Our grade view populates with default titles. I'll try to search Harry

default titles. I'll try to search Harry Potter.

Good stuff.

Let's cancel.

and some default movies pop back up.

Toggle for a TV show.

Harry Potter now returns TV shows.

I'll search instead for one of my favorite TV shows Breaking Bad.

There it is. I do want to show how the error looks like offscreen. I'll delete

the movie database API key and run the app again.

The app is back up with no API key and we have an error on the home tab. We

didn't install this error and it shows navigating to search.

The error looks much better here. It

scrolls up and down with our view.

I do want to talk about some ways this screen can improve. To wrap up, if we search for something that doesn't give results, an error saying no search

results would be nice. In addition, a default image when the titles don't load would be better. Right now, we just have a progress circle that doesn't stop

spinning when this happens.

We won't be adding these features, but if you want to try, please feel free.

Navigation is all that's left to finish this section and that will be the topic of the next video. Thank you for watching.

Our search screen is looking great, but still needs navigation to be complete.

We've seen how to use both navigation path and navigation link to complete this. Out of the two, let's use

this. Out of the two, let's use navigation path for this screen.

Navigation links are easier but come with performance issues. If you're just joining us, the starter code for this video is in the description.

Let's start in search view.

Make a new line after search view model at state private bar

navigation path equals navigation path.

This variable will allow the view to track and control navigation history.

Inside navigation stack, add parenthesis path colon dollar sign navigation path.

This binds the stack's navigation state to our navigation path, letting us push and display views.

Scrolling down we need to append a title to our navigation path when it's clicked. One

way is to add a tap gesture to our async image. Make a new line after frame

image. Make a new line after frame dot on tap gesture.

Navigation path append title.

We just need to tell the path where to go when there's a title object.

Scrolling further down make a new line after task dot Navigation destination or

destination in four type title self tab. Press enter to complete the

tab. Press enter to complete the closure.

The closure variable can be title inside the braces title detail view title title.

When the title strruct is passed into our navigation path, the view will navigate to title detail view passing in the tab title. We're ready to run.

Please update your API keys for the movie database and the YouTube API inside API config.json.

Press command R to run.

Our app is running. Navigate to search.

Click on the title.

Perfect. Our title detail screen is working.

This completes the search screen. To

wrap up, I would like to add a button to the title detail screen for the final section.

Open title detail view.

Make some room after title overview button with braces.

label inside label text constants dot download string dot ghost button.

We'll just keep the same button style already made earlier.

I do want the button to be in the middle of the screen. Before the button type hstack with braces.

Go to spacers.

Cut the button and paste it in between.

This will center the button. Press

command R to run.

App is running. Click on any title and our button is showing correctly.

This completes our async search bar.

We've come a long way with this app and I want to thank you all for the support throughout this journey. I'll see you in the final section. Swift Data.

Swift Data is Apple's modern framework for managing data offline. It's

efficient, lightweight, and easy to use.

In our app so far, we display data primarily from the movie database API.

The goal in this section is to save and delete titles in the download tab. If

you're just joining us, the starting code is in the description so you can code along. The first step in this

code along. The first step in this journey is to get our title model swift data ready. Starting in title.sswift

data ready. Starting in title.sswift

we can replace import foundation with swift data.

Foundation wasn't used in this file and the swift data import allows us access to the library.

The tile strct is what will be saved in our data. Therefore, it needs to be

our data. Therefore, it needs to be marked with at model.

To properly use this macro, change strruct to class.

At model converts a swift class into a stored model that's managed by Swift data. There are a few errors that happen

data. There are a few errors that happen with this change. The first of which is because in it isn't used. Make some room after the poster path property. Type in

it and press enter to autocomplete.

Adding init tells Swift Data exactly how to create our class from scratch.

Without it, Swift data can't properly initialize all sort properties when loading the model from storage.

The next error is because our model isn't conforming to the decodable protocol. Scroll down.

protocol. Scroll down.

Add a couple of new lines after in it.

Required in it.

Wait a few seconds and autocomplete if it adds a decoder.

Required in it tellswift data how to create our model from the decoded JSON data. Since we also change the strct to

data. Since we also change the strct to a class, required init make sure any subclass can be decoded the same way.

Before we complete the required init function, coding keys are needed to properly match the JSON's object property to ours. Right above required

in it, make some space enum coding keys colon coding key with braces. inside

coding keys. Case ID, case title case name case overview

case poster path inside required init let container equals try decoder.container

decoder.container keyed by holding keys do self. This line

creates a container that lets us access the JSON data using the keys defined encoding keys. Now that we have our

encoding keys. Now that we have our container, we can try to decode and initialize our properties.

ID equals try container.deode

if present int self or key do ID. All our properties are optionals because there might be a

chance the server data returns nil. In

this case, I found decode if present is a safe way to continue. We just need to complete this for the rest of our properties.

Title equals try container decode if present string self for key.title.

key.title.

Copy this line and paste it three times. One, two

three. Replace the rest of the properties.

Name dot name overview dot overview and poster path.poster

path.

Our model is Swift Data ready. The final

step is to add a model container to our app. Open blossom app.sswift.

app. Open blossom app.sswift.

At the top import swift data after the window group closing brace model container

or title.

This creates a Swift data container that knows how to store and manage title objects. It'll handle saving, loading

objects. It'll handle saving, loading and keeping our model data in sync with the database. With this, our model is

the database. With this, our model is ready. We'll put it to use in the next

ready. We'll put it to use in the next one. Thank you for watching.

one. Thank you for watching.

Saving and displaying data is a critical step for any app managing data. Lucky

for us iOS developers, Swift Data makes these actions very simple. We'll

showcase this in today's video.

There are two places we can save titles in the app. The first is the download button in our home tab. That's a good place to start. Open home view.

Make a new line after title detail path.

Type at environment parenthesis backslash dom context var model context.

This grabs our connection to Swift data.

With it, we can insert, delete, and save data anywhere in this view. Scroll down

to our download button.

Inside the braces, add model context.insert

context.insert view model hertitle.

Try question mark model context.save.

Here we insert the hero title into Swift data and then try to save it. Adding

some kind of feedback would be a good idea, but this works for now. The second

place we save is inside our title detail view. Let's navigate there.

view. Let's navigate there.

Make a new line after view model at environment backslash.model context

backslash.model context bar model context. Before we write the save logic, it's important to think about how our API data is structured.

The movie database API has both movies and TV shows. For movies, it uses the property title. As for TV shows, it uses

property title. As for TV shows, it uses a property name. That's why we include the title name competed property in this view. It checks which property isn't nil

view. It checks which property isn't nil and returns it. With this in mind, we shouldn't save the title to our database as is. This can result in a database

as is. This can result in a database where the name can be in two places.

When we try to sort, a nightmare can occur. A simple solution is to modify

occur. A simple solution is to modify the data before saving it. Scroll down

to the download button.

Inside the braces, type let save title equals title.

Save title.title title equals title name model context.insert

model context.insert save title try question mark model context.s save

by putting the title name and title. We

make sure the title is in one place.

It's odd to see that let was used instead of bar here. Remember that title is now a class not a strruct. Let only

locks the reference, but the properties inside are still mutable. Therefore, let

is okay here. It's worth mentioning that this wasn't needed in the hero title save. That's because hero title will

save. That's because hero title will only show different movies. As we

discussed, movies already have their name in title. The save feature is complete. Before testing, we need to

complete. Before testing, we need to display the data saved. Let's make the last file of this project with command

N. Swift UI view. Next.

N. Swift UI view. Next.

Download view. Create.

After import Swift UI, add import Swift data. Make some space before the body

data. Make some space before the body at query bar saved titles colon bracket title.

This line tells Swift that fetch all the saved titles. It also updates the list

saved titles. It also updates the list automatically whenever the database changes. Inside the body, we can delete

changes. Inside the body, we can delete hello world. Add a navigation stack.

hello world. Add a navigation stack.

Then a quick logic check. If save titles that is empty text no down loads

padding dot font title 3bold.

It's a good idea to display some kind of message if the list is empty. That way

the user doesn't just see an empty screen. Add an else after the if

screen. Add an else after the if statement.

We'll reuse our vertical list view to display the save titles.

One more thing before we test open content view.

Replace download string with download view.

Please update both your API keys for the movie database and the YouTube APIs in API config.json.

API config.json.

Press command R to run.

Our app is up and running. Navigate to

download.

The empty list message is showing correctly. Go back to the home tab.

correctly. Go back to the home tab.

Press download on the hero title.

Back to download and our title is showing because we reused the vertical list view.

Navigation is already included. Let's go

back home and select the trending movie.

Download that one.

Back to download.

All good. Everything is working so far.

There are a few things left to do, like being able to delete, not allowing duplicates, and sorting. We'll tackle

all that in the next video. Thank you

for watching. Deleting data is just as important as saving in any app. Users

will most likely have data that they'll want to delete at some point. Swift data

makes this action insanely easy. In this

video, we'll demonstrate how we can accomplish deleting as well as sorting in Swift Data. I would like to keep things Apple native and have the delete button as a list swipe action. Let's

open a vertical list view to get started.

We have to remember that this view is used for both our downloads list and upcoming movies list. This is important because we don't want to allow the swipe action in the upcoming list. To help

keep things separated, we can add another property. Make a new line after

another property. Make a new line after titles. Let can delete colon bull. When

titles. Let can delete colon bull. When

this view is called, we can set can delete to true or false. Helping us with our logic. We also need the model

our logic. We also need the model context to make changes to our database at environment

parenthesis backslash domod context bar model context before moving on. Scroll down to fix the error.

Set the preview boolean to true.

Scrolling back up after the label closing brace, add dot swipe actions select the one with edge and content.

Dot swipe actions allows us to define an edge either leading or trailing. Leading

will show at the front of the items trailing at the end. For this action, go ahead and put trailing. Tab over and hit enter. I'll scroll down a bit. Inside

enter. I'll scroll down a bit. Inside

our code block, we can first check if deleting is allowed. If can delete with braces inside here, make a button

with a label.

Our label will be image system name with quotes. Inside the quotes, hit command

quotes. Inside the quotes, hit command shift L. Look up trash. Choose the first

shift L. Look up trash. Choose the first one.

Add a tint modifier.

Let's make it red. So far so good.

Inside the put in logic, we need to delete the title. Model context dodelete title

try question mark model context.save.

This completes our swiping logic. There

are a few errors to address before testing. Open upcoming view.

testing. Open upcoming view.

Click on the error. Fix

false.

Lastly, go to download view.

Click on the error.

True.

Please update your API keys inside API config.json.

config.json.

Hit command R to run.

Our simulator is up. Click on download.

Swipe the first title.

It looks like I accidentally put the swipe action on leading. Easy fix. Go to

a vertical list view.

There it is. Switch it to trailing.

Rerun the app.

We're back. My apologies. Let's go to download.

Swipe to delete the first item.

Close the app.

Reopen back to download.

Looks good. We do need to check our upcoming tab.

Try to swipe.

Nice. We're not able to. Just a couple of more things left. I would like no duplicate titles in our data. That's an

easy change. Open title.swift.

before v id type at attribute dot unique.

This macro makes sure that each title saved in swift data has a unique ID.

Therefore, if two titles have the same ID, saving won't be allowed, preventing duplicates.

I would also like to sort the list in alphabetical order. For this, open

alphabetical order. For this, open download view.

Add a parenthesy after query sort colon backs slashtitle.title.

This macro will sort our swift data titles alphabetically by the title property. Remember when we save our

property. Remember when we save our titles we make sure all names go into the title property. Let's run this code and test it.

Our simulator is up. Try to save the hero title a few times.

Go to download and it only saved once in home. Download a few titles so we can

in home. Download a few titles so we can test the sorting.

Get some TV shows.

One more TV show.

Back to download.

Looks like it's in working order. We're

all set with these features. There is a small bug left in the app. We'll tackle

it in the next one. Thank you for watching. Bugs are a natural part of

watching. Bugs are a natural part of development. Where there's code, bugs

development. Where there's code, bugs can and will appear. We combat this by testing our apps and trying to catch bugs before production. At this stage

our app has all the features we wanted to add. However, if we go to the

to add. However, if we go to the upcoming tab, click on the first title download,

the screen goes white. It's important to note this bug only happens on the upcoming and download tab. The common

denominator of these tabs is that they were made with navigation links instead of navigation paths. Another reason not to use navigation links in production.

The best solution is to rewire our upcoming and download tabs to use paths.

We have seen this a few times in the course already. I challenge you to do

course already. I challenge you to do this on your own and test your skills.

Use search view as a guide. For our

purposes, when the double button is pressed, we'll dismiss the detail screen. This is definitely a workaround

screen. This is definitely a workaround to show different options, but using navigation paths is a true solution. To

start, open title detail view.

Make some space before the title property at environment open parentheses backslash.ismiss

bar dismiss.

This property provides access to the current views dismiss action. We can

call dismiss to culture sheet or navigation destination when needed.

We'll place this in our download button after everything saves. Scroll down.

After model context.save

dismiss.

Please update your API keys in APIcon.json.

APIcon.json.

Press command R to run.

Our simulator is up and running. Go to

upcoming.

Click on the second title.

Download.

Nice. No more white screen. To finish

off the iOS 18 version of this app, I want to display errors better. Please

remove the API keys and rerun the app.

The simulator is up and as you can see the home screen error is real sloppy.

Going into the search tab it looks much better.

Let's have all our errors look like this. Inside Xcode, open search view.

this. Inside Xcode, open search view.

Copy the modifiers on our error text.

Open constants.

Scroll down.

Paste the modifiers at the end for now.

Copy and paste the ghost button extension.

Rename the second one error message.

Get the modifiers and paste them inside error message.

With our new extension, we can go to home view.

Scroll down to our error case.

Delete everything inside text.

Type error.loicalize

description and at the dot error message modifier.

I would like this error to be at the center of the screen. We use our geometry reader for the progress view last time. Scroll up to the progress

last time. Scroll up to the progress case.

Copy the frame modifier.

Scroll back down.

Paste it after the error message modifier.

Before moving on, copy both modifiers.

Open title detail view.

Paste the modifiers on the error.

Replace gio with geometry.

We just need upcoming view.

Same thing with the modifiers.

That should be good. Please run the project.

The errors look so much better. That

completes the iOS 18 version of this app and the course. I want to thank you all for the support. We're going to take a break from YouTube and work on our first

paid course. It's going to only use

paid course. It's going to only use Swift data and Swift UI to keep things lean and fast. Thank you again and I'll see you in the next one.

Loading...

Loading video analysis...