Permissions

Harness exposes a single top-level permissions flag:

{
  permissions: true,
}

This setting is currently all-or-nothing. You can enable permission automation for the whole run or disable it completely. Harness does not yet support selecting individual permission types or per-platform overrides.

Current Behavior

When permissions is false:

  • Harness does not start permission automation helpers.
  • On iOS, this means the XCTest-based permission helper is not started.

When permissions is true:

  • On iOS simulators, Harness starts the XCTest agent and enables best-effort permission prompt auto-accept.
  • On iOS physical devices, Harness also starts the XCTest agent when the runner is configured with device.codeSign.
  • On Android emulators and physical devices, Harness uses adb shell pm grant to automatically grant the app's requested dangerous permissions.

Platform-Specific Implementation Details

iOS

The iOS implementation does not pre-approve every permission up front. Instead, it runs a best-effort watchdog that taps known positive system-prompt buttons such as:

  • Allow
  • OK
  • Continue
  • While Using the App
  • Always Allow
  • Allow Once

Because this is button-based automation, it should be treated as a practical helper for common prompts rather than a guarantee that every permission dialog will be handled.

Android

On Android, Harness inspects the installed app and grants only the permissions that meet both of these conditions:

  • The app declares the permission in its manifest
  • The permission is currently classified by the device as a dangerous permission

This approach:

  • Grants permissions proactively before the app runs, avoiding permission dialogs during testing
  • Works on both emulators and physical devices
  • Avoids trying to grant normal permissions such as INTERNET that do not require a runtime grant
  • Uses adb shell pm grant for each matching permission

In practice, apps commonly end up having permissions such as camera, microphone, location, contacts, calendar, storage, SMS, or sensors granted automatically, but the exact set depends on what the app requests.

Performance Impact

Enabling permissions can make Harness heavier on iOS because it starts an additional XCTest agent session alongside the normal run.

This can have a negative impact on:

  • startup time
  • device preparation time
  • overall run performance

The impact is most important to keep in mind for iOS physical devices, where the XCTest agent requires code signing and extra setup, but the same helper is also started on iOS simulators when permissions are enabled.

Where the XCTest agent comes from

Harness needs a built XCTest agent before it can start one. It obtains one from the first source that applies:

  1. An .xctestrun you provide. Set HARNESS_IOS_XCTESTRUN_FILE (optionally with HARNESS_IOS_XCTEST_DERIVED_DATA_PATH) to a ready .xctestrun, and Harness uses it as-is.
  2. Artifacts shipped with the platform package. Releases carry a prebuilt unsigned agent for simulators and one for physical devices. Harness copies one into .harness/cache and runs it, so no compilation happens. A simulator copy runs as copied; a device copy is signed first, as described below.
  3. A local build. Harness runs xcodebuild build-for-testing and caches the result.

The shipped artifacts work from Xcode 26 up to the major Xcode version the release was packed with. On any other Xcode, or when the agent sources in your installed package were modified, Harness says so and builds locally instead, so a new Xcode costs one local build until a release ships artifacts packed with it. Set HARNESS_IOS_XCTEST_PREBUILT_ROOT to point Harness at artifacts packed elsewhere, and HARNESS_SKIP_XCTEST_PREBUILDS=1 to skip them entirely.

Running on a physical device from shipped artifacts

A device build cannot ship ready to run, because a runnable bundle carries your certificate and a profile that lists your device. So Harness ships device artifacts unsigned and signs a private copy on your machine, in the same .harness/cache location, using the certificate and profile you configure. This needs no network access and takes about a second.

Harness picks the certificate by matching the certificates embedded in your provisioning profile against the usable identities in your keychain. It picks the profile by requiring that it covers the agent's bundle ids, lists your device, and has not expired. When no installed profile qualifies, Harness builds locally instead, which is the path that can reach the Developer Portal to obtain one.

Two settings matter when signing is not automatic:

  • signingIdentity narrows the choice to one certificate. codesign refuses the name Apple Development outright once your keychain holds two development certificates, so name the certificate by its SHA-1 if you have several accounts.
  • bundleIdPrefix rewrites the agent's own bundle ids. Individual developer accounts and many corporate ones cannot mint a profile covering com.callstackincubator.*, and a prefix that your wildcard profile does cover is the way around that. It must be a reverse-DNS prefix such as com.example.myapp. It rewrites the three bundle Info.plists along with the bundle ids the .xctestrun names, so the test session launches what was installed. It applies to the artifact that ships with the package: a locally built agent keeps the shipped ids, because xcodebuild cannot override one target's bundle id without giving both agent targets the same one.
applePlatform({
  name: 'iphone',
  device: applePhysicalDevice('My iPhone', {
    codeSign: {
      teamId: 'TEAMID1234',
      bundleIdPrefix: 'com.example.myapp',
      signingIdentity: 'A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2',
      provisioningProfile: 'My Wildcard Profile',
    },
  }),
  bundleId: 'com.example.myapp',
})

Device artifacts ship unsigned and generic, so they carry nothing specific to your account. They add roughly half again to the package download. Pack a narrower set with HARNESS_XCTEST_PREBUILD_DESTINATIONS=simulator, or point Harness at artifacts packed elsewhere with HARNESS_IOS_XCTEST_PREBUILT_ROOT.

Physical iOS Devices

To use permission automation on a physical iOS device, the runner must provide device.codeSign.

applePlatform({
  name: 'iphone',
  device: applePhysicalDevice('My iPhone', {
    codeSign: { teamId: 'TEAMID1234' },
  }),
  bundleId: 'com.example.myapp',
})

If permissions: true is set without device.codeSign, Harness skips the XCTest agent for that physical device.

Recommendation

Leave permissions disabled unless your tests actually need system permission prompts to be handled automatically.

That keeps the default Harness startup path lighter and avoids paying the extra iOS XCTest overhead on runs that do not need it.

Need React or React Native expertise you can count on?