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

iOS Screen Time API:从DeviceActivityMonitorExtension获取FamilyActivitySelection

解决方案:解决DeviceActivityMonitorExtension无法获取FamilyActivitySelection的问题

核心问题分析

你遇到的问题本质是主App与Extension的UserDefaults容器不共享,尽管配置了App Groups,但代码中仍使用UserDefaults.standard(各自独立的沙盒容器),导致Extension无法读取主App保存的选择数据。此外,忽略编码解码错误也会掩盖序列化失败的问题。


具体修复步骤

1. 切换到App Groups共享的UserDefaults

将所有UserDefaults.standard替换为App Groups对应的共享容器,确保主App和Extension访问同一存储区域:

  • 替换为你项目中实际的App Groups标识符(示例为group.com.yourcompany.yourapp)

2. 完善编码解码的错误处理

移除try?的静默失败逻辑,添加显式错误捕获,便于排查序列化过程中的问题。

3. 初始化时加载共享数据

修改MyModel的初始化逻辑,启动时自动从共享UserDefaults加载已保存的选择,避免初始化为空值。


修改后的代码示例

MyModel.swift 修改部分

class MyModel: ObservableObject {
    let store = ManagedSettingsStore(named: .mySettingStore)
    static let shared = MyModel()
    
    @Published var familyActivitySelection: FamilyActivitySelection
    
    private let encoder = PropertyListEncoder()
    private let decoder = PropertyListDecoder()
    private let userDefaultsKey = "ScreenTimeSelection"
    // 替换为你的App Groups标识符
    private let appGroupSuiteName = "group.com.yourcompany.yourapp"
    
    // 获取共享UserDefaults
    private var sharedDefaults: UserDefaults? {
        UserDefaults(suiteName: appGroupSuiteName)
    }
    
    func saveFamilyActivitySelection(selection: FamilyActivitySelection) {
        print("selected app updated: ", selection.applicationTokens.count," category: ", selection.categoryTokens.count)
        guard let defaults = sharedDefaults else {
            print("Failed to access App Groups UserDefaults")
            return
        }
        
        do {
            let data = try encoder.encode(selection)
            defaults.set(data, forKey: userDefaultsKey)
            defaults.synchronize() // 确保数据立即写入
            // 验证保存结果
            if let saved = getSavedFamilyActivitySelection() {
                print("Saved successfully: apps=\(saved.applicationTokens.count), categories=\(saved.categoryTokens.count)")
            }
        } catch {
            print("Failed to encode selection: \(error.localizedDescription)")
        }
    }
    
    func getSavedFamilyActivitySelection() -> FamilyActivitySelection? {
        guard let defaults = sharedDefaults else {
            print("Could not access App Groups UserDefaults")
            return nil
        }
        guard let data = defaults.data(forKey: userDefaultsKey) else {
            print("No saved selection data found")
            return nil
        }
        do {
            let selectedApp = try decoder.decode(FamilyActivitySelection.self, from: data)
            print("saved selected app updated: \(selectedApp.categoryTokens.count)")
            return selectedApp
        } catch {
            print("Failed to decode selection: \(error.localizedDescription)")
            return nil
        }
    }

    init() {
        // 初始化时加载已保存的选择
        if let savedSelection = getSavedFamilyActivitySelection() {
            familyActivitySelection = savedSelection
            print("Loaded saved selection on init")
        } else {
            familyActivitySelection = FamilyActivitySelection()
            print("Initialized with empty selection")
        }
    }
    
    // ... 其余方法保持不变
}

DeviceActivityMonitorExtension.swift 优化部分

在intervalDidStart中先验证数据是否加载成功,再执行限制逻辑:

override func intervalDidStart(for activity: DeviceActivityName) {
    super.intervalDidStart(for: activity)
    
    // 先验证数据是否获取成功
    if let selection = model.getSavedFamilyActivitySelection() {
        print("Extension loaded selection: apps=\(selection.applicationTokens.count), categories=\(selection.categoryTokens.count)")
        model.startAppRestrictions()
        showLocalNotification(title: "My Start Restrict App", desc: "Restriction App started successfully")
    } else {
        print("Extension failed to load selection")
        showLocalNotification(title: "Restriction Failed", desc: "No apps selected")
    }
    
    let socialStore = ManagedSettingsStore(named: .mySettingStore)
    socialStore.clearAllSettings()
}

额外验证步骤

  1. 确认App Groups配置
    • 主App和Extension的Signing & Capabilities中均添加App Groups,并勾选同一组
    • 检查两者Info.plist的com.apple.security.application-groups数组,确保包含相同的组标识符
  2. 测试权限
    • 确保App已获得Family Controls的完整权限(在系统设置中确认)
  3. 日志排查
    • 查看Xcode控制台的打印信息,确认编码解码是否成功,以及共享UserDefaults是否能正常访问

内容的提问来源于stack exchange,提问作者Yusuf Fachroni

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 09:06:01