Skip to main content
This feature works with any app, regardless of whether it uses Apple’s built-in scanning or a third-party library like ZXing or Google ML Kit.

Examples

Inject a QR code:
// Inject QR Code
const ios = await import("@qawolf/run-globals-ios");

const { bundleId } = await driver.execute("mobile: activeAppInfo");

const cleanup = await ios.injectBarcode(driver, bundleId, {
  value: "https://qawolf.com/qr-code",
});

// ... assert that the app handled the scanned QR code ...

await cleanup();
Inject a barcode:
// Inject EAN-13 Barcode
const ios = await import("@qawolf/run-globals-ios");

const { bundleId } = await driver.execute("mobile: activeAppInfo");

const cleanup = await ios.injectBarcode(driver, bundleId, {
  type: "org.gs1.EAN-13",
  value: "1234567890123",
});

// ... assert that the app handled the scanned barcode ...

await cleanup();

When to use

  • Your app scans QR codes to trigger navigation, deep links, or actions
  • Your app scans barcodes for product lookups, ticketing, or payments
  • You need to test scanning flows without a physical code or camera setup
  • You need to run the same scanning scenario repeatedly with consistent results

Supported formats

2D Codes:
  • QR Code (org.iso.QRCode)
  • Aztec Code (org.iso.Aztec)
  • PDF417 (org.iso.PDF417)
  • Data Matrix (org.iso.DataMatrix)
  • Micro QR Code (org.iso.MicroQRCode)
1D Barcodes:
  • EAN-13/EAN-8 (org.gs1.EAN)
  • UPC-A/UPC-E (org.gs1.UPC)
  • Code 128 (org.gs1.Code128)
  • Code 39 (org.gs1.Code39)
  • Code 93 (org.gs1.Code93)
  • ITF-14 (org.gs1.ITF14)
  • Interleaved 2 of 5 (org.gs1.Interleaved2of5)
  • Codabar (org.gs1.Codabar)

Advanced: Multiple objects

Some apps expect the camera to detect multiple codes simultaneously. To inject multiple detections in a single call:
// Inject Multiple Barcodes (delivered to the delegate in one batch)
const ios = await import("@qawolf/run-globals-ios");

const cleanup = await ios.injectBarcode(driver, bundleId, [
  { type: "org.iso.QRCode",  value: "https://example.com/qr-deep-link" },
  { type: "org.gs1.EAN",     value: "1234567890123" },
  { type: "org.gs1.Code128", value: "PRODUCT-SKU-12345" },
 ]);

// ... assert that the app handled all scanned codes ...

await cleanup();

Advanced: Custom bounds and corners

Some apps use the position of the detected code in the view. To include position data:
// Inject QR Code with Custom Bounds and Corners
const ios = await import("@qawolf/run-globals-ios");

const cleanup = await ios.injectBarcode(driver, bundleId, {
  type: "org.iso.QRCode",
  value: "https://example.com",
  bounds: {
    x: 0.3, // normalized coordinates (0.0 - 1.0)
    y: 0.2,
    width: 0.4,
    height: 0.6,
  },
  corners: [
    { x: 0.3, y: 0.2 }, // top-left
    { x: 0.7, y: 0.2 }, // top-right
    { x: 0.7, y: 0.8 }, // bottom-right
    { x: 0.3, y: 0.8 }, // bottom-left
  ],
});

// ... assert that the app handled the scanned QR code at the expected position ...

await cleanup();

Advanced: Raw binary data

To include raw binary data alongside the decoded value:
// Inject QR Code with Raw Binary Data (iOS 13+)
const ios = await import("@qawolf/run-globals-ios");

const cleanup = await ios.injectBarcode(driver, bundleId, {
  type: "org.iso.QRCode",
  value: "https://example.com",
  rawValue: Buffer.from("custom binary data"), // also accepts a base64-encoded string
});

// ... assert the app received the raw payload ...

await cleanup();

Default values

If not specified, the following defaults apply:
  • type: org.iso.QRCode
  • bounds: { x: 0.38, y: 0.27, width: 0.32, height: 0.57 }
  • corners: Calculated automatically from bounds
  • rawValue: UTF-8 encoding of the value string

Full sample test

import { flow } from "@qawolf/flows/ios";
import { expect } from "@qawolf/flows/web";
import * as ios from "@qawolf/run-globals-ios";

export default flow(
  "iOS Media - AVMetadata Injection",
  "iOS - iPhone 15 (iOS 26)",
  async ({ wdio, test, ...testContext }) => {
    let driver;
    await test("App install and open AVMetadata", async () => {
      driver = await wdio.startIos({
        "appium:app": process.env.IOS_APP_STAGING,
        "appium:settings[respectSystemAlerts]": true,
        "appium:autoAcceptAlerts": true,
        "appium:iosInstallPause": "5000",
      });

      // Tap "Media"
      await driver
        .$(
          `-ios predicate string:name == 'Media' AND type == 'XCUIElementTypeButton'`,
        )
        .waitForDisplayed();
      await driver
        .$(
          `-ios predicate string:name == 'Media' AND type == 'XCUIElementTypeButton'`,
        )
        .click();

      // Click media dropdown
      await driver
        .$(
          `-ios predicate string:name == 'AVMetadata (Barcode/QRCode)' AND type == 'XCUIElementTypeStaticText'`,
        )
        .waitForDisplayed();
      await driver
        .$(
          `-ios predicate string:name == 'AVMetadata (Barcode/QRCode)' AND type == 'XCUIElementTypeStaticText'`,
        )
        .click();
    });
    await test("QR Code Test", async () => {
      const qrCodeLinkToInject = "https://qawolf.com/qr-code";

      await ios.injectBarcode(driver, process.env.APP_ID, {
        value: qrCodeLinkToInject,
      });

      await driver.pause(3000);

      //--------------------------------
      // Assert:
      //--------------------------------
      expect(
        await driver
          .$(
            `-ios predicate string:name == 'qrLastCodeLabel' AND type == 'XCUIElementTypeStaticText'`,
          )
          .getText(),
      ).toBe(`QR Code: ${qrCodeLinkToInject}`);
    });
    await test("Barcode Scanning Test", async () => {
      const barcodeToInject = "1234567890123";

      await ios.injectBarcode(driver, process.env.APP_ID, {
        type: "org.gs1.EAN-13",
        value: barcodeToInject,
      });

      await driver.pause(3000);

      //--------------------------------
      // Assert:
      //--------------------------------
      expect(
        await driver
          .$(
            `-ios predicate string:name == 'qrLastCodeLabel' AND type == 'XCUIElementTypeStaticText'`,
          )
          .getText(),
      ).toBe(`EAN-13: ${barcodeToInject}`);
    });
  },
);
Last modified on April 17, 2026