如何在AppIntent的perform()中检测macOS快捷指令的取消操作?
问题
我正在测试一个简单的AppIntent,它每秒从1计数到10,代码如下:
import AppIntents struct Loop10SecCount: AppIntent { static var title: LocalizedStringResource = "Loop 10 second count" static var description = IntentDescription("For debugging", categoryName: "Debug") func perform() async throws -> some IntentResult { for i in 1...10 { print("loop \(i)") try await Task.sleep(for: .seconds(1)) if Task.isCancelled { return .result() } } print("Done") return .result() } }
我的开发流程是在Xcode中运行应用,单独打开macOS快捷指令App,使用应用提供的该AppIntent,同时在快捷指令App和Xcode控制台查看进度。当我中途在快捷指令中点击停止按钮时,快捷指令App的进度停止,但Xcode控制台仍会打印到10。我原本期望此时Task.isCancelled变为true,perform()会在打印loop 5后终止,但实际perform()仍继续执行。请问如何检测快捷指令的取消操作?
解决方案
AppIntent的perform()方法不会因快捷指令停止自动取消当前Task,需要通过IntentExecutionContext监听取消状态。以下是修正后的实现:
import AppIntents struct Loop10SecCount: AppIntent { static var title: LocalizedStringResource = "Loop 10 second count" static var description = IntentDescription("For debugging", categoryName: "Debug") func perform() async throws -> some IntentResult { // 获取当前意图的执行上下文 let context = await intentExecutionContext for i in 1...10 { // 循环开始前检查是否已取消 guard !context.isCancelled else { print("Cancelled at loop \(i)") return .result() } print("loop \(i)") // 处理sleep过程中的取消事件 do { try await Task.sleep(for: .seconds(1)) } catch { // Task.sleep被取消时抛出CancellationError print("Cancelled during sleep at loop \(i)") return .result() } } print("Done") return .result() } }
核心说明
intentExecutionContext是AppIntent提供的专属上下文,其isCancelled属性会在快捷指令点击停止时同步变为true,这是检测取消的可靠依据。- 原代码中的
Task.isCancelled仅检查当前Task的取消状态,但系统不会因快捷指令停止而自动取消perform()所在的Task,因此无法触发预期的终止逻辑。 - 若需要响应更复杂的取消场景,可通过监听上下文的
objectWillChangepublisher实现:
func perform() async throws -> some IntentResult { let context = await intentExecutionContext let cancellable = context.objectWillChange.sink { _ in if context.isCancelled { print("Cancelled via context publisher") // 执行清理操作 } } defer { cancellable.cancel() } for i in 1...10 { guard !context.isCancelled else { return .result() } print("loop \(i)") try await Task.sleep(for: .seconds(1)) } print("Done") return .result() }
内容的提问来源于stack exchange,提问作者Hlung
相关产品推荐
相关产品推荐

