You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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,因此无法触发预期的终止逻辑。
  • 若需要响应更复杂的取消场景,可通过监听上下文的objectWillChange publisher实现:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 10:42:18