You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在自定义键盘扩展中判断主应用是否处于运行状态?

如何在自定义键盘扩展中判断主应用是否运行

嘿,我刚好处理过类似的需求,既然你已经配置好了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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 11:07:42