Get Started With SwiftUI and Xcode UI Testing
Learn how easy it is to get started testing your SwiftUI components
--

Every once and a while, it’s a good idea to just take a moment from working on your projects to take a step back and see what’s currently happening in your field of development. Fortunately, for iOS developers that moment comes (at least) once a year when Apple starts announcing the new features for iOS at WWDC.
One of the major announcements Apple made in 2019 was the addition of SwiftUI to the iOS development toolkit. It’s not meant as a direct replacement of UIKit, but it’s certainly a more modern approach to building user interfaces. It’s one of the biggest changes to user-interface development for iOS in the last couple of years.
With a nice declarative way of creating user interfaces, I wondered if life would get easier when doing test-driven development (TDD). The prospect of not having to go back and forth between the Storyboard, ViewController, and XCTest seemed at least very appealing.
I created a small test project, which would simply show a text label, request some input from a text field, and display a result in a third text field. Not at all rocket science — but just something to get started with. I’ll add functionality incrementally, not all at once, so you can see how your tests evolve as you go along. This mirrors the way normal development occurs: Features get expanded and added over time, and test suites need to be updated accordingly.
Let’s go.
The Test Project
First up, I created a new Single View App from the New Project option dialog. I named it SimpleGreeter
— guess what it’ll do!

Make sure you select SwiftUI for User Interface, and tick the Include UI Tests option. I didn’t tick the Include Unit Tests option in this case due to the limited scale of the application, but it’s easy to add unit tests afterward if you want to.
Once you’ve saved the application to a sensible location, it should look something like this. If the preview isn’t showing, make sure you press the Resume button on the top right of the preview window.

Now, before we start coding the user interface, take a moment and think about what we want to show in the app and how the user can interact (we’re doing this test-driven, right?). For starters, I want to show a text label to provide some instructions and a text field for the user to enter their name.
So switch to the SimpleGreeterUITests.swift
file, and add a test. First of all, remove the tearDown
and testLaunchPerformance
methods since we won’t be using them for this example. Rename the testExample
method to something like testInitialViewState
to indicate this is a method that tests the default state of the user interface. Also, feel free to remove the abundance of comments Xcode generates for you.
To fill in the test method, we’re going to add four assertions:
- Does the text label exist?
- Does the text label have the correct text?
- Does the text field exist?
- Does the text field have the correct placeholder text?
You should end up with something like this:
Now, of course, if you run this test, it’ll fail even though there’s a text label because it contains a different text (“Hello, World!”). So let’s fix that, we’ll go to our ContentView.swift
file and start adding some controls as well as making sure our assertions are true.
We’ll arrange the text label and text field in a VStack
, add some padding, and provide sensible titles and placeholders. The input from the text field we’ll store in a variable called name
. That’s it for now — just focus on the minimum functionality you need to add to make your tests pass. You’ll end up with something like this:
Now run your tests again, and voila! Green across the board. Pay special attention to the @State
annotation for the name
variable. This signals to SwiftUI that every time this value gets updated, the view should update accordingly.
But we’re not done yet, now we want to update it so every time you enter something in the text field, we see a message in a new text label greeting you. So if I enter “Jony” in the text field, we should see a label that says, “Nice to meet you, Jony.” Again, we’ll do this test first. Go back to the SimpleGreeterUITests.swift
file, and add another method that tests whether you can tap on the text field, add some text, and see the correct result.
However, since we’re going to be adding another text label, we can’t simply refer to the text label by using app.staticTexts.element
because this only works if we have exactly one text label on the view. From the documentation:
“Use the
element
property to access a query’s result when you expect a single matching element for the query, but want to check for multiple ambiguous matches before accessing the result. Theelement
property traverses your app’s accessibility tree to check for multiple matching elements before returning, and fails the current test if there is not a single matching element.”
So instead, we’ll need to differentiate between the old text label and the new text label we’re going to add. We can do this by using the accessibilityIdentifier
. This property is provided by the UIAccessibilityIdentification
protocol and the documentation says:
“An identifier can be used to uniquely identify an element in the scripts you write using the UI Automation interfaces. Using an identifier allows you to avoid inappropriately setting or accessing an element’s accessibility label.”
So this is exactly the property that Apple thinks we should use to query our view for the correct element. This avoids (ab)using the accessibilityLabel
(which is used by screen readers) or checking for certain titles or other textual contents that may change or have unexpected values at runtime. The test will look like this:
The interesting part here is we’ll type each letter one by one. The typeText
method accepts a full string, but if you use that, the UI automation will sometimes type too fast and some characters will be omitted resulting in randomly failing tests. Not cool.
Again, this test will fail because we don’t have a label with the correct accessibilityIdentifier
, and we don’t have the correct behaviour implemented. Let’s fix that. We’ll just add a new text label to the bottom of the VStack
in ContentView.swift
, and we’re good to go:
Text("Nice to meet you, \(name).")
.accessibility(identifier: "greetingTextLabel")
Right? Well, no. Because, yes, your new test will pass, but your first test will fail. The app has the unwanted side effect that we now have an extra text field that in its initial state says Nice to meet you, .
Awful!
So we want to fix these things:
- Fix the first test (and update it for the new UI).
- Make sure that whenever there’s no name entered (name is empty), we don’t show any greeting.
To fix the first point, we’ll add an identifier to the first text label, update the test to use this, and add extra assertions for the newly introduced text label — check whether it’s present and if the default state is empty. The updated test will look like this:
Now that we’ve updated the first test, we’ll update the behaviour of the view to match this. We’ll introduce a computed property, greetingText
, that either returns an empty string (if there’s no name entered) or the full greeting text. Here, I’ve used the new Swift 5.1 feature (SE-0255) where if your method only contains one expression, it’s also used as an implicit return. The final ContentView
looks like this:
The final SimpleGreeterUITests
looks like:
Conclusion
In closing, this is just a very brief look at Xcode UI testing using SwiftUI. There’s, of course, much more that can be done to improve the UI, but it shows that with very little effort, you can start using interface testing in your SwiftUI views. I don’t think it’s been any easier up to now to make changes and see them immediately take effect in your app.
It does, however, (as with any TDD) require a bit of discipline and dedication. Also, the Apple documentation in this regard sometimes offers little support, and test failures can occur without clear descriptions of what’s actually causing this. It can be a bit of a struggle, but future you will thank you for it.
If you’re starting a new project or just getting into SwiftUI and you’re not already using UI testing, now’s as good a time as any to start.
As a final note: The use of logic in your views (e.g., greeterText
) should, of course, be avoided as much as possible and is only done here as to not complicate matters for this example project. However, as soon as you start noticing you have application logic in your views, consider switching to a ViewModel-based architecture (and be sure to test that as well).
The final code of the project can be found on GitHub.