2026-02-10 • 27:10
Explore essential SwiftUI concepts in this video recorded live in Cupertino. Learn the different contextual meanings of “View” and how to think about them in your app. Discover the advantages of built-in views and how to customize your own. And understand how to structure your code with dependencies to work seamlessly with the system.
This session was originally presented as part of the Meet with Apple activity "SwiftUI foundations: Build great apps with SwiftUI”. Watch the full video for more insights and related sessions.
Speaker: Leah Womelsdorf
Part of: SwiftUI foundations: Build great apps with SwiftUI
Downloads from Apple
Transcript
Today, I’ll share concepts that are central to how SwiftUI works. These concepts will help you write better code, and they’ll also set the scene for the rest of today’s presentations. I’ll start by taking a closer look at a term used a lot in SwiftUI view. Then I’ll cover the views that are built into SwiftUI and the logic behind making new custom views. Finally, I’ll cover dependencies and how to structure your code with intention.
In each section, I’ll share the most important concepts for each and how they relate to the bigger picture of writing. Great SwiftUI code. All right. In SwiftUI, the term view is an overloaded term. Depending on the context, it can mean three different things, and this can be a little confusing.
In many frameworks. Vue refers to the user interface and pixels on the screen. In SwiftUI, a view is also a protocol. It’s a description for what you want on the screen. Because each view is a struct. Each instance of a view has a particular value. These three concepts are all related. The pixels on the screen are a result of the description of the view and the actual values of that particular view struct. The description of the view and the values that drive it result in the final pixels on the screen.
These three meanings of view come up in different areas of an app. The description is the code itself. The value is the instance in memory. So SwiftUI can render the pixels, and the pixels on the screen need to show the latest, most accurate information. I’ll go into each of these different meanings of view throughout my presentation. The context is important and it will help you think through problems in your own SwiftUI code. I’ll start off with views as descriptions.
In SwiftUI, views are descriptions with intentional ways to build them. Let’s say I want an image of a cool underwater scene with snorkeling in English. To describe this, I’d write the phrase image of snorkeling underwater. In SwiftUI. I build this description with the view protocol Image is a SwiftUI view, and deep sea is the name of an image, just like my phrase in English. Views describe what I want in SwiftUI.
SwiftUI takes the view and decides what to render on the screen. Like this. Nice photo. All SwiftUI views are descriptions that are written with code. This is true whether they’re built in views or custom views that you write yourself. Image is an example of a built in SwiftUI view.
Views are the building blocks of every app, and the built in views are a great way to get started. There are many built in views in SwiftUI, and each one’s name describes what it produces. For instance, similar to how image visualizes an image. Color is a view that fills the entire frame with a particular color, like purple.
Built in views are powerful because SwiftUI integrates them with the hardware. Here, this purple is actually a context dependent purple color. The actual color values adjust based on the context of the device, like if the phone is in light or dark mode, or whether the sun is shining bright on the screen.
Text visualizes strings like Kona Deep Dive. Like color, text views are optimized for the context of where they exist. SwiftUI draws the string using a font that’s appropriate for the current platform. For larger displays like an iMac, the text view is going to be physically larger than smaller ones, like on an Apple Watch.
Textviews also support Dynamic Type, which is an accessibility feature. Dynamic type lets people adjust the size of visible text on their devices so they can comfortably read it. Here, the phone on the right has a larger system font size. The Textviews in my SwiftUI code automatically scale so that the content is still readable, and this doesn’t require any additional code on my end.
For each view, image, color, and text, the view’s name is a description of what I want, combined with the values of deep Sea purple and Kona Deep Dive. They result in pixels on the screen. Built in views are a great starting point because they’re not just useful as is, they’re also customizable.
Customizing views lets you express the unique personality of your app while leveraging the benefits of Swiftui’s built in views. There are several ways to customize views in SwiftUI. My TextView from before looks all right, but I can make it more memorable. Viewmodifiers are a tool to customize individual views. I’ll dive into some code.
There are many ways to customize text views. For instance, changing the size or the color of the font. That’s something we did a lot in the wishlist app. For now, I’ll start with something simple. I want my text view to visually pop against a bright color. The background modifier sets the background of my view. You can provide any view as a background. I chose the color orange. Background is an example of a view modifier. A view modifier is a method that’s called on a particular view like text, and it returns a new view that contains the original.
The original text view is wrapped in changes from the view modifier, and it produces a new view. I like to think of view modifiers as wrappers that surround views. Together, these two lines of code produce the original string Kona Deep Dive with an orange background. It’s a slightly modified version of the original text view. You can apply multiple view modifiers to create a compounded custom effect.
Padding is another view modifier. It adds points to the edges of the view that it’s applied to. In this case, padding adds points on all four sides of the TextView, and then the orange background fills it in just like background padding wraps everything before it. So all three of these lines of code represents one big view in the view hierarchy.
One final note on Viewmodifiers the order is important. Each viewmodifier only affects the lines of code above it. If I swap the order so that background goes first. The color orange only covers the original text view. The padding shown with the gray dotted box is applied to the rest. Be intentional about the order of your viewmodifiers. If your code isn’t behaving the way you expect, check to confirm that your view is wrapped in an order that makes sense.
While viewmodifiers customize individual views. Composition is a technique to combine multiple views. You can build your own custom views by combining existing ones. Let’s say I want to build one of these rows in the search tab of wish list for Kona Deep Dive. I can use some of my built in views from before.
First, I’ll define my view search row. It’s a struct and it conforms to the view protocol. Then I’ll add the body of my view. This is something that’s required for all views. And I’ll add an image view of the deep sea. And my text view that says Kona Deep Dive.
By default, SwiftUI stacks them vertically. I want them to be side by side, so I’ll add a horizontal stack or an H stack like image and text. H stack is a view, but instead of describing what to render like an image, it describes how to render it. SwiftUI H stacks. Arrange all of the subviews like image and text in a horizontal line.
Later today, Cat will go deeper into the tools and techniques for layout and SwiftUI. I want to call something out even as I make more sophisticated views. The core concept is the same. My search row is a description of what I want, and SwiftUI takes that description to render the pixels on the screen.
Composition makes code easier to read, and once I’ve built my search row, I can use it in other areas of my app with just a single line of code. In my search view, instead of needing three stacks, three images, and three text views. I just used three of the composite search row views, and later on, if I want to change anything in the search row, like make the background purple, I can do it local to that view. This reduces the amount of code I need to write, and makes it easier to read and reason about. Because views are structs. They’re lightweight, so splitting up a view into smaller views like this doesn’t hurt performance.
Just like built in views, custom views are a description of our descriptions of what SwiftUI should draw on screen. The names like Search View are important, but even more so are the views inside of body and the order that they’re listed in. SwiftUI uses the descriptions in your code to draw the pixels on the screen.
Now I want to take this one step further. My search row is pretty close to the intended design, but it’s not quite there. Right now I have three instances of Kona Deep Dive, and I don’t know about you, but I like to switch up my vacations every year. Apps are dynamic and represent data that changes. I need a way to switch this up so I can show the other trips on my bucket list.
This brings me to my last topic dependencies. SwiftUI views are driven by data. Whether that data stays the same, like my image of the deep sea and the string Kona Deep dive. Or that data changes. All of the views I’ve shown today are dependent on data. The pixels on the screen are partially a result of the description like image, and partially of the data like deep sea.
I mentioned earlier that all views are structs, and that means that all views are value types, and every instance of a view has a value. I like to visualize an instance of a view like this, with the name of the view on top, like image and the data like deep sea below.
SwiftUI takes a particular instance of a view, including the data to render the pixels on the screen. Once the pixels are rendered, SwiftUI discards the instance. It doesn’t need it anymore. This is the third meaning of view. A view as a value. Instances of a view. The particular values power the pixels on the screen.
Each view instance exists transiently. They’re short lived. They’re created when they’re needed. Living in swiftui’s memory. And then once the pixels are rendered, the view’s job is done and the instance is discarded. Note that discarding them isn’t a bad or a high stakes thing. Views are lightweight and easy to create.
I like to think of views as templates, able to stamp out the same thing, creating pixels on the screen. Many times these templates represent data that can be flexible and dynamic. For instance, in my app I’ve been focusing on the deep sea, but I also need another image of Kyoto.
This is another instance of a view, but this one has a slightly different value. The name of the image Kyoto is different from deep sea. SwiftUI takes these image views which have different values because of the image names, and it produces different images. Now I’ll take this flexibility and revisit the code for my search row.
Now, instead of hard coding the name of the image and the text, my search row takes arguments for the name of the image and the title of the trip. This version of my search row is more flexible. I can still use it to make the Kona Deep Dive row, but I can use it for other trips too. This gets me closer to the real design.
When I use the new search row in my search view. I provide all of the different images and trip names like Mammoth Blush and Kyoto Mystique. And this is much better. The data is driving the pixels on the screen, and I’m still able to leverage the clean code from composition.
Keep in mind that this implementation of the search view is still a simplification of the final version in wishlist. The actual search looks up the data dynamically and then populates the search rows based on the images and strings entered in the search field. I want to highlight one more thing about these search rows.
Just like the image views from before, each instance of the search row has a different value based on the trip name and the photo name. SwiftUI can easily compare these and notice they’re not equal. After SwiftUI draws the pixels on the screen, it releases the instances of the views. It doesn’t need them anymore because the pixels are rendered. Now, I want to talk a little bit more about how SwiftUI works with dependencies.
For my search row. The description is my view code. Different values of the view result in different pixels on the screen. SwiftUI tracks important values with dependencies, and by important values, I mean the kind of values that could make the pixels seem right or wrong based on the data. Here’s how it works.
The first time that SwiftUI runs body, it keeps track of all of the values it reads with a graph. The first part of this graph is a node for search row. In body, SwiftUI reads the value of photo name for the image view, so it adds a node for photo name and an arrow that points down to search row.
SwiftUI also reads the value of trip name, so it adds a node and an arrow for trip name. Two SwiftUI tracks this because for the pixels to be correct, they need to represent the right photo name and the right trip name. If either of those change. For instance, if I provide a new photo name with. I’m representing with this red dot, then the pixels for the search row would be out of date and SwiftUI would need to draw new ones. Dependencies are like the inputs of a view.
Dependency tracking is one of the reasons that SwiftUI is so efficient at making updates. In an app with a lot of views and a lot of data. It would be extremely inefficient if every time one thing changed, SwiftUI needed to redraw everything. Data changes frequently in apps and this would cause a lot of unnecessary updates.
Instead, SwiftUI tracks dependencies. I’m representing it here with arrows, and they indicate which views depend on certain pieces of data. With this system, when one thing changes, SwiftUI only recomputes and redraws the things downstream. Dependency tracking keeps SwiftUI efficient as things change quickly and often in your apps. This is sophisticated, and there’s great news. You do not have to build this graph yourself.
SwiftUI builds it for you automatically in its dependency graph. It keeps track of the properties that each view reads every time it runs body. This system keeps the graph continuously up to date. Even though you don’t have to build or maintain this graph. There are still some things for you to keep in mind for your code.
Keep views light when building your views, consider what information you’ve told SwiftUI that you need in body, and whether changes to those views should invalidate the entire view. It’s best to keep your views light with minimal dependencies, so SwiftUI only redraws them when it’s important. This doesn’t mean that you can’t have complex views. It means that you should break those complex views down into smaller, simpler pieces like I did earlier with the rows in my search view.
Use the right tools to represent your data and how to communicate changes only when they should change the pixels on the screen. There are a few different ways that a view can depend on data. One way is to pass that data directly into a view like I did earlier, by providing the specific names of images and trips. Another way to create a piece of data is with at state. My colleague Cole will go more into depth into at state and each of the other options for data flow in his presentation, but I want to cover some of the basics.
Properties wrapped with at state. Tell SwiftUI to store that piece of information for the entire lifetime of a view. That’s the entire period of time that the view is in the view hierarchy. I’ll demonstrate with a simple example. in the search tab of wish list. As I start to type in the search bar, the results filter down. When I add the letter J, the most recent items are replaced by my trips and activities that include the search string like hike, Joshua Tree, and Cliff jump.
When I add another letter, the letter O. The results continue to filter down. Out of all of the trips and activities that I’ve added to my app, Joshua Tree is the only item that includes the string j o. The state of my app. The string that I’ve entered into the search field drives the results and the pixels on the screen. I’ll build a simplified example to demonstrate how at state works.
This is a simplified version of the search view, which I’m calling Simple Search. It has the core functionality of the real search view, but some simplifications in the design. There are only two views in the body. I’m using a text field and a TextView. The TextView is just a placeholder that says results here. This is something I do a lot while I’m prototyping. I put placeholders in. I’ll eventually replace it with a custom view that shows the search results.
Text field is another built in SwiftUI view. It’s meant for collecting input. When people tap on it, the keyboard opens and it displays the string that I’ve entered. I provide a starting prompt type here, and then tell SwiftUI to store the string that’s entered in the variable named Search Value. The dollar sign is syntax for a binding, which is another data flow tool that Cole will go into later.
I decorated the property search value with the At state property wrapper. This tells SwiftUI to hold on to the value of search value across updates. The initial value is an empty string. When I tap the text field, the cursor blinks, indicating that I can type as I type the letter J, the value of search value is changed from the empty string to J. SwiftUI.
Hold on to that value and can update the pixels for simple search accordingly. The same thing happens when I add the letter O to keep filtering down towards Joshua Tree. SwiftUI holds on to the value of search string j o so it can make continuous progress across updates.
At state is the simplest way to model information that should stick around for the entire lifetime of your view. For the entire time that I’m in the search tab, my search value will persist so I can keep adding to it. If I navigate away from the tab and then I come back later, the value will start from scratch again with an empty string.
This is just the first step of building the real search view in wishlist. Next, I’d need to replace my placeholder text view with the trips and activities that match the search value string. For now, focus on these aspects of state. At state is a property wrapper in SwiftUI. When a property is marked with at state SwiftUI, SwiftUI knows that it’s a source of truth for my app, and it holds on to that value across updates.
State remains in memory for the entire lifetime of the view. This means that apps can display data that’s dynamic and compounding, like the string typed in the text field. There are multiple ways to structure dependencies in SwiftUI, and Cole will go into each of them later. For now, I want to emphasize one thing invest the time to learn how these tools work. This way, you can ensure that you’re modeling your dependencies to work with SwiftUI.
As I’ve covered today, views are the building blocks of SwiftUI. By understanding more about the different contexts they are used, you can understand not just the framework, but improve the quality of your code. For the rest of today’s presentations. As you explore different areas of wish list, I encourage you to focus on the theory behind these solutions. This way you can apply those learnings to your own apps. From my presentation, keep these things in mind.
Leverage Swiftui’s built in views to create your own custom views. Explore different modifiers to express what’s unique about your app. As you build those views or you revisit existing ones, remember to keep the views lightweight. Consider what data each view should really depend on, and break complex views down into simpler, smaller pieces. By keeping these foundations in mind, you’ll be better equipped to reason about challenges as they come up. Thank you so much for joining me.