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
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}`);
});
},
);