How the Caching Mechanism in EFQRCode Recognizer Works: A Deep Dive into Lazy-Load Detection

The EFQRCode recognizer implements a lazy-load caching mechanism through the private contentArray property in Source/EFQRCode+Recognizer.swift, which stores QR code detection results after the first recognize() call and automatically invalidates when the underlying CGImage changes.

The efprefix/efqrcode repository provides a Swift library for QR code generation and recognition. Understanding the caching mechanism in EFQRCode recognizer is essential for optimizing performance in applications that repeatedly access QR code data from the same image, as it eliminates redundant Core Image detection operations.

Where the Cache Lives in EFQRCode

The contentArray Property

In Source/EFQRCode+Recognizer.swift, the caching mechanism centers on a private optional array defined at lines 75-77:

private var contentArray: [String]?

This property holds the decoded QR code strings after the first successful detection. It remains nil until the initial call to recognize(), functioning as a lazy-load cache that persists across multiple recognition attempts on the same Recognizer instance. Because the property stores an array of strings, it supports caching multiple QR codes detected within a single image.

How Cache Invalidation Works

The Image Property Observer

The caching mechanism automatically invalidates stored results when the input image changes. The image property includes a didSet observer at lines 69-73 that resets contentArray to nil:

var image: CGImage? {
    didSet {
        contentArray = nil
    }
}

When a new CGImage is assigned to the recognizer, this observer immediately clears the cache. This guarantees that subsequent calls to recognize() trigger fresh Core Image detection rather than returning stale results from the previous image.

The Recognition Flow with Caching

Checking the Cache in recognize()

The recognize() method at lines 99-104 implements the caching logic by checking contentArray before invoking expensive detection operations:

func recognize() -> [String] {
    if let contentArray = contentArray {
        return contentArray
    }
    let result = getQRString()
    contentArray = result
    return result
}

If contentArray contains a value, the method returns the cached array immediately without Core Image overhead. Otherwise, it calls getQRString() to perform detection, stores the result in the cache, and returns the decoded strings.

The Detection Fallback in getQRString()

When the cache misses, getQRString() at lines 15-24 executes the actual QR code detection using Core Image. It implements a two-pass strategy for robust recognition:

private func getQRString() -> [String] {
    // High accuracy attempt on original image
    var result = image.recognizeQRCode(options: [.accuracy: CIQRCodeDetectorAccuracyHigh])
    if result.isEmpty {
        // Fallback: grayscale + low accuracy
        let grayscale = image.grayscale()
        result = grayscale.recognizeQRCode(options: [.accuracy: CIQRCodeDetectorAccuracyLow])
    }
    return result
}

The method first attempts high-accuracy detection on the original image. If no codes are found, it creates a grayscale version using the grayscale() extension and retries with low accuracy settings. This detection result is then cached by the recognize() method for subsequent calls.

Practical Implementation Example

The following example demonstrates the caching behavior in a real-world iOS scenario:

import CoreGraphics
import EFQRCode

// Initialize recognizer with a CGImage
let recognizer = EFQRCode.Recognizer(image: qrCodeCGImage)

// First call triggers Core Image detection and caches results
let firstDetection = recognizer.recognize()  // Expensive operation executes

// Subsequent calls return cached results instantly
let cachedResult = recognizer.recognize()    // Returns immediately from contentArray

// Changing the image invalidates the cache automatically
recognizer.image = differentCGImage
let newDetection = recognizer.recognize()    // Fresh detection runs for new image

For UIKit applications, the convenience extension provides the same caching benefits:

import UIKit

if let cgImage = uiImage.cgImage {
    // Recognizer initializes and caches on first use
    let contents = EFQRCode.Recognizer(image: cgImage).recognize()
    // Repeated access to the same recognizer instance returns cached data
    print(contents)
}

Summary

  • The EFQRCode recognizer implements lazy-load caching through the private contentArray property in Source/EFQRCode+Recognizer.swift, storing decoded QR strings after the first detection.
  • Cache invalidation occurs automatically via the image property's didSet observer, which resets contentArray to nil whenever the underlying CGImage changes.
  • The recognize() method checks the cache first, returning stored results immediately or calling getQRString() to perform Core Image detection and populate the cache.
  • Detection includes a fallback strategy: high-accuracy detection first, followed by grayscale conversion and low-accuracy detection if no codes are found, with results cached for subsequent calls.

Frequently Asked Questions

How does the EFQRCode recognizer cache detection results?

The recognizer stores detection results in a private property named contentArray of type [String]? defined in Source/EFQRCode+Recognizer.swift. When recognize() is called, it first checks whether contentArray contains a value. If the array is not nil, the method returns the cached strings immediately. If the cache is empty, the method calls getQRString() to perform Core Image detection, stores the returned array in contentArray, and returns the results.

When is the cache cleared in EFQRCode?

The cache clears automatically whenever the image property of the Recognizer instance changes. The property includes a didSet observer at lines 69-73 in Source/EFQRCode+Recognizer.swift that sets contentArray to nil whenever a new CGImage is assigned. This invalidation mechanism ensures that subsequent calls to recognize() perform fresh detection rather than returning stale results from a previous image.

Can the EFQRCode recognizer cache multiple QR codes from a single image?

Yes, the contentArray property is defined as an optional array of strings [String]?, allowing it to store multiple decoded QR code contents simultaneously. When getQRString() detects several QR codes within a single image, it returns all decoded strings in an array. The recognize() method caches this entire array in contentArray, ensuring that subsequent calls return all detected codes without reprocessing the image, regardless of how many QR codes are present.

What happens if the first QR code detection attempt fails?

If the initial high-accuracy detection returns an empty array, the getQRString() method implements a fallback strategy defined at lines 15-24 in Source/EFQRCode+Recognizer.swift. The method creates a grayscale version of the input image using the grayscale() extension method and retries detection with low accuracy settings. This two-pass approach improves recognition rates for difficult or low-contrast QR codes while maintaining the performance benefits of caching for successful detections.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →