An interface to support the graceful cancellation of your app intent’s task.
This protocol adds cancellation support to your app intent types. Use it to let the
system know that you need additional time to perform cleanup operations when your app
intent receives a cancellation notification. To use this protocol, use the
``CancellableIntent/withIntentCancellationHandler(operation:onCancel:isolation:)`` method
to wrap your app intent’s task. With this method in place, the system gives your
code extra time to handle cancellation before the app intent's task ends.
The system can cancel an app intent for the following reasons:
* The app intent didn’t report progress and exceeded its 30-second runtime limit.
* Someone monitoring the progress of the app intent in Siri, Live Activities, or Shortcuts
chose to cancel the task.
Swift provides the standard <doc://com.apple.documentation/documentation/swift/withtaskcancellationhandler(handler:operation:)>
function for tasks to handle cancellation. You can use that function in your app intent’s
``AppIntent/perform()`` method to perform any cleanup tasks if you don’t need to know the
reason cancellation occurred. If you want to know the reason for cancellation, use the
``CancellableIntent/withIntentCancellationHandler(operation:onCancel:isolation:)`` method
that this protocol offers instead. When cancellation occurs, stop your app intent’s current task,
release any allocated memory or resources, save intermediate state as needed, and write
out any relevant diagnostic information related to the cancellation.
The following example shows a payment-processing app intent and the basic code it uses to
perform its task. The app intent’s ``AppIntent/perform()`` method calls the
``CancellableIntent/withIntentCancellationHandler(operation:onCancel:isolation:)`` method to
process the payment and handle cancellation if it occurs. The cancellation handler performs
different tasks based on whether the someone canceled the operation deliberately or the
system timed out.
```swift
enum PaymentMethod: String, AppEnum {
case creditCard
case debitCard
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Payment Method")
static var caseDisplayRepresentations: [PaymentMethod: DisplayRepresentation] = [
.creditCard: "Credit Card",
.debitCard: "Debit Card"
]
}
class PaymentService {
func initiateTransaction(_ id: UUID, amount: Decimal) async throws {
// Initiate payment transaction
}
func authorize(_ id: UUID, method: PaymentMethod) async throws {
// Authorize payment
}
func process(_ id: UUID) async throws {
// Process payment
}
func rollback(_ id: UUID, reason: String) async throws {
// Roll back transaction
}
func cancel(_ id: UUID, reason: String) async throws {
// Cancel transaction
}
}
struct ProcessPaymentIntent: AppIntent, ProgressReportingIntent, CancellableIntent {
static var title: LocalizedStringResource = "Process Payment"
@Parameter var amount: Decimal
@Parameter var paymentMethod: PaymentMethod
private let paymentService = PaymentService()
func perform() async throws -> some IntentResult & ProvidesDialog {
let transactionID = UUID()
return try await withIntentCancellationHandler {
updateProgress(fractionCompleted: 0.0, message: "Initiating payment...")
try await paymentService.initiateTransaction(transactionID, amount: amount)
updateProgress(fractionCompleted: 0.3, message: "Authorizing payment...")
try await paymentService.authorize(transactionID, method: paymentMethod)
updateProgress(fractionCompleted: 0.7, message: "Processing payment...")
try await paymentService.process(transactionID)
updateProgress(fractionCompleted: 1.0, message: "Payment complete")
return .result(dialog: "Payment of \(amount) processed successfully")
} onCancel: { reason in
Task {
switch reason {
case .timeout:
try? await paymentService.rollback(transactionID, reason: "timeout")
case .userCancelled:
try? await paymentService.cancel(transactionID, reason: "user_cancelled")
default:
try? await paymentService.cancel(transactionID, reason: "unknown")
}
}
}
}
}
```
> Important: Design your cancellation handlers to finish their tasks quickly. On all
> platforms except macOS, the system can still suspend the process shortly after cancellation.
> However, supporting this protocol provides additional time compared to app intents that
> don’t support this protocol.
Runs an operation with a cancellation handler that receives a cancellation reason.
- Parameters:
- operation: The closure with the operation to run. Use this closure to run the
code for your app intent’s task.
- handler: The closure to run when cancellation occurs. Use this closure to respond
to the cancellation and perform any required cleanup. The closure receives a parameter
with the cancellation reason and returns no value.
- isolation: The actor isolation for the operation. If you don’t specify a value, the
system uses the current isolation context.
- Returns: The value you return from the `operation` closure.
- Throws: Rethrows any error thrown by the operation.
Use this method to handle cancellation in an app intent’s ``AppIntent/perform()`` method
when you also want to know the reason for cancellation. If cancellation occurs due to a
timeout or someone canceling the operation, the system runs your `handler` and gives you
a little extra time to perform any cleanup. If you don't need to know the cancellation reason,
you can use the standard Swift <doc://com.apple.documentation/documentation/swift/withtaskcancellationhandler(handler:operation:)>
method for tasks instead.