Read file with System Network Extension from App Group

I have trouble with reading a file from an App Group with my System Network Extension.

The app group container is found successfully.

However the file read returns empty.

In the app itself the same code runs fine and returns a string array of items found in the file.


Code:
Code Block
func readFile() -> [String] {
        var jsonResult: [String] = []
        guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: AppConstants.groupID) else {
            fatalError()
        }
        let fileURL = containerURL.appendingPathComponent("file.json")
        if let data = try? NSData(contentsOfFile: fileURL.path, options: .mappedIfSafe) as Data {
            if let json = try? JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) {
                jsonResult = json as! [String]
            }
        }
        os_log("jsonResult: %{public}@", jsonResult)
        return jsonResult
    }


Log:
Code Block
default 09:42:19.486793+0200 app-network-extension container_create_or_lookup_app_group_path_by_app_group_
identifier: success
default 09:42:20.105792+0200 app-network-extension
jsonResult: ( )


Edit, after more digging:

fileURL is different!

App: file:///Users/me/Library/Group%20Containers/
SysExt: file:///private/var/root/Library/Group%20Containers/
Answered by DTS Engineer in 673355022

I have trouble with reading a file from an App Group with my System
Network Extension.

App Groups won’t help in this scenario because App Group containers are per-user and are the app and the sysex run as different users (the logged in GUI user and root, respectively). The best way to share state between these two components is via IPC (and specifically XPC).

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Accepted Answer

I have trouble with reading a file from an App Group with my System
Network Extension.

App Groups won’t help in this scenario because App Group containers are per-user and are the app and the sysex run as different users (the logged in GUI user and root, respectively). The best way to share state between these two components is via IPC (and specifically XPC).

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

Is there really no way to do this in 2026? I have some files which the system extension needs at runtime which is updated by the main app.

How efficient is using XPC to copy files? Is there no other way to have a shared FS between main app and rooted system extension?

Is there really no way to do this in 2026?

The situation with app group containers hasn’t changed.

Well, the situation with app group and their containers has changed a lot in the last five years, but there’s still no way to set up a container that’s shared by your sysex and its container app.

If you’re curious about the evolution app groups in general, I have a long post about that: App Groups: macOS vs iOS: Working Towards Harmony

I have some files which the system extension needs at runtime which is updated by the main app.

That’s not a good idea security-wise. Your sysex runs as root and the container app doesn’t, so having the sysex trust data written by the app represents a privilege inversion.

The best way to approach this depends on the nature of these files:

  • If the app downloads them from the ’net then you can just have the app command the sysex to do that. The sysex can then authenticate the command and authenticate the resulting data.
  • Alternatively, you can have the app download the data and command the sysex to adopt it. Again, the sysex has to authenticate stuff before it actually trusts the data.
  • If these files are generated by the app itself — for example, they’re based on user choices — then have the app assemble them into a coherent package and then command the sysex to adopt it.
How efficient is using XPC to copy files?

It depends on how you approach the task. In a case like this I’d probably pass a file descriptor across the ‘wire’ and then have the sysex call fclonefileat to make a copy-on-write copy of the file. This is cheap — no blocks are copied — and it guarantees that the sysex sees the file in a consistent state.

This works best if the data is packed into a single file, because that avoids any possibility of the sysex being tricker into using inconsistent data. If you have multiple files to transfer, you can pack them into an archive.

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

Read file with System Network Extension from App Group
 
 
Q