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 video analysis...