如何在自定义键盘扩展中判断主应用是否处于运行状态?
如何在自定义键盘扩展中判断主应用是否运行
嘿,我刚好处理过类似的需求,既然你已经配置好了App Group和共享UserDefaults,那咱们可以基于这个基础来实现,给你两个亲测有效的方案:
方案一:共享心跳时间戳(最简单易实现)
核心思路是让主应用在前台活跃时定时更新一个共享的时间戳到App Group的UserDefaults,键盘扩展通过读取这个时间戳,判断它是否在最近的一个合理区间内(比如10秒),如果是就认为主应用正在运行。
主应用端代码
在主应用的SceneDelegate(或者AppDelegate,根据你项目的生命周期)里,添加前台活跃和后台暂停的回调,来启动/停止心跳:
import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? var heartbeatTimer: Timer? func sceneDidBecomeActive(_ scene: UIScene) { // 前台活跃时启动心跳,每5秒更新一次时间戳 heartbeatTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in let sharedDefaults = UserDefaults(suiteName: "group.share.keyboard") sharedDefaults?.set(Date().timeIntervalSince1970, forKey: "MainAppLastActiveTimestamp") sharedDefaults?.synchronize() } } func sceneWillResignActive(_ scene: UIScene) { // 退后台时停止心跳,同时更新时间戳为过期值 heartbeatTimer?.invalidate() heartbeatTimer = nil let sharedDefaults = UserDefaults(suiteName: "group.share.keyboard") sharedDefaults?.set(0, forKey: "MainAppLastActiveTimestamp") sharedDefaults?.synchronize() } }
键盘扩展端代码
在键盘的InputViewController里,读取这个时间戳并判断:
import UIKit class KeyboardViewController: UIInputViewController { func isMainAppRunning() -> Bool { guard let sharedDefaults = UserDefaults(suiteName: "group.share.keyboard"), let lastTimestamp = sharedDefaults.value(forKey: "MainAppLastActiveTimestamp") as? TimeInterval else { return false } // 判断是否在最近10秒内更新过,这里的时间可以根据需求调整 let currentTime = Date().timeIntervalSince1970 return currentTime - lastTimestamp < 10 } // 比如在键盘激活时判断 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if isMainAppRunning() { print("主应用正在运行") // 执行你的逻辑 } else { print("主应用未运行") } } }
方案二:跨进程Darwin通知(更实时)
如果需要更实时的状态同步,可以用Darwin分布式通知,主应用进入前台/后台时发送通知,键盘扩展监听这些通知来维护状态。
主应用端代码
在前台活跃和后台暂停时发送通知:
import UIKit import Foundation class SceneDelegate: UIResponder, UIWindowSceneDelegate { let mainAppActiveNotification = "group.share.keyboard.MainAppActive" let mainAppInactiveNotification = "group.share.keyboard.MainAppInactive" func sceneDidBecomeActive(_ scene: UIScene) { // 发送主应用活跃通知 CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFNotificationName(rawValue: mainAppActiveNotification as CFString), nil, nil, true) } func sceneWillResignActive(_ scene: UIScene) { // 发送主应用暂停通知 CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFNotificationName(rawValue: mainAppInactiveNotification as CFString), nil, nil, true) } }
键盘扩展端代码
监听通知并维护状态:
import UIKit import Foundation class KeyboardViewController: UIInputViewController { let mainAppActiveNotification = "group.share.keyboard.MainAppActive" let mainAppInactiveNotification = "group.share.keyboard.MainAppInactive" var isMainAppActive = false override func viewDidLoad() { super.viewDidLoad() // 注册通知监听 CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), nil, { (_, _, _, _, _) in self.isMainAppActive = true }, mainAppActiveNotification as CFString, nil, .deliverImmediately) CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), nil, { (_, _, _, _, _) in self.isMainAppActive = false }, mainAppInactiveNotification as CFString, nil, .deliverImmediately) // 初始化时先检查一次心跳时间戳,避免通知没收到的情况 isMainAppActive = checkMainAppHeartbeat() } func checkMainAppHeartbeat() -> Bool { guard let sharedDefaults = UserDefaults(suiteName: "group.share.keyboard"), let lastTimestamp = sharedDefaults.value(forKey: "MainAppLastActiveTimestamp") as? TimeInterval else { return false } return Date().timeIntervalSince1970 - lastTimestamp < 10 } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if isMainAppActive { print("主应用正在运行") } else { print("主应用未运行") } } deinit { // 移除监听 CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), nil, mainAppActiveNotification as CFString, nil) CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), nil, mainAppInactiveNotification as CFString, nil) } }
注意事项
- 方案一的时间间隔要合理,太短会增加资源消耗,太长会降低准确性,建议5-10秒。
- 由于iOS系统的后台限制,主应用进入后台后心跳会停止,所以方案一的时间戳会停留在最后一次更新的值,这时候键盘扩展判断的是“主应用是否在最近活跃过”,如果需要严格判断“当前是否在前台运行”,方案二结合方案一更稳妥。
- 确保App Group的配置完全正确,主应用和键盘扩展都要添加相同的App Group ID,并且开启相应的权限。
内容的提问来源于stack exchange,提问作者badboy_tqj
相关产品推荐
相关产品推荐

