Using core data in ShieldConfigurationExtension

Hi there,

In short, I'm trying to use CoreData in my ShieldConfigurationDataSource extension. Trying to fetch from core data at all seems to cause the shield to render it's default look. I already added the extension to an app group + configured my persistence store to use the app group. Below is my code, any help is appreciated:

// Shield extension
override func configuration(shielding application: Application) -> ShieldConfiguration {
        do {
            let appSelectionId = "***"
            let blockedItemReq = ...
            blockedItemReq.predicate = ...
            let moc = PersistenceController.shared.container.viewContext  // Commenting this and the bottom out makes it work, but I need the data!
            let blockedItemRes = try moc.fetch(blockedItemReq)
            
            let shieldTitle = ShieldConfiguration.Label(text: "Hello there", color: .red)
            return ShieldConfiguration(backgroundColor: .black, title: shieldTitle)
        }
        catch {
            let shieldTitle = ShieldConfiguration.Label(text: "ERROR \(error.localizedDescription)", color: .white)
            return ShieldConfiguration(backgroundColor: .black, title: shieldTitle)
        }
    }
// Persistence Controller
  init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "AppBlockerOne")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        else {
            let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.appblockerone")!
            let storeURL = containerURL.appendingPathComponent("AppBlockerOne.sqlite")
            let description = NSPersistentStoreDescription(url: storeURL)
            container.persistentStoreDescriptions = [description]
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }

Are you able to verify this isn't falling into the catch block?

let blockedItemRes = try moc.fetch(blockedItemReq)

Some additional considerations of using CoreData in your extension:

  1. The amount of time the fetch may take can cause a watchdog event or have the system only show the default.
  2. If your data store loads a lot of information it may crash under memory pressure and have the system only show the default.

More importantly to quote the documentation:

Your extension is expected to return an appropriate configuration as quickly as possible, in order to provide the best possible user experience. To protect the Family Sharing group’s privacy, your extension runs in a sandbox. This sandbox prevents your extension from making network requests or moving sensitive content outside the extension’s address space.

It is possible that the same mechanism is preventing you from importing data. To further debug the app's behavior use Console.app and start with filtering by the BundleID of the target and / or the target name itself (the process name).

Also be sure to check the system processes for additional information. This will vary based on what and where you are looking but some examples:
To debug APNS you'll want to monitor apnd along with other relevant processes.
To debug WidgetKit, LiveActivities, Dynamic Island you'll want to monitor springboardd, liveactivitiesd along with other relevant processes.

You can find relevant processes for family controls as you debug.

Rico
WWDR - DTS - Software Engineer

I think it's quite likely that the sandbox is blocking access to Core Data. I'm using it via Swift Data:

do {
    container = try ModelContainer(
        for: Settings.self,
        configurations: ModelConfiguration(allowsSave: allowsSave)
    )
    context = ModelContext(container)
} catch {
    fatalError("*** Failed to create BreakManager: \(error)")
}

I get an error when I try to create the ModelContainer:

Failed to register for com.apple.managedconfiguration.effectivesettingschanged: 9
Could not register for user invalidated notifications; status = 9
error: (3) access permission denied
error: Encountered exception error during prepareSQL for SQL string 'SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'' : access permission denied with userInfo {
    NSFilePath = "/private/var/mobile/Containers/Shared/AppGroup/<snip>/Library/Application Support/default.store";
    NSSQLiteErrorDomain = 3;
} while checking table name from store: <NSSQLiteConnection: 0x1010d4180>
error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (256)
error: userInfo:
error: 	NSFilePath : /private/var/mobile/Containers/Shared/AppGroup/<snip>/Library/Application Support/default.store
error: 	NSSQLiteErrorDomain : 3
error: storeType: SQLite
error: configuration: default
error: URL: file:///private/var/mobile/Containers/Shared/AppGroup/<snip>/Library/Application%20Support/default.store
error: <NSPersistentStoreCoordinator: 0x1010f4230>: Attempting recovery from error encountered during addPersistentStore: 0x101025260 Error Domain=NSCocoaErrorDomain Code=256 "The file “default.store” couldn’t be opened." UserInfo={NSFilePath=/private/var/mobile/Containers/Shared/AppGroup/<snip>/Library/Application Support/default.store, NSSQLiteErrorDomain=3}
error: Store failed to load.  <NSPersistentStoreDescription: 0x101025200> (type: SQLite, url: file:///private/var/mobile/Containers/Shared/AppGroup/<snip>/Library/Application%20Support/default.store) with error = Error Domain=NSCocoaErrorDomain Code=256 "The file “default.store” couldn’t be opened." UserInfo={NSFilePath=/private/var/mobile/Containers/Shared/AppGroup/<snip>/Library/Application Support/default.store, NSSQLiteErrorDomain=3} with userInfo {
    NSFilePath = "/private/var/mobile/Containers/Shared/AppGroup/<snip>/Library/Application Support/default.store";
    NSSQLiteErrorDomain = 3;
}
Unresolved error loading container Error Domain=NSCocoaErrorDomain Code=256 UserInfo={NSFilePath=<private>, NSSQLiteErrorDomain=3}

NSSQLiteErrorDomain corresponds to SQLite's own errors, and SQLite Error 3 is ERROR_PERM, for a permissions error.

I can read and write to my App Group's container, including via UserDefaults, so it appears that the sandboxing mechanism in the Screen Time technology frameworks specifically restricts SQLite's permissions when working inside the sandbox. This is pretty frustrating, because the sandbox is only really to stop apps from getting data out of DeviceActivityReport, no reason it should break elsewhere. I've filed FB14915291 to track this.

Using core data in ShieldConfigurationExtension
 
 
Q