Most Useful iOS 15 SwiftUI Features
Rate this tutorial
I'm all-in on using SwiftUI to build iOS apps. I find it so much simpler than wrangling with storyboards and UIKit. Unfortunately, there are still occasions when SwiftUI doesn't let you do what you need—forcing you to break out into UIKit.
I've revisited some of my existing iOS apps to see how I could exploit the new iOS 15 SwiftUI features to improve the user experience and/or simplify my code base. This article steps through the features I found most interesting/useful, and how I tested them out on my apps. These are the apps/branches that I worked with:
Lists are pretty critical to data-based apps. I use
Lists in almost every iOS app I build, typically to represent objects stored in Realm. That's why I always go there first when seeing what's new.
We've all used mobile apps where you swipe an item to the left for one action, and to the right for another. SwiftUI had a glaring omission—the only supported action was to swipe left to delete an item.
This was a massive pain.
With iOS 15, I can replace that popup sheet with swipe actions:
The role of the delete button is set to
.destructivewhich automatically sets the color to red.
When you're presented with a long list of options, it helps the user if you offer a way to filter the results.
The view includes a state variable to store the
searchText(the characters that the user has typed) and a
searchResultscomputed value that uses it to filter the full list of symbols:
Listthen loops over those
searchResults. We add the
.searchablemodifier to add the search bar, and bind it to the
This is the full view:
We've all used this feature in iOS apps. You're impatiently waiting on an important email, and so you drag your thumb down the page to get the app to check the server.
This feature isn't always helpful for apps that use Realm and Realm Sync. When Realm cloud data changes, the local realm is updated, and your SwiftUI view automatically refreshes to show the new data.
However, the feature is useful for the RCurrency app. I can use it to refresh all of the locally-stored exchange rates with fresh data from the API:
In that code snippet, you can see that I added the
.listRowSeparator(.hidden)modifier to the
List. This is another iOS 15 feature that hides the line that would otherwise be displayed between each
Listitem. Not a big feature, but every little bit helps in letting us use native SwiftUI to get the exact design we want.
iOS 15 allows you to render markdown text within a
Textview. If you pass a literal link to a
Textview, then it's automatically rendered correctly:
But, it doesn't work out of the box for string constants or variables (e.g., data read from Realm):
RChat now supports markdown in user messages:
We all know that working with dates can be a pain. At least in iOS 15 we get some nice new functionality to control how we display dates and times. We use the new
In RChat, I want the date/time information included in a chat bubble to depend on how recently the message was sent. If a message was sent less than a minute ago, then I care about the time to the nearest second. If it were sent a day ago, then I want to see the day of the week plus the hour and minutes. And so on.
This preview code lets me test it's working in the Xcode Canvas preview:
It's now trivial to rename the on-screen keyboard's "return" key. It sounds trivial, but it can give the user a big hint about what will happen if they press it.
.onSubmit(userAction)modifier on the password field. If the user taps "go" (or hits return on an external keyboard), then the
userActionfunction is called.
userActioneither registers or logs in the user, depending on whether "Register new user” is checked.
It can be tedious to have to click between different fields on a form. iOS 15 makes it simple to automate that shifting focus.
I use the
.focussedmodifier to bind
focussedFieldto the two fields:
It's a two-way binding. If the user selects the email field, then
focussedFieldis set to
.username. If the code sets
.password, then focus switches to the password field.
This next step feels like a hack, but I've not found a better solution yet. When the view is loaded, the code waits half a second before setting focus to the username field. Without the delay, the focus isn't set:
The final step is to shift focus to the password field when the user hits the "next" key in the username field:
Previously, I've created custom SwiftUI views to make buttons look like…. buttons.
Things get simpler in iOS 15.
Before making this change, I experimented with other button styles:
It's very easy to accidentally tap the "Logout" button, and so I wanted to add this confirmation dialog:
Again, iOS 15 makes this simple.
This is the modified version of the
These are the changes I made:
- Added a new state variable (
- Changed the logout button's action from calling the
logoutfunction to setting
- The dialog title (I didn't override the
titleVisibilityoption and so the system decides whether this should be shown)
- A binding to
isConfirmingthat controls whether the dialog is shown or not
- A view containing the contents of the dialog:
- A button to logout the user
- A cancel button
I'm no designer, and this is blurring the edges of what changes I consider worth adding.
The result looks like this:
Finally, there are a couple of enhancements that are helpful during your development phase.
I'm a big fan of Xcode's "Canvas" previews. Previews let you see what your view will look like. Previews update in more or less real time as you make code changes. You can even display multiple previews at once for example:
- For different devices:
.previewDevice(PreviewDevice(rawValue: "iPhone 12 Pro Max"))
- For dark mode:
For example, this code will show two devices in the preview. The first will be in portrait mode. The second will be in landscape and dark mode:
SwiftUI is very smart at automatically refreshing views when associated state changes. But sometimes, it can be hard to figure out exactly why a view is or isn't being updated.
If I log in and check the Xcode console, I can see that it's the update to
usernamethat triggered the refresh (rather than
There can be a lot of these messages, and so remember to turn them off before going into production.
SwiftUI is developing at pace. With each iOS release, there is less and less reason to not use it for all/some of your mobile app.
This post describes how to use some of the iOS 15 SwiftUI features that caught my attention. I focussed on the features that I could see would instantly benefit my most recent mobile apps. In this article, I've shown how those apps could be updated to use these features.
There are lots of features that I didn't include here. A couple of notable omissions are:
- Adding a toolbar to your keyboards (e.g., to let the user switch between input fields).