The Intent Result


In the last article we discussed the main entry point into the App Intent world which is the AppIntent protocol. In this article we will break down the perform function of this protocol.
func perform() async throws -> some IntentResult
Now before we get into the App Intent parts of this function I just want to briefly touch on two parts of this function call that don’t relate directly to the App Intent library but maybe new to some people.
async and throws
async
is a keyword used to indicate that a function is asynchronous, meaning it performs work that may take time to complete and doesn’t block the main thread while waiting. When a function is marked withasync
, it can be suspended and resumed later, allowing the app to handle other tasks concurrently. You callasync
functions with theawait
keyword, which pauses the function’s execution until the asynchronous task completes, helping to manage long-running operations without freezing the UI or blocking other operations.Key Points:
Non-blocking: Allows functions to run without freezing the main thread.
Suspension and Resumption: Async functions can pause execution and continue later, optimizing task management.
await
Keyword: Used to call async functions, indicating where execution pauses until the task completes.
It's especially useful for network requests, file handling, and any operation that could delay the main thread in an iOS app.
throws
is a keyword used in function declarations to indicate that the function can throw an error. A function marked withthrows
must handle errors internally or pass them up the call stack for the caller to handle. When calling athrows
function, you use thetry
keyword to catch and manage potential errors, making it clear where failure might occur.Key Points:
Error Propagation: Functions with
throws
pass errors up the call chain if not handled directly.try
Keyword: Used to callthrows
functions, indicating error handling is required.Error Handling Mechanism: Allows handling runtime issues gracefully, like failed network calls or invalid inputs.
This is essential for robust apps, as it keeps unexpected failures manageable and improves overall stability.
Now that those two keywords are out of the way, the next thing to dive into is the return parameter of the perform function.
some IntentResult
What is an IntentResult? Well, IntentResult is a protocol that Apple doesn’t intend for you to implement directly.
Instead of implementing this protocol, use the
ReturnsValue
,OpensAppIn
tent
,ProvidesDial
og
, andShowsSni
ppetView
type aliases on yourperform()
implementation in combination with theresult()
Apple then goes on to give this code example.
func perform() async throws -> some ReturnsValue<Int> & OpensAppIntent {
.result(value: 1, opensIntent: MyOpensIntent())
}
So, what exactly then is the IntentResult protocol doing if we don’t actually implement it. Well, it guarantees that anything you return has to conform to IntentResult. This allows you to pick and choose which protocols you want to return (like in the previous example) as long as that protocol inherits from the IntentResult protocol.
some ReturnsValue<Int> & OpensAppIntent
Something with this return signature would conform to both ReturnsValue and OpensAppIntent while still maintaining original adherence to
func perform() async throws -> some IntentResult
and that is because both of those protocols inherit from IntentResult
The IntentResult also defines a bunch of static functions name result that seem to just return self, but that have a whole but of signatures. A few examples include:
static func result() -> Self where Self == IntentResultContainer<Never, Never, Never, Never>
static func result<Value, OpensAppIntent, Content>(
value: Value,
opensIntent: OpensAppIntent,
dialog: IntentDialog,
view: Content = EmptyView()
) -> Self where Self == IntentResultContainer<Value, OpensAppIntent, _SnippetViewContainer, IntentDialog>, Value : _IntentValue, OpensAppIntent : AppIntent, Content : View
From these two examples you can see that the static result function returns Self where Self is a IntentResultContainer. So, if we were to look at a very simple perform function we may see something like this:
func perform() async throws -> some IntentResult {
return .result()
}
This would mean that perform() is returning an IntentResultContainer<Never, Never, Never, Never>. Which would be an IntentResultContainer that doesn't have a Value, OpensAppIntent, _SnippetContainer, or an IntentDialog. As another example we could have:
func perform() async throws -> some ReturnsValue<Int> & ProvidesDialog & ShowsSnippetView {
return .result(value: 23, opensIntent: self , dialog: .init("Dialog"), view: Text("Snippet"))
}
This is a perform function that returns every possible option. It returns a value of type Int with value 23, an opensIntent of self (meaning it would just open itself again), a dialog that just says "dialog" and a SwiftUI Text view that just says "Snippet"
Subscribe to my newsletter
Read articles from Nicholas Hartman directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
