PAYATHON 2026

iOS 键盘扩展如何获取宿主 App 的 Bundle ID 或 URL scheme?

数据小许

通过 Apple 当前公开的自定义键盘 API,无法通用地获取使用该键盘的宿主 App 的 Bundle ID 或 URL scheme,也没有可直接调用的接口能自动返回上一个 App。对于事先确定且提供公开跳转链接的目标应用,可以从自己的主 App 打开对应链接,但这无法自动识别来源应用,也不保证恢复原来的编辑位置。

为什么无法直接识别宿主 App?

这里涉及三个不同的对象:键盘扩展、包含该扩展的主 App,以及当前显示输入框的宿主 App。例如,在 WhatsApp 中使用你的键盘时,WhatsApp 是宿主 App。

Apple 文档说明,自定义键盘在独立进程中运行,通过 UIInputViewController.textDocumentProxy 操作文本输入。公开接口提供文本上下文、插入和删除等能力,但没有查询当前宿主 App 身份的方法,因此无法通过这些接口取得宿主的 Bundle ID。参见 Apple 自定义键盘文档。

在键盘扩展中读取 Bundle.main.bundleIdentifier,得到的是扩展自身的标识,而非 WhatsApp 等宿主 App 的标识。开启 Allow Full Access 也不会增加宿主身份查询能力;其公开说明涉及网络访问、共享容器等权限。参见 Open Access 配置说明。

Bundle ID 和 URL scheme 不能互相推导

Bundle ID 用于标识应用,URL scheme 用于处理链接。知道某个应用的 Bundle ID,并不意味着可以把它拼成 URL 来打开该应用。

canOpenURL(_:) 只能判断系统是否有应用能够处理给定链接,不能判断该应用是否正在使用你的键盘,也不能枚举当前宿主注册的所有 URL scheme。参见 canOpenURL 文档。

Notes、Mail、Messages、WhatsApp 等应用需要逐个核对其公开链接能力。某个链接可能打开新消息、撰写界面或特定内容,而不是原来正在编辑的页面,因此不能一概视为“返回原 App”。

针对已知目标应用如何实现?

前提是目标应用已由用户选择,或由你控制的集成流程明确提供,并且存在经过确认的公开跳转链接。

  1. 为支持的应用维护明确的链接配置,记录链接实际打开的页面。
  2. 主 App 完成操作后,打开已确定的目标链接。
  3. 链接不存在或打开失败时,提示用户手动返回原应用。

下面的代码仅用于自己的主 App target,假设使用支持 Swift Concurrency 的开发环境。传入的 url 必须事先确定,代码不负责识别宿主:

import UIKit

@MainActor
func openKnownReturnURL(_ url: URL) async -> Bool {
    await UIApplication.shared.open(url, options: [:])
}

返回 true 只表示 URL 已成功打开,不能确认原来的输入框、光标位置或键盘状态已经恢复。参见 UIApplication.open 文档。

如果还要使用 canOpenURL(_:) 查询第三方自定义 scheme,需要按文档在调用方主 App 的 Info.plist 中配置 LSApplicationQueriesSchemes。直接调用 open 和事先查询是两个不同的操作,不要把查询配置当成跳转授权。

键盘启动主 App 也需要单独验证

上述代码不能直接搬进键盘扩展。NSExtensionContext 虽然定义了 open(_:completionHandler:),但 Apple 明确说明,是否支持该方法取决于扩展点。文档列出的 iOS 支持类型是 Today 和 iMessage,没有把自定义键盘列为支持类型。参见 NSExtensionContext.open 文档。

因此,需要分别验证“从键盘打开自己的主 App”和“从主 App 返回目标应用”这两个步骤。第二步能够打开某个链接,不代表第一步已有通用、受支持的实现。

如何理解其他产品中的类似行为?

仅凭 WisprFlow、Willow 或 Typeless 的界面表现,无法确定它们是否识别了宿主、维护了应用映射或依赖其他机制,也无法确认其适用系统版本和应用范围。观察到的效果不能证明公开 API 的存在。

如果目标是覆盖任意第三方 App,应保留手动返回流程;如果只支持少量指定应用,可以提供用户明确选择的返回入口。不要把读取私有属性或调用私有 API 当作稳定方案。

备注:内容仅供参考。