接入系统级 AI 时代的必修课。让你的 App 与 Siri、Spotlight、快捷指令深度集成,提供智能化用户体验。
1. Apple Intelligence 概述
Apple Intelligence 是 Apple 在 iOS 18/macOS 15 推出的端侧 AI 系统,让 App 能够:
- 与 Siri 深度集成
- 出现在 Spotlight 搜索中
- 被快捷指令调用
- 支持 Action Button 触发
- 在控制中心显示
2. App Intents 框架
App Intents 是定义 App 能力的现代化框架,取代了旧的 SiriKit Intents。
基础 Intent
import AppIntents
struct OpenArticleIntent: AppIntent {
// 显示名称
static var title: LocalizedStringResource = "打开文章"
// 描述
static var description = IntentDescription("打开指定的文章")
// 参数
@Parameter(title: "文章标题")
var articleTitle: String
// 执行逻辑
func perform() async throws -> some IntentResult {
// 打开文章的逻辑
ArticleManager.shared.open(title: articleTitle)
return .result()
}
}
带返回值的 Intent
struct GetWeatherIntent: AppIntent {
static var title: LocalizedStringResource = "获取天气"
@Parameter(title: "城市")
var city: String
func perform() async throws -> some IntentResult & ReturnsValue<String> {
let weather = try await WeatherService.fetch(city: city)
return .result(value: "当前温度: \(weather.temperature)°C")
}
}
打开 App 的 Intent
struct OpenAppIntent: AppIntent, OpenIntent {
static var title: LocalizedStringResource = "打开我的应用"
@Parameter(title: "目标页面")
var target: NavigationTarget?
func perform() async throws -> some IntentResult {
// 导航到指定页面
await NavigationManager.shared.navigate(to: target)
return .result()
}
}
// 定义导航目标
enum NavigationTarget: String, AppEnum {
case home = "首页"
case settings = "设置"
case profile = "个人中心"
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "页面")
static var caseDisplayRepresentations: [NavigationTarget: DisplayRepresentation] = [
.home: "首页",
.settings: "设置",
.profile: "个人中心"
]
}
3. App Shortcuts
让 Intent 在系统中可被发现。
定义 Shortcuts
struct MyAppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: OpenArticleIntent(),
phrases: [
"打开 \(.applicationName) 的文章",
"在 \(.applicationName) 中查看 \(\.$articleTitle)",
"用 \(.applicationName) 阅读文章"
],
shortTitle: "打开文章",
systemImageName: "doc.text"
)
AppShortcut(
intent: GetWeatherIntent(),
phrases: [
"用 \(.applicationName) 查天气",
"\(.applicationName) \(\.$city) 天气怎么样"
],
shortTitle: "查询天气",
systemImageName: "cloud.sun"
)
}
}
注册 ShortcutsProvider
在 App 入口注册:
@main
struct MyApp: App {
init() {
// 更新 Shortcuts
MyAppShortcuts.updateAppShortcutParameters()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
4. 参数类型
基础类型
@Parameter(title: "数量")
var count: Int
@Parameter(title: "名称")
var name: String
@Parameter(title: "日期")
var date: Date
@Parameter(title: "启用")
var isEnabled: Bool
可选参数
@Parameter(title: "标签", default: nil)
var tag: String?
枚举参数
enum Priority: String, AppEnum {
case low, medium, high
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "优先级")
static var caseDisplayRepresentations: [Priority: DisplayRepresentation] = [
.low: "低",
.medium: "中",
.high: "高"
]
}
@Parameter(title: "优先级")
var priority: Priority
实体参数
struct ArticleEntity: AppEntity {
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "文章")
var id: UUID
var title: String
var author: String
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(title)", subtitle: "作者: \(author)")
}
static var defaultQuery = ArticleQuery()
}
struct ArticleQuery: EntityQuery {
func entities(for identifiers: [UUID]) async throws -> [ArticleEntity] {
// 从数据库获取文章
ArticleStore.shared.articles(for: identifiers)
}
func suggestedEntities() async throws -> [ArticleEntity] {
// 返回建议的文章
ArticleStore.shared.recentArticles
}
}
5. Spotlight 集成
CSSearchableItem
让 App 内容出现在 Spotlight 搜索中:
import CoreSpotlight
func indexArticle(_ article: Article) {
let attributeSet = CSSearchableItemAttributeSet(contentType: .text)
attributeSet.title = article.title
attributeSet.contentDescription = article.summary
attributeSet.thumbnailData = article.thumbnailData
attributeSet.keywords = article.tags
let item = CSSearchableItem(
uniqueIdentifier: article.id.uuidString,
domainIdentifier: "com.myapp.articles",
attributeSet: attributeSet
)
// 设置过期时间
item.expirationDate = Date().addingTimeInterval(30 * 24 * 60 * 60) // 30天
CSSearchableIndex.default().indexSearchableItems([item]) { error in
if let error {
print("索引失败: \(error)")
}
}
}
// 删除索引
func removeArticleFromIndex(_ articleId: UUID) {
CSSearchableIndex.default().deleteSearchableItems(
withIdentifiers: [articleId.uuidString]
) { error in
if let error {
print("删除索引失败: \(error)")
}
}
}
处理 Spotlight 点击
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if userActivity.activityType == CSSearchableItemActionType,
let identifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
// 导航到对应内容
navigateToArticle(id: identifier)
}
}
}
6. Siri 语音交互
SiriTipView
显示 Siri 调用提示:
import AppIntents
struct ArticleView: View {
var body: some View {
VStack {
// 文章内容...
// Siri 提示
SiriTipView(intent: OpenArticleIntent())
.siriTipViewStyle(.automatic)
}
}
}
自定义语音响应
struct ReadArticleIntent: AppIntent {
static var title: LocalizedStringResource = "朗读文章"
@Parameter(title: "文章")
var article: ArticleEntity
func perform() async throws -> some IntentResult & ProvidesDialog {
let content = article.content
return .result(dialog: "正在为您朗读: \(article.title)")
}
}
7. 控制中心 Widget
iOS 18 支持在控制中心添加 App 控件:
import WidgetKit
import AppIntents
struct ToggleFeatureControl: ControlWidget {
static let kind = "com.myapp.togglefeature"
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: Self.kind) {
ControlWidgetButton(action: ToggleFeatureIntent()) {
Label("快速操作", systemImage: "bolt.fill")
}
}
.displayName("快速操作")
.description("一键执行常用操作")
}
}
struct ToggleFeatureIntent: AppIntent {
static var title: LocalizedStringResource = "切换功能"
func perform() async throws -> some IntentResult {
FeatureManager.shared.toggle()
return .result()
}
}
8. Action Button 支持
让 iPhone 15 Pro 的 Action Button 调用你的 App:
struct QuickCaptureIntent: AppIntent {
static var title: LocalizedStringResource = "快速捕捉"
static var isDiscoverable = true
// 标记为 Action Button 可用
static var authenticationPolicy: IntentAuthenticationPolicy = .alwaysAllowed
func perform() async throws -> some IntentResult & OpensIntent {
// 打开 App 并开始捕捉
return .result(opensIntent: OpenCaptureIntent())
}
}
9. Focus 滤镜
在专注模式下过滤 App 内容:
import AppIntents
struct WorkFocusFilter: SetFocusFilterIntent {
static var title: LocalizedStringResource = "工作模式"
static var description = IntentDescription("只显示工作相关内容")
@Parameter(title: "显示工作内容")
var showWorkContent: Bool
func perform() async throws -> some IntentResult {
ContentFilter.shared.workModeEnabled = showWorkContent
return .result()
}
}
10. 最佳实践
✅ 推荐
// 1. 使用清晰的 Intent 命名
struct CreateReminderIntent: AppIntent {
static var title: LocalizedStringResource = "创建提醒"
// ...
}
// 2. 提供多种触发短语
AppShortcut(
intent: CreateReminderIntent(),
phrases: [
"用 \(.applicationName) 创建提醒",
"在 \(.applicationName) 添加提醒",
"\(.applicationName) 提醒我 \(\.$content)"
]
)
// 3. 处理错误
func perform() async throws -> some IntentResult {
guard let data = try? await fetchData() else {
throw IntentError.message("无法获取数据")
}
return .result()
}
// 4. 使用 @MainActor 更新 UI
@MainActor
func perform() async throws -> some IntentResult {
// 安全地更新 UI
}
❌ 避免
// 1. 不要执行长时间操作
func perform() async throws -> some IntentResult {
// ❌ 超过 10 秒会被系统终止
await veryLongOperation()
}
// 2. 不要依赖 App 状态
func perform() async throws -> some IntentResult {
// ❌ Intent 可能在 App 未运行时执行
guard let viewController = window?.rootViewController else { return }
}
// 3. 不要暴露敏感信息
struct BadIntent: AppIntent {
// ❌ 不要在 Siri 响应中返回密码等敏感信息
func perform() async throws -> some ProvidesDialog {
return .result(dialog: "密码是: \(password)")
}
}
11. 调试技巧
测试 Shortcuts
- 打开 快捷指令 App
- 创建新快捷指令
- 搜索你的 App Intent
- 运行测试
Siri 测试
# 在模拟器中使用 Siri
# 长按 Home 键或说 "Hey Siri"
# 查看 Intent 日志
xcrun simctl spawn booted log stream --predicate 'subsystem == "com.apple.Intents"'
调试日志
import os.log
struct MyIntent: AppIntent {
static let logger = Logger(subsystem: "com.myapp", category: "Intents")
func perform() async throws -> some IntentResult {
Self.logger.info("Intent 开始执行")
// ...
Self.logger.info("Intent 执行完成")
return .result()
}
}