Test iOS QR Code Exports on a Cloud Mac

AI Automation ·~5 min read

Test iOS QR Code Exports on a Cloud Mac

Before a release, an app may save an entry pass as a QR code PNG that looks fine in the UI preview, yet fails when scanned. Scaling interpolation, cropped edges, or insufficient contrast between the foreground and background can all cause problems. Instead of merely asserting that the file exists, retrieve the image the app actually exported on a cloud Mac, decode it with Vision, and compare the result with the expected content. This makes a useful file-level check in an iOS regression suite.

Define the acceptance criteria

This check uses the PNG actually written by the app, not a QR code generated by the test script. The fixed test payload is case-123; the app’s test entry point should write the corresponding image to Documents/qr-sample.png in its sandbox. If the product only displays the QR code on screen, use its existing export feature to obtain a file. Do not build a separate test generator and treat its output as the app’s production output.

There are three acceptance criteria: the file can be retrieved from the app container; Vision detects exactly one QR code; and the decoded string matches the expected value character for character. A file hash is not a good primary assertion: changes to encoding settings or PNG metadata can change the bytes without changing the QR code’s content.

“Readable” here applies only to the exported digital image. Camera focus, screen brightness, and print size introduce additional variables; file-level acceptance cannot replace a real scan.

Retrieve the final file from the simulator

First, run the test entry point in a booted iOS simulator and confirm that the app writes to the agreed path. The following commands require BUNDLE_ID to be set to the app’s actual bundle identifier. get_app_container returns the path to that app’s data container.

test -n "$BUNDLE_ID" || exit 2
APP_DATA=$(xcrun simctl get_app_container booted "$BUNDLE_ID" data) || exit 1
mkdir -p artifacts
cp "$APP_DATA/Documents/qr-sample.png" artifacts/qr-sample.png
file artifacts/qr-sample.png

If cp fails, first check that the app ran in the currently booted simulator and that the export logic wrote to Documents. Do not substitute a same-named image from the development machine: that would bypass the app in the regression check. If the app produces multiple files on each run, have the test entry point use a stable filename and remove old files before each run so the check cannot pick up a previous result.

Decode the QR code with Vision and compare the payload

Save the following as qr-check.swift in the working directory. The script takes a PNG path and the expected text. It exits with a nonzero status if the input cannot be analyzed, detection fails, or the result does not match, so the pipeline can use its exit status directly.

import Foundation
import Vision

guard CommandLine.arguments.count == 3 else {
    fputs("usage: swift qr-check.swift IMAGE EXPECTED\n", stderr)
    exit(2)
}

let url = URL(fileURLWithPath: CommandLine.arguments[1])
let expected = CommandLine.arguments[2]
let request = VNDetectBarcodesRequest()
request.symbologies = [.qr]

do {
    try VNImageRequestHandler(url: url, options: [:]).perform([request])
    let codes = (request.results ?? []).filter { $0.symbology == .qr }
    guard codes.count == 1,
          codes[0].payloadStringValue == expected else {
        fputs("QR count or payload mismatch\n", stderr)
        exit(1)
    }
    print("QR payload verified")
} catch {
    fputs("QR image could not be analyzed\n", stderr)
    exit(1)
}

Run swift qr-check.swift artifacts/qr-sample.png case-123. Do not check only the detection count: a successfully decoded code could still contain an old order number, an incorrect prefix, or truncated text. Do not put real credentials in test data, either. If the business payload contains sensitive fields, use deliberately invalid test values that still exercise the format and parsing logic.

Diagnose failures layer by layer

Inspect the exported file first, then the detection result, and only then revisit the QR code generation code. This separates “nothing was exported” from “the export is unreadable.”

Symptom Check first
PNG not found Simulator selection, bundle identifier, sandbox path, and cleanup of old files
Vision finds no code Whether the image was cropped, reduced to a small size, or processed with a lossy method
Multiple codes detected Whether another QR code ended up in the exported image
Payload mismatch Text before encoding, character encoding, caching, and export timing

If the app generates QR codes with Core Image, preserve the full bounds of the generator’s outputImage, scale it up by an integer factor, and then write the PNG. Check whether the share or screenshot path scales it down again with interpolation. A smooth-looking preview does not mean the square edges remain sharp. For dark themes, also inspect the colors in the final file: the UI background and the exported image background may differ, and transparent areas can lose contrast against another background color.

Keep the check from producing false results

Use one fixed test image, one explicit payload, and one output path, but retrieve the image from the current app run every time. To cover payloads of different lengths, non-ASCII characters, or delimiters, create separate test cases and compare each result individually. Do not accept a match against any one of several expected strings. Successfully decoding a QR code also does not prove that a business link works; if navigation needs acceptance testing, check parsing and routing in a separate test layer.

Add the result to the regression workflow

After the existing simulator tests, run the file retrieval commands and the Swift script. Let a nonzero exit status from either step fail the run. On failure, retain that run’s PNG and the script’s error output to determine whether the export path changed or image processing caused a regression. On success, recording the acceptance result is enough; there is no need to keep images containing business data indefinitely.

The final check covers four things: the image comes from the app’s final output; it is not left over from a previous run; Vision reads exactly one code; and the payload matches the fixed test value exactly. With those checks in place, file-level QR code regression testing has a clear, repeatable boundary. For on-screen display or printed delivery, add a real-device scanning check.

Frequently asked questions

Can Vision replace a scan with a physical device?

No. It verifies that the file decodes and contains the expected data; screen brightness, camera focus, and print size still need real-world checks.

Why not generate the QR code inside the test script?

That would only test the script. Checking the app's final PNG also catches scaling, color, cropping, and export defects in the app.

Choose a rental term

Validate your next step on a dedicated physical Mac mini

VMCommit offers dedicated physical M4 Mac mini rentals by the day, week, month, or quarter. Choose a model and location, then check real-time availability in the console.

Rent a Mac mini