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

> Discover the lazy-load caching in EFQRCode recognizer. Learn how contentArray stores results and invalidates when CGImage changes for optimal performance.

- Repository: [EFPrefix/efqrcode](https://github.com/efprefix/efqrcode)
- Tags: deep-dive
- Published: 2026-03-01

---

**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:

```swift
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`:

```swift
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:

```swift
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:

```swift
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:

```swift
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:

```swift
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.