Sharing NSUserDefaults between XPCService, Network Extension and container app

Hi,

I’m writing a network extension for macOS using the following pattern:

  • Network extension with ID com.company.app.extension
  • XPC Service with ID com.company.app.controller
  • Container app with ID com.company.app.container
  • First 2 are contained in the 3rd

All of 3 are sandboxed & share the same com.company.app AppGroup. XPC Service uses network (client) entitlement to fetch settings & control the extension. The app need to support managed preferences as well.

I read many posts in this forum, but I’m confused about using NSUserDefaults accross those processes.

Pb 1 - standardsUserDefaults vs initWithSuiteName

I noticed that to observe defaults using KVO I need to use initWithSuiteName:@"com.company.app" (or addSuiteName).

Observing standardsUserDefaults alone will never trigger any event. This is the first confusion because my understanding was that KVO observation wasn’t restrincted to suites.

Pb 2 - AppGroup ‘group.’ prefix

If I observe a suite named ‘com.company.app’ from the container app or XPC Service I get error : [User Defaults] Couldn't read values in CFPrefsManagedSource<> (Domain: com.company.app, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: No): accessing preferences outside an application's container requires user-preference-read or file-read-data sandbox access

This can be fixed using ‘group.com.company.app’ suite (both in the code and AppGroup entitlement). However if I use the same AppGroup for extension it will fail loading with error code OSSystemExtensionErrorValidationFailed.

Checked the provisioning profiles, they all map to the same ID in Dev portal. Including the TeamID in the suite name will produce the same situation.

The only way to load the extension is to use AppGroup com.company.app. This again is confusing because Dev portal force the ‘group.’ prefix. I can’t figure out how to use the same suite name for all processes.

Pb 3 - Inter-process synchronisation

Based on previous conclusion the situation is both container & controller use AppGroup (TeamID).group.com.company.app and addSuiteNamed:@"group.com.company.app" for observing & updating the defaults.

Each process can observe its own defaults updates but they do not synchronize between container app and XPC Service.

This is the most frustating part. The documentation says:

NSUserDefaultsDidChangeNotification is posted whenever any user defaults changed within the current process, but is not posted when ubiquitous defaults change, or when an outside process changes defaults. Using key-value observing to register observers for the specific keys of interest will inform you of all updates, regardless of where they're from.

Did I missed some steps ?

Accepted Reply

An update on KVO observing. I succeeded solving my issue by :

  • Observing a defaults inited with initWithSuiteNamed. Observing standardUserDefault + addSuiteName seems to fail.
  • Adding TeamID to the suite name.

Replies

I’m writing a network extension for macOS using the following pattern:

Is your NE provider an appex or a sysex? You mentioned OSSystemExtensionErrorValidationFailed, which suggests a sysex. If so, sharing an App Group between your provider and your sysex isn’t feasible. That’s because your provider runs as root and your other components run as the logged in user, and shared App Groups only allow you to share between apps (and other app-like things) run by the same user.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn,

I confirm this is a System Extension (content filter used as Firewall).

OSSystemExtensionErrorValidationFailed disappears if bundle ID and AppGroup are synchronized.

It’s not yet clear to me if the ‘group.’ prefix need to be used for AppGroup. Looking at Group Containers folder some apps use the prefix some others do not (including Apple’s).

Based on your explanation, I moved the XPC Service from daemon to agent so that it runs in user context. However KVO observation isn’t triggered between container app and XPC Service. Yet defaults values are up-to-date if I use XPC communication as notification.

An update on KVO observing. I succeeded solving my issue by :

  • Observing a defaults inited with initWithSuiteNamed. Observing standardUserDefault + addSuiteName seems to fail.
  • Adding TeamID to the suite name.