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

如何通过Swift AppIntents/Shortcuts+SwiftData实现Siri多字段录入保存记录

问题

尝试配置Shortcuts与AppIntents,实现用户召唤Siri后弹出对话框,创建包含两个及以上SwiftData模型属性的新记录。目前已完成Siri打开应用并跳转至添加记录页面的开发,但无法捕获Siri对话中的用户回复并保存到新记录中。现有代码如下:

@Model
class Thing {

    var name: String = ""
    var comment: String = "no comment"
    var recordType: String = "0"
    var count: Int = 0

    init(name: String, comment: String, recordType: String, count: Int) {
        self.name = name
        self.comment = comment
        self.recordType = recordType
        self.count = count
    }
    
}//class

struct CreateNewThing: AppIntent {
    
    static var title: LocalizedStringResource = "Create New Thing"
    static var description = IntentDescription("Opens the app and moves to the Add Thing screen.")
    static var openAppWhenRun: Bool = true

    @AppStorage("navigateToThingAddView") private var navigateToThingAddView: Bool = false

    @MainActor
    func perform() async throws -> some IntentResult {
        navigateToThingAddView = true
        return .result()
    }
}//create new thing

struct ThingAddDialog: AppIntent {

    static var title: LocalizedStringResource = "Add Thing Dialog"
    static var description = IntentDescription("Prompt the user for a name and comment for a new Thing.")
    
    static var openAppWhenRun: Bool = true
    
    @Parameter(title: "Name", default: "")
    var name: String

    @Parameter(title: "Comment", default: "No comment")
    var comment: String

    @MainActor
    func perform() async throws -> some ProvidesDialog & ShowsSnippetView {
        // Logic to navigate to the Add Thing dialog view
        //navigateToThingDialog = true

        let dialog = IntentDialog(
          full: "What is the name of the new Thing?",
          supporting: "I have opened the Add Thing Dialog.")

         let answer = $name.needsValueError("Provide a name")
        name = answer.description
        
        saveThing(name: name, comment: comment)
        return .result(dialog: dialog)

    }
    
    @MainActor
    private func saveThing(name: String, comment: String) {

        let newThing = Thing(name: name, comment: comment, recordType: "0", count: 0)
        
        // Add newThing to SwiftData 
        let container = try! ModelContainer(for: Thing.self)
        container.mainContext.insert(newThing)
        
        try? container.mainContext.save()
    }
}

struct ThingShortcuts: AppShortcutsProvider {
    static var shortcutTileColor = ShortcutTileColor.navy
    
    static var appShortcuts: [AppShortcut] {
        
            AppShortcut(
                intent: CreateNewThing(),
                phrases: [
                    "Create a new Thing in \(.applicationName)"
                ],
                shortTitle: "Create a Thing",
                systemImageName: "document.badge.plus"
            )
        
             AppShortcut(
                intent: ThingAddDialog(),
                phrases: ["Add Dialog in \(.applicationName)"],
                shortTitle: "Add Thing Dialog",
                systemImageName: "plus.square"
        )
        
    }//var
}//thing shortcuts

使用环境:Xcode 16.1,iOS 18.1

解决方案

核心问题分析

  1. 参数收集逻辑错误:$name.needsValueError() 仅用于参数缺失时抛出错误提示,无法直接获取用户的语音回复。需要通过Intent参数定义和Siri交互流程收集输入。
  2. SwiftData容器使用错误:每次调用saveThing时新建ModelContainer,导致数据无法存入应用主上下文,无法持久化。
  3. App跳转打断交互:openAppWhenRun = true会强制打开应用,打断Siri对话流程,若要纯语音交互需关闭该配置。

修改步骤与代码示例

1. 调整Intent参数与交互逻辑

将name设为必填参数,comment设为可选参数,通过Siri自然语言交互收集输入,或通过IntentDialog逐步引导用户提供信息。

2. 正确复用SwiftData容器

使用应用全局的ModelContainer,而非每次新建。

3. 修正保存逻辑

确保参数收集完成后再执行SwiftData的插入与保存操作。

修改后的完整代码:

@Model
class Thing {
    var name: String = ""
    var comment: String = "no comment"
    var recordType: String = "0"
    var count: Int = 0

    init(name: String, comment: String, recordType: String, count: Int) {
        self.name = name
        self.comment = comment
        self.recordType = recordType
        self.count = count
    }
}

// 保留原有的跳转页面Intent(若需要)
struct CreateNewThing: AppIntent {
    static var title: LocalizedStringResource = "Create New Thing"
    static var description = IntentDescription("Opens the app and moves to the Add Thing screen.")
    static var openAppWhenRun: Bool = true

    @AppStorage("navigateToThingAddView") private var navigateToThingAddView: Bool = false

    @MainActor
    func perform() async throws -> some IntentResult {
        navigateToThingAddView = true
        return .result()
    }
}

struct ThingAddDialog: AppIntent {
    static var title: LocalizedStringResource = "Add Thing Dialog"
    static var description = IntentDescription("Create a new Thing by providing name and comment via Siri.")
    
    // 无需打开应用,纯Siri交互
    static var openAppWhenRun: Bool = false
    
    // 将name设为必填参数,去掉默认值
    @Parameter(title: "Name", description: "The name of the new Thing")
    var name: String

    @Parameter(title: "Comment", description: "Optional comment for the Thing", default: "No comment")
    var comment: String

    @MainActor
    func perform() async throws -> some IntentResult & ProvidesDialog {
        // 双重验证name参数
        guard !name.isEmpty else {
            throw $name.needsValueError("Please tell me the name of the new Thing.")
        }
        
        // 保存数据
        try saveThing(name: name, comment: comment)
        
        // 回复用户操作完成
        let dialog = IntentDialog(full: "Created a new Thing named \(name) with comment: \(comment)")
        return .result(dialog: dialog)
    }
    
    @MainActor
    private func saveThing(name: String, comment: String) throws {
        // 获取应用全局的ModelContainer
        guard let container = ModelContainer.shared as? ModelContainer<Thing> else {
            fatalError("ModelContainer not initialized")
        }
        
        let newThing = Thing(name: name, comment: comment, recordType: "0", count: 0)
        container.mainContext.insert(newThing)
        try container.mainContext.save()
    }
}

struct ThingShortcuts: AppShortcutsProvider {
    static var shortcutTileColor = ShortcutTileColor.navy
    
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: CreateNewThing(),
            phrases: ["Create a new Thing in \(.applicationName)"],
            shortTitle: "Create a Thing",
            systemImageName: "document.badge.plus"
        )
        
        AppShortcut(
            intent: ThingAddDialog(),
            phrases: [
                "Add a Thing named \(\.$name) with comment \(\.$comment) in \(.applicationName)",
                "Create a Thing via Siri in \(.applicationName)"
            ],
            shortTitle: "Add Thing via Siri",
            systemImageName: "plus.square"
        )
    }
}

额外配置说明

  • App初始化SwiftData容器:需在App入口处初始化全局容器,例如:
@main
struct YourApp: App {
    let container: ModelContainer
    
    init() {
        do {
            container = try ModelContainer(for: Thing.self)
            // 设置全局共享容器
            ModelContainer.shared = container
        } catch {
            fatalError("Failed to initialize ModelContainer: \(error)")
        }
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(container)
        }
    }
}
  • Siri短语优化:为ThingAddDialog添加灵活的语音短语,支持直接说出名称和评论,Siri会自动解析参数。

内容的提问来源于stack exchange,提问作者JohnSF

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 14:48:12