Confirmation and Result Interactive Snippets

This post is brought to you by Emerge Tools, the best way to build on mobile.
Recently, on the Superwall blog, I wrote up a guide over interactive snippets. If you haven’t checked that out, I’d go there first. Continuing on that same beat, I wanted to also highlight an important difference in interactive snippets.
There are two types you can provide:
- Result snippets: These show data that you don’t have to confirm (i.e. here’s how much caffeine you’ve had today).
- Confirmation snippets: These show data, but also require you to make some sort of decision before continuing (i.e here’s some coffee I want to order).
Both of them are interactive, and can have buttons or toggles that fire intents. At the API level, they both have the same signatures. However, confirmation snippets request said confirmation inside of the perform
function. From Apple’s sample code:
struct FindTicketsIntent: AppIntent {
func perform() async throws -> some IntentResult & ShowsSnippetIntent {
let searchRequest = await searchEngine.createRequest(landmarkEntity: landmark)
// Kicks off a confirmation snippet here
try await requestConfirmation(
actionName: .search,
snippetIntent: TicketRequestSnippetIntent(searchRequest: searchRequest)
)
// Resume searching...
}
}
And that is where things diverge. To understand that divergence better, consider that interactive snippets are typically dismissed by the user tapping the system provided “Done” button found below them:

All “result” interactive snippets work this way. Alternatively, they can also just swipe it away to yeet it out of there.
But when you need confirmation, the “Done” button isn’t displayed, and instead the system will show contextualized buttons based on the actionName
parameter. In the code above, that was set to “.search”, and so it is — you get a “Cancel” and “Search” button:

And then the intent carries on, here’s the full sample, I’ve edited the comment to demonstrate the flow better:
struct FindTicketsIntent: AppIntent {
static let title: LocalizedStringResource = "Find Tickets"
static var parameterSummary: some ParameterSummary {
Summary("Find best ticket prices for \(\.$landmark)")
}
@Dependency var searchEngine: SearchEngine
@Parameter var landmark: LandmarkEntity
func perform() async throws -> some IntentResult & ShowsSnippetIntent {
let searchRequest = await searchEngine.createRequest(landmarkEntity: landmark)
// The new UI shows here, so you can think of `TicketRequestSnippetIntent`
// As its own confirmation snippet. Once the user is done and they tap search,
// We'll come back right here, and the search engine code will fire
try await requestConfirmation(
actionName: .search,
snippetIntent: TicketRequestSnippetIntent(searchRequest: searchRequest)
)
// This happens after the confirmation of the number of tickets is done
try await searchEngine.performRequest(request: searchRequest)
// And finally, a new snippet is shown
return .result(
snippetIntent: TicketResultSnippetIntent(
searchRequest: searchRequest
)
)
}
}
What can be tricky here is that this flow shown today technically uses three different interactive snippets:
1. First, there’s the ClosestLandmarkIntent
, which returns that landmark view:

2. Then, tapping “Find Best Ticket Prices” takes us to the above code, where TicketRequestSnippetIntent
asks for confirmation on the ticket count:

3. And, it circles back to TicketResultSnippetIntent
to show the results. If you tap “Book Now”, we go back to first step:

I hope that helps clear up when you’d use a “result” interactive snippet versus a confirmation one. Just because Apple refers to result interactive snippets as a result of something, they are very much well-suited to powerful tasks complete with interactivity.
Now go forth, and make all quick, impactful interactions a snippet!
Until next time ✌️