Swift中NotificationCenter能否跨目标发送?自定义键盘通知无响应问题
自定义键盘向主App发送通知无响应的问题解决
问题根源
自定义键盘属于App Extension,和主App运行在不同的进程中。NotificationCenter.default是进程内通知中心,只能在同一个进程内传递通知,跨进程无法生效。而系统原生的UIApplication.keyboardDidShowNotification等通知是由系统层面广播的跨进程通知,所以主App能正常接收。
解决方案
方案1:App Groups + UserDefaults(推荐)
通过配置共享的App Groups,让主App和键盘Extension共享UserDefaults,以此传递状态并监听变化。
配置App Groups
- 在开发者后台为你的App ID开启App Groups权限,并创建一个唯一的Group标识(比如
group.com.yourcompany.yourapp.keyboard)。 - 在Xcode中,分别给主App和键盘Extension的
Signing & Capabilities里添加App Groups,并勾选刚才创建的Group。
- 在开发者后台为你的App ID开启App Groups权限,并创建一个唯一的Group标识(比如
键盘端代码(发送状态)
在键盘激活/退出时更新共享UserDefaults:// 自定义键盘类中 override func viewDidLoad() { super.viewDidLoad() // 标记键盘已激活 let sharedDefaults = UserDefaults(suiteName: "group.com.yourcompany.yourapp.keyboard") sharedDefaults?.set(true, forKey: "CustomKeyboardIsActive") sharedDefaults?.synchronize() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 标记键盘已退出 let sharedDefaults = UserDefaults(suiteName: "group.com.yourcompany.yourapp.keyboard") sharedDefaults?.set(false, forKey: "CustomKeyboardIsActive") sharedDefaults?.synchronize() }主App端代码(监听状态变化)
在RootViewController中监听共享UserDefaults的变化:// RootViewController中 override func viewDidLoad() { super.viewDidLoad() // 监听共享UserDefaults的变更通知 NotificationCenter.default.addObserver(self, selector: #selector(keyboardStatusChanged(_:)), name: UserDefaults.didChangeNotification, object: nil) } @objc func keyboardStatusChanged(_ notification: Notification) { let sharedDefaults = UserDefaults(suiteName: "group.com.yourcompany.yourapp.keyboard") let isKeyboardActive = sharedDefaults?.bool(forKey: "CustomKeyboardIsActive") ?? false NSLog(TAG, "\(#function) - 自定义键盘状态:\(isKeyboardActive)") // 这里执行UI更新或功能暂停/恢复逻辑 } deinit { NotificationCenter.default.removeObserver(self) }
方案2:Darwin通知中心(跨进程通知)
使用CFNotificationCenter发送跨进程的Darwin通知,适合简单的状态通知(无法传递复杂数据)。
键盘端发送通知
override func viewDidLoad() { super.viewDidLoad() let notifyCenter = CFNotificationCenterGetDarwinNotifyCenter() // 发送键盘激活通知 CFNotificationCenterPostNotification(notifyCenter, "com.yourcompany.yourapp.CustomKeyboardActive" as CFString, nil, nil, true) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) let notifyCenter = CFNotificationCenterGetDarwinNotifyCenter() // 发送键盘退出通知 CFNotificationCenterPostNotification(notifyCenter, "com.yourcompany.yourapp.CustomKeyboardInactive" as CFString, nil, nil, true) }主App端监听通知
override func viewDidLoad() { super.viewDidLoad() let notifyCenter = CFNotificationCenterGetDarwinNotifyCenter() // 监听键盘状态变更 CFNotificationCenterAddObserver(notifyCenter, nil, { (_, _, name, _, _) in guard let nameStr = name?.rawValue as? String else { return } if nameStr == "com.yourcompany.yourapp.CustomKeyboardActive" { NSLog(TAG, "自定义键盘已激活") // 处理激活逻辑 } else if nameStr == "com.yourcompany.yourapp.CustomKeyboardInactive" { NSLog(TAG, "自定义键盘已退出") // 处理退出逻辑 } }, nil, nil, .deliverImmediately) }
注意事项
- 不要尝试用
NotificationCenter.default跨Extension和主App通信,进程隔离机制会阻止这种传递。 - 使用App Groups时,确保主App和Extension的Bundle Identifier前缀一致,且都正确配置了Group权限。
- Darwin通知是全局广播的,建议使用唯一的通知名称(比如包含你的App Bundle Identifier),避免和其他App冲突。
内容的提问来源于stack exchange,提问作者Avicii4ever
相关产品推荐
相关产品推荐

