DeviceActivity Report Extension cannot pass App Store Connect validation without becoming un-installable on device

I'm running into a contradictory requirement involving the DeviceActivity Report extension (com.apple.deviceactivityui.report-extension) that makes it impossible to both:

upload the app to App Store Connect, and

install the app on a physical device.

This creates a complete catch-22.

📌 Overview

My extension:

Path: Runner.app/PlugIns/LoADeviceActivityReport.appex

Extension point: com.apple.deviceactivityui.report-extension

Implementation (SwiftUI):

import SwiftUI import DeviceActivity

@main struct LoADeviceActivityReport: DeviceActivityReportExtension { var body: some DeviceActivityReportScene { // ... } }

This is the standard SwiftUI @main DeviceActivityReportExtension template.

🟥 Side A — iOS runtime behavior (device installer)

If I add either of these keys to the extension's Info.plist:

NSExtensionPrincipalClass

NSExtensionMainStoryboard

then the app cannot be installed on a real iPhone/iPad.

The device installer fails with:

Error 3002 AppexBundleContainsClassOrStoryboard

NSExtensionPrincipalClass and NSExtensionMainStoryboard are not allowed for extension point com.apple.deviceactivityui.report-extension.

To make the app install and run, I must remove both keys completely.

This leaves the extension Info.plist like:

<key>NSExtension</key> <dict> <key>NSExtensionPointIdentifier</key> <string>com.apple.deviceactivityui.report-extension</string> </dict>

With this, the app installs and runs correctly.

🟥 Side B — App Store Connect upload validator

However, when I upload the IPA with the runtime-correct Info.plist, App Store Connect rejects it:

State: STATE_ERROR.VALIDATION_ERROR (HTTP 409)

Missing Info.plist values. No values for NSExtensionMainStoryboard or NSExtensionPrincipalClass found in extension Info.plist for Runner.app/PlugIns/LoADeviceActivityReport.appex.

So ASC requires that at least one of those keys be present.

💥 The catch-22 If I add PrincipalClass / MainStoryboard:

✔ App Store Connect validation passes

❌ But the app can NOT be installed on any device (Error 3002)

If I remove PrincipalClass / MainStoryboard:

✔ The app installs and runs correctly

❌ ASC rejects the upload with “Missing Info.plist values”

There is currently NO Info.plist configuration that satisfies both:

Runtime:

"NSExtensionPrincipalClass and NSExtensionMainStoryboard are not allowed."

App Store Connect:

"You must include NSExtensionPrincipalClass or NSExtensionMainStoryboard."

📌 Expected behavior

For SwiftUI @main DeviceActivityReportExtension, the documentation and examples suggest the correct configuration is:

<key>NSExtensionPointIdentifier</key> <string>com.apple.deviceactivityui.report-extension</string>

with no principal class or storyboard at all.

If that is correct for runtime, ASC seems to need updated validation rules for this extension type.

❓My Questions

What is the officially correct Info.plist configuration for a SwiftUI DeviceActivityReportExtension?

Should principal class / storyboard not be required for this extension type?

Is this a known issue with App Store Connect validation?

Is there currently a workaround that allows:

installation on device and

successful App Store Connect upload, without violating runtime restrictions?

Running into the same thing - following.

Have the exact same issue - please help with a solution

@yosuke65 @cbcb After extensive debugging, I found a configuration that passes both App Store Connect validation AND works on device.

The Problem:

  • Embedding in PlugIns folder (dstSubfolderSpec = 13) → ASC rejects with "Missing NSExtensionPrincipalClass"
  • Adding NSExtensionPrincipalClass → Device install fails with "not allowed for this extension point"

The Solution:

Use ExtensionKit extension type embedded in the Extensions folder (not PlugIns), with explicit dstPath:

project.pbxproj - Embed Phase: /* Embed ExtensionKit Extensions / = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = "$(EXTENSIONS_FOLDER_PATH)"; dstSubfolderSpec = 16; files = ( / YourReportExtension.appex in Embed ExtensionKit Extensions */, ); name = "Embed ExtensionKit Extensions"; runOnlyForDeploymentPostprocessing = 0; };

Key settings:

  • dstSubfolderSpec = 16 (Extensions folder, NOT 13/PlugIns)
  • dstPath = "$(EXTENSIONS_FOLDER_PATH)" ← Critical!
  • productType = "com.apple.product-type.extensionkit-extension"
  • explicitFileType = "wrapper.extensionkit-extension"

Info.plist (EXAppExtensionAttributes only, no NSExtension): <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>EXAppExtensionAttributes</key> <dict> <key>EXExtensionPointIdentifier</key> <string>com.apple.deviceactivityui.report-extension</string> </dict> </dict> </plist>

The missing piece for me was the explicit dstPath = "$(EXTENSIONS_FOLDER_PATH)" - without it, the extension wouldn't render properly even when in the Extensions folder.

Hope this helps others hitting this wall!