Latest

macOS 原生应用的窗口与导航管理

SwiftUI 提供了 NavigationSplitView、NavigationStack、TabView 等多种导航容器,它们在 macOS、iPadOS 和 iOS 上的行为各有不同,组合方式也不止一种。 这篇文章整理了我在实际开发中对 macOS 原生应用导航管理的观察与总结,希望能帮助你在项目初期做出更清晰的架构选择。 创建三列应用 NavigationSplitView 是天然首选 如果你的应用始终需要三列结构——比如 Notes 的「文件夹 → 笔记列表 → 编辑器」,或 Mail 的「邮箱 → 邮件列表 → 邮件正文」——那么 NavigationSplitView 的三列形态就是最自然的方案: NavigationSplitView { // sidebar } content: { // 中间列 } detail: { // 详情列 } 在这种场景下,系统的原生体验非常稳定,toolbar、标题栏与分栏折叠行为都与系统预期一致,几乎不需要额外干预。 混合列数场景:谨慎切换容器形态 实际项目中更常见的情况是:
廖林

HealthKit|心率与静息心率

在 HealthKit(iOS) 中,与心率(Heart Rate)相关的数据并非单一指标,而是一组彼此关联、可供开发者获取的健康数据类型,主要包括: * 基础心率 * 静息心率 * 步行平均心率 * 心率变异性(HRV) * 以及高心率、低心率和心律不齐等心率异常事件 心率与静息心率 心率异常与系统事件 常见问题 获取心率数据是否对性能有影响? 不会。HealthKit 查询在后台线程进行,不阻塞主线程,并且有内部缓存机制。
廖林

ActivityKit|通过 activityBackgroundTint 设置实时活动卡片背景色调

修改实时活动锁屏卡片的背景,有两种方式: * .activityBackgroundTint() * .containerBackground(for: .widget) 使用 activityBackgroundTint 设置背景色调 设置 Liquid Glass 玻璃效果() 如果要设置 iOS 26 系统默认的液态玻璃效果,只需添加 .activityBackgroundTint(.clear) 即可。 如果不添加这个修饰器并指定 .clear,默认会显示为偏蓝色的背景色,原因未知: 建议在子组件内部设置,例如 PickupActivityLockScreenView 组件内部最外层的 VStack 上设置,在这里统一设置 .activityBackgroundTint(.clear) 或 .background,更加统一。 下面这个 Reddit 帖子中也有讨论这个修饰器: Liquid Glass live activity widget. How? by u/Nilsolivier in
廖林

ActivityKit|在 ViewModel 中统一管理灵动岛启动

问题场景说明 在取件喵 App 中,创建了一个 LiveActivityManager.shared.startPickupActivity() 方法用于启动灵动岛通知。 在项目中,我在两处分别调用了这个方法: 1. 在 PickupViewMode 的 addPickup 方法中,如果添加取件码成功,则调用 startPickupActivity() 方法。 2. 在 AnalyzeImageIntent 这个 Intent 中,我再次根据添加结果调用了 startPickupActivity() 方法。 这不仅导致重复的代码逻辑,而且在 AnalyzeImageIntent 中会启动两次灵动岛,导致灵动岛时不时出现异常。 最佳实践 在主 App 的 ViewModel 中统一管理灵动岛的启用逻辑,addPickup() 方法现在统一处理 Live Activity 启动。 在 Intent 中直接调用 ViewModel
廖林

AppIntent|可以直接调用主 App 中的 ViewModel 方法

一个常见的错误(包括主流 AI 工具)是:Intent 运行独立的 Extension 进程,无法访问主 App 中定义的方法—— 这是错误的。 早期的 SiriKit Intents(iOS 16+)确实是这样,但从 iOS17 开始,新的 SiriKit Intents 运行在主 App 进程内,因此可以直接访问 App 数据,可以访问 ViewModel 单例。 App Intent 能力边界 对于 iOS 17 中新添加的 AppIntent,是这样的: 1. 默认运行在主 App 中,可以直接通过快捷指令或者 Live Activity
廖林

ActivityKit|灵动岛中的图片资源限制

在灵动岛和 Widget 中,对 Assets 资源有一些限制。如果不满足条件,可能无法显示。 资源大小限制 根据官方文档信息,图片必须小于或等于 4kb,才能在实时互动中显示。 但在实践中,我发现即使 PNG 文件达到 117KB 大小,也可以显示。不确定这个限制是否仅对灵动岛 Minimal 和 Compact 模式下生效? PDF 格式测试 使用一张 Figma 导出的 PDF 格式文件测试 * 97KB,无法显示 * 20KB,使用在线工具压缩,仍然无法显示 注意:由于 PDF 是矢量图,在 Figma 中即使使用 Scale 将尺寸调整为 0.25x,导出文件大小也不会变化。 💡使用这个工具来压缩:
廖林

ActivityKit|为灵动岛添加操作按钮

在灵动岛中,支持通过 Button(:intent) 的方式添加可交互的按钮。按钮只能通过 LiveActivityIntent 来执行功能。 常见问题 灵动岛按钮无法执行 AppIntent 在取件喵 App 中,在 Live Activity 视图里点击“我已取餐”按钮无任何反应,Xcode 也没有输出,灵动岛状态不会更新。 原因是 Live Activity 的按钮需要绑定 LiveActivityIntent 才能被系统执行。原实现使用的是 `AppIntent`,导致按钮点击后不会触发执行逻辑。
廖林

微信小程序|在 Supabase 中集成微信登录

在微信小程序中使用 Supabase 作为后端时,无法直接使用 Supabase 内置的 OAuth 登录流程。我们可以通过 Supabase Edge Functions 实现微信原生登录,让用户可以在小程序中无缝注册和登录。 在 Supabase 完成完整登录流程,无需引入格外的后端 API(例如 Vercel)。 架构与原理概述 ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ 微信小程序 │ ───▶ │ Edge Function │ ───▶ │ Supabase │ │ wx.login() │ │ wechat-login │ │ Auth + DB │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │
廖林

Supabase|集成 Google 登录认证

iOS 应用的 Google 登录流程: iOS App → Google OAuth (获取 ID Token) → Supabase Auth (验证 Token) → 返回 Session 在 Google Cloud Console 创建 OAuth 客户端(iOS 以及 Web) iOS 客户端出于安全考虑不提供 Client Secret(移动端无法安全存储密钥),但 Supabase 作为服务器端需要 Client Secret 来与 Google OAuth 服务通信。 因此,我们需要创建两个 OAuth 2.0 Client IDs,一起添加到 Supabase
廖林

NotificationCenter|在组件之间发送和接收消息

NotificationCenter ≠ 系统通知 很多初学者会混淆这两个概念: * 发送 iOS 系统通知,使用 UserNotifications 框架 * NotificationCenter 属于 Foundation 框架,无需格外导入 一句话总结:NotificationCenter 是 App 内部的广播系统,让不同组件之间可以发送和接收消息,而无需直接引用对方。 两种跨组件通信方式 方式 1:NotificationCenter(iOS 2+) 优点:简单、一对多广播、松耦合 缺点:字符串标识易出错、需手动管理生命周期 方式 2:withObservationTracking(iOS 17+) 优点:类型安全、自动追踪、现代化 缺点:需 iOS 17+、需手动重建追踪 withObservationTracking 只生效一次
廖林

macOS 配置 Claude Code Option+P 快捷键指南

在 macOS 上使用 Claude Code 时,Option+P 快捷键可能会被系统拦截,输出 π 字符。本文介绍如何解决这个问题。 问题原因 macOS 默认将 Option 键用于输入特殊字符(如 Option+P = π)。这与终端中 Option 作为 Meta 键的用法冲突。 Cursor 配置方法 1. 打开 Cursor 2. 按 Cmd + Shift + P 打开命令面板 3. 输入 Preferences: Open User Settings (JSON) 4. 添加以下配置: { "terminal.integrated.
廖林

SwiftData|在 AppIntent 中使用 ModelContainer

使用 @Dependency @Dependency 是 App Intents 框架专用的属性包装器,用于依赖注入。 常用的实际场景 使用 @Dependency 注入 ModelContainer 使用方法 直接使用 DataModel 单例(推荐) 是否能访问完整 ModelContainer 数据? 经过测试,直接使用 DataModel 单例,可以正常查询到主 App 中的完整数据: 使用 @Dependency 的方式,如果注入的也是 DataModel.shared.modelContainer 实例,那么也可以正常获取到所有数据:
廖林