> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qawolf.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Beacon injection (iOS)

> Simulate iBeacon proximity events in your iOS app without physical beacon hardware.

Use `device.injectBeacon()` to simulate the device detecting a nearby iBeacon, triggering `CLLocationManager` callbacks in your app as if real hardware were present. Pushing a `.region` file triggers a region-entry callback; also pushing a `.beacon` file triggers ranging callbacks.

See the [iOS Device Reference](/qawolf/libraries/flows/api-reference/ios-device-reference) for the full `injectBeacon` API.

## Examples

**Region entry and ranging**

```typescript theme={null}
const cleanup = await device.injectBeacon(driver, "com.example.app", {
  uuid: "8613BEAD-5465-4515-8F9C-AEEA717484C9",
  beacons: [{ major: 1, minor: 7 }],
});

// assert your app responded to the beacon event

await cleanup();
```

**Region entry only (no ranging)**

Omit the `beacons` array to trigger only a region-entry event.

```typescript theme={null}
const cleanup = await device.injectBeacon(driver, "com.example.app", {
  uuid: "e62c96fd-014a-454f-9b41-32245d802bb3",
});

// assert your app responded to the region entry

await cleanup();
```

## When to use

* Your app uses iBeacon proximity for retail check-ins, loyalty triggers, or location-based promotions.
* Your app uses indoor navigation or asset tracking via Bluetooth beacons.
* Your app responds to beacon region entry or exit events.
* Your test needs to verify ranging callbacks with specific major/minor values.

## Location permissions

iBeacons require location permissions. Two important caveats:

**`autoAcceptAlerts` chooses "Ask Next Time" for location dialogs.** If the permission dialog appears, Appium will not choose "Always Allow" — and there is no way to change it after the fact.

**Background location access cannot be auto-accepted.** If your app requires "Change to Always Allow" for background beacon detection, you must handle the dialog manually using `respectSystemAlerts: true`:

```typescript theme={null}
const { driver: d } = await launch({
  bundleId: "com.example.app",
  respectSystemAlerts: true,
});

const allowBtn = d.$(`//XCUIElementTypeButton[@name='Allow']`);
if (await allowBtn.isExisting()) {
  await allowBtn.click();
}
```

<Warning>
  `respectSystemAlerts: true` may slow down Appium performance. Use it only when you need to interact with system dialogs manually.
</Warning>

## Full sample test

```typescript theme={null}
import { device, flow } from "@qawolf/flows/ios";

export default flow(
  "Test beacon proximity",
  { target: "iOS - iPhone 15 (iOS 26)", launch: true },
  async ({ driver, test }) => {
    await test("inject beacon and verify app response", async () => {
      // Arrange
      const allowBtn = driver.$(`//XCUIElementTypeButton[@name='Allow']`);
      if (await allowBtn.isExisting()) {
        await allowBtn.click();
      }

      await driver.$(`//XCUIElementTypeStaticText[@name='Nearby']`).waitForDisplayed({ timeout: 10_000 });

      // Act
      const cleanup = await device.injectBeacon(driver, process.env.BUNDLE_ID, {
        uuid: "8613BEAD-5465-4515-8F9C-AEEA717484C9",
        beacons: [{ major: 1, minor: 7 }],
      });

      // Assert
      await driver
        .$(`//XCUIElementTypeStaticText[@name='Beacon Detected']`)
        .waitForDisplayed({ timeout: 10_000 });

      await cleanup();
    });
  },
);
```
