Sequoia Group Container for Mac Catalyst Apps

Prior to Sequoia, Mac Catalyst Apps worked fine when using group folders that started with group. They now get an alert that the Mac Catalyst app is trying to access data from other applications. This may also impact some SwiftUI developers.

According to this the documentation for the App Group Entitlements entitlement, on macOS we should begin use the Team Identifier instead of group.

Should Mac Catalyst follow the macOS or iOS rules for com.apple.security.application-groups? If they should need to follow the macOS rules now, that creates several issues for developers. We would now need separate build targets to pick up the different Entitlements files. More distressing is that we would need to do some kind of migration process to get our files to the new location. There wouldn't be a transparent way to do so where the user wasn't warned about the application accessing files that don't belong to it.

Any clarification on what Mac Catalyst developers should be doing to prepare for Sequoia would be greatly appreciated.

Answered by DTS Engineer in 794749022

There is a bunch of stuff to unpack here. First up, I’m gonna assume that you’re familiar with the backstory in App Groups: macOS vs iOS: Fight!. If not, read it now.

In that I wrote:

In Mac Catalyst things behave as they do on iOS.

and I wasn’t kidding. A Mac Catalyst app should use iOS-style app group names (group.*), where those app group entitlement claims are authorised by its provisioning profile. Here’s an example of that:

% vtool -show-build Test758358.app/Contents/MacOS/Test758358
…
 platform MACCATALYST
…
% codesign -d --entitlements - Test758358.app               
…
	[Key] application-identifier
	[Value]
		[String] SKMME9E2Y8.com.example.apple-samplecode.Test758358
	[Key] com.apple.application-identifier
	[Value]
		[String] SKMME9E2Y8.com.example.apple-samplecode.Test758358
    …
	[Key] com.apple.security.application-groups
	[Value]
		[Array]
			[String] group.eskimo1.test
…
% security cms -D -i Test758358.app/Contents/embedded.provisionprofile | plutil -p -
{
  …
  "Entitlements" => {
    "application-identifier" => "SKMME9E2Y8.com.example.apple-samplecode.Test758358"
    "com.apple.application-identifier" => "SKMME9E2Y8.com.example.apple-samplecode.Test758358"
    …
    "com.apple.security.application-groups" => [
      0 => "group.eskimo1.test"
    ]
    …
  }
  …
}

As you can see, the group.eskimo1.test app group claim is authorised by the allowlist in the app’s provisioning profile.

Note Having both the application-identifier (iOS) and com.apple.application-identifier (macOS) entitlement is a tell-tale sign of a Mac Catalyst app. But if you want to confirm that, vtool is a great option.

Thus, Mac Catalyst apps meet the third criterion (“the app group identifier is authorised by a provisioning profile embedded within the app”). However, this isn’t working right now )-:

Based on bug reports from you and other developers, we investigated this and determined that there’s a bug in the app group authorisation check (r. 129667695).

As always, I can’t make any promises as to when this’ll be fixed, but I encourage you to monitor both Feedback Assistant and any future macOS 15 beta seeds.


Non-Catalyst apps should use Mac-style app group names (TTT.*, where TTT is the Team ID). That’ll avoid the container prompt because of the second criterion (“the app group identifier is prefixed with the app’s Team ID”)

Non-Catalyst apps deployed via the Mac App Store may also use an iOS-style app group name (group.*) as long as that group is registered to the app’s team. This is the first criterion mentioned in the release notes (“the app is deployed through Mac App Store”).


Finally, some thoughts on testing. First, when testing this stuff it’s best to create a new app. macOS 15 beta has some built-in compatibility exceptions and it’s easy to get confused if you run your tests using popular products [1].

Second, to do a full test you’ll need multiple teams. That’s tricky if you’re a member of just one team. A neat way to get around this is to use a different Apple ID as a Personal Team.

Third, I’ve found that VMs are super helpful when testing stuff like this, because I can easily reset the VM back to a clean state, one where it’s never seen any of my test apps.

Share and Enjoy

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

[1] Ask me how I know )-:

Do you have a currently shipping app that uses the group prefix?

ps For context, a while back I wrote App Groups: macOS vs iOS: Fight! At some point I’ll update that to cover the macOS 15 changes, but I haven’t quite got to that yet )-: I’m also waiting to see how things pan out in the final release (-:

Share and Enjoy

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

Yes. My app Zavala is impacted by the macOS 15 changes.

OK. Can you walk me through the process that triggers the problem?

AFAICT your app uses a group ID, group.io.vincode.Zavala, that is registered to your team and the app is distributed via the Mac App Store. Given that, I wouldn’t expect you to have problems.

ps For more context on this, see 114586798 in the macOS Sequoia 15 Beta 2 Release Notes.

Share and Enjoy

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

There is a sample project included in FB14171101 which I filed about the issue. Here is the code in the sample project which triggers the issue.

let appGroup = "group.io.vincode.AppGroupSampleProject"
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)
let testFileURL = containerURL!.appendingPathComponent("test.txt")
		
let fileData = "Sample file data".data(using: .utf8)
try? fileData?.write(to: testFileURL)

The alert appears when writing to the file.

Zavala is an Open Source project if you want to look at it in particular.

I'm sure several other projects have to be getting tripped up by this. If you are building a Mac Catalyst project, Xcode prompts you to create your App Group beginning with group. Xcode doesn't even allow you to have a different App Groups for iOS vs. macOS in the same target. It certainly feels like App Groups starting with group should be allowed.

It certainly looks like I shouldn't be receiving the prompt based on this sentence from the change notes.

Specifically, the app must use FileManager to get the app group container path and meet one of the following requirements: the app is deployed through Mac App Store; the app group identifier is prefixed with the app’s Team ID; or the app group identifier is authorised by a provisioning profile embedded within the app

The second criteria doesn't look like it is being honored.

Another project impacted by this is NetNewsWire. Although it isn't a Mac Catalyst app, it uses an App Group suffixed with group. (I didn't realize macOS differed from iOS when I contributed the code to NNW.) Based on the change notes, it shouldn't be impacted either, but it does receive the prompt.

There is a bunch of stuff to unpack here. First up, I’m gonna assume that you’re familiar with the backstory in App Groups: macOS vs iOS: Fight!. If not, read it now.

In that I wrote:

In Mac Catalyst things behave as they do on iOS.

and I wasn’t kidding. A Mac Catalyst app should use iOS-style app group names (group.*), where those app group entitlement claims are authorised by its provisioning profile. Here’s an example of that:

% vtool -show-build Test758358.app/Contents/MacOS/Test758358
…
 platform MACCATALYST
…
% codesign -d --entitlements - Test758358.app               
…
	[Key] application-identifier
	[Value]
		[String] SKMME9E2Y8.com.example.apple-samplecode.Test758358
	[Key] com.apple.application-identifier
	[Value]
		[String] SKMME9E2Y8.com.example.apple-samplecode.Test758358
    …
	[Key] com.apple.security.application-groups
	[Value]
		[Array]
			[String] group.eskimo1.test
…
% security cms -D -i Test758358.app/Contents/embedded.provisionprofile | plutil -p -
{
  …
  "Entitlements" => {
    "application-identifier" => "SKMME9E2Y8.com.example.apple-samplecode.Test758358"
    "com.apple.application-identifier" => "SKMME9E2Y8.com.example.apple-samplecode.Test758358"
    …
    "com.apple.security.application-groups" => [
      0 => "group.eskimo1.test"
    ]
    …
  }
  …
}

As you can see, the group.eskimo1.test app group claim is authorised by the allowlist in the app’s provisioning profile.

Note Having both the application-identifier (iOS) and com.apple.application-identifier (macOS) entitlement is a tell-tale sign of a Mac Catalyst app. But if you want to confirm that, vtool is a great option.

Thus, Mac Catalyst apps meet the third criterion (“the app group identifier is authorised by a provisioning profile embedded within the app”). However, this isn’t working right now )-:

Based on bug reports from you and other developers, we investigated this and determined that there’s a bug in the app group authorisation check (r. 129667695).

As always, I can’t make any promises as to when this’ll be fixed, but I encourage you to monitor both Feedback Assistant and any future macOS 15 beta seeds.


Non-Catalyst apps should use Mac-style app group names (TTT.*, where TTT is the Team ID). That’ll avoid the container prompt because of the second criterion (“the app group identifier is prefixed with the app’s Team ID”)

Non-Catalyst apps deployed via the Mac App Store may also use an iOS-style app group name (group.*) as long as that group is registered to the app’s team. This is the first criterion mentioned in the release notes (“the app is deployed through Mac App Store”).


Finally, some thoughts on testing. First, when testing this stuff it’s best to create a new app. macOS 15 beta has some built-in compatibility exceptions and it’s easy to get confused if you run your tests using popular products [1].

Second, to do a full test you’ll need multiple teams. That’s tricky if you’re a member of just one team. A neat way to get around this is to use a different Apple ID as a Personal Team.

Third, I’ve found that VMs are super helpful when testing stuff like this, because I can easily reset the VM back to a clean state, one where it’s never seen any of my test apps.

Share and Enjoy

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

[1] Ask me how I know )-:

Quinn, thank you for the very though answer. I guess I'll hold tight and see what happens to Mac Catalyst apps on Sequoia. I'll also notify the NetNewsWire team that a fix needs to be made prior to the Sequoia public release.

It looks like this was resolved in macOS Sequoia Developer Beta 4.

It looks like this was resolved in macOS Sequoia Developer Beta 4.

If you’re referring to the bug we bumped into above (r. 129667695) then, yes, that matches my expectations. Yay!

Share and Enjoy

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

@DTS Engineer

Non-Catalyst apps deployed via the Mac App Store may also use an iOS-style app group name (group.*) as long as that group is registered to the app’s team. This is the first criterion mentioned in the release notes (“the app is deployed through Mac App Store”).

Does this apply for dev-builds running directly from Xcode? Like, if a build from Xcode is prompting users to "Allow" data from other apps, will it still prompt if it's installed from the mac app store?

My macAppStore app is prompting me to allow. It's using group.* because it used to be a catalyst app.

Does this apply for dev-builds running directly from Xcode?

No, and that’s definitely an annoying aspect of relying on the criteria 1: You can only confirm that it works once the app is deployed.

To be clear, I don’t think anyone is happy with this story. I’m hoping that one day we’ll be able to include iOS-style app group IDs in the allowlist for macOS provisioning profiles, which will allow apps like yours to fall under criteria 3. However, today is not that day )-:

Share and Enjoy

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

I have a Mac Catalyst app with a menu bar extension. The menu bar extension is a regular Mac app integrated in the Catalyst app with the bridging header. Both apps use app groups for communication. The problem is that I still get the error described above, even though the app group is declared in the entitlements of both apps. If the app is downloaded from App Store, it does appear to work normal. But the problem makes development really annoying, and it also happens with TestFlight. Does anyone have the same problem?

I had the same problem after updating to Sequoia - every time I launch the application, a window started appearing asking me to approve the action "Application wants to access the data of another application".

I changed the values ​​to these in my application and everything worked:

File "*.entitlements":

<key>com.apple.security.application-groups</key>
<array>
-   <string>maxrys.js-blocker.group</string>
+   <string>$(TeamIdentifierPrefix)maxrys.js-blocker</string>
</array>

File "modelDomains.swift":

public class Domains: NSManagedObject {

    @NSManaged var name: String

    static let context: NSManagedObjectContext = {
    -   let appGrpName = "maxrys.js-blocker.group"
    +   let appGrpName = "97CZR6J379.maxrys.js-blocker"
        let storeDirectory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGrpName)!
        let storeURL = storeDirectory.appendingPathComponent("Database.sqlite")
        let storeDescription = NSPersistentStoreDescription(url: storeURL)
        let container = NSPersistentContainer(name: "Model")
        container.persistentStoreDescriptions = [storeDescription]
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container.viewContext
    }()

    convenience init() {
        self.init(context: Domains.context)
    }

    static func selectAll(orderBy: String = #keyPath(Domains.name), ascending: Bool = true) -> [Domains] {
        let fetchRequest = NSFetchRequest<Domains>(entityName: "Domains")
        let sortDescriptorKey = NSSortDescriptor(key: orderBy, ascending: ascending)
        fetchRequest.sortDescriptors = [sortDescriptorKey]
        return try! self.context.fetch(
            fetchRequest
        )
    }

}

You can get your Team ID here: https://developer.apple.com/account → Membership details → Team ID

As it's part of SIP, the alert will be suppressed if you disable SIP itself, but I don't like this workaround since it unnecessarily endangers my machine. However, I can't live with this alert. Not only does it significantly slow down development, but I also can't properly test the actual behavior.

Technically, I could change the app to meet the criteria, but I wouldn't do that, as it would require writing migration code for CoreData, User Defaults, and other files in the Group Container for existing users. This would be a huge amount of work, including testing.

I’m hoping that one day we’ll be able to include iOS-style app group IDs in the allowlist for macOS provisioning profiles, which will allow apps like yours to fall under criteria 3. However, today is not that day )-:

I hope that day comes soon. It's ironic that this new security feature could end up encouraging people to disable one of the most important security layers.

it also happens with TestFlight.

We seem to be fixing that in macOS 15.1, currently in beta. See the macOS Sequoia 15.1 Beta 4 Release Notes.

Once that roles out, using a group. prefix in a Mac App Store app should be fine in all user-level scenarios. That just leaves day-to-day development )-:

Share and Enjoy

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

Sequoia Group Container for Mac Catalyst Apps
 
 
Q