Search
Loading...
Skip to content

Insert QR Code

QR codes are a practical way to turn any design into a scannable gateway for:

  • Landing pages
  • App installs
  • Product info
  • Event tickets
  • You name it

CE.SDK doesn’t include a built-in QR generator, but you can create the image with Core Image in just a few lines and place it on the canvas as an image fill. This guide shows the full workflow with Swift examples.

What You’ll Learn#

  • Generate a QR code image from a String using Core Image.
  • Add it to a CE.SDK scene as an image fill on a shape block.
  • Control size, position, and color for brand consistency.
  • Store metadata for quick regeneration later.

When You’ll Use It#

  • Business cards, flyers, or packaging that need a scannable link.
  • Apps that let users personalize templates with their own URLs.
  • Automated workflows that embed links into generated designs.

Platform Setup#

The example uses type aliases to abstract platform differences between iOS (UIKit) and macOS (AppKit). PlatformColor maps to UIColor or NSColor, and PlatformImage maps to UIImage or NSImage.

import CoreImage.CIFilterBuiltins
import IMGLYEngine
import SwiftUI
#if canImport(UIKit)
import UIKit
private typealias PlatformColor = UIColor
private typealias PlatformImage = UIImage
#elseif canImport(AppKit)
import AppKit
private typealias PlatformColor = NSColor
private typealias PlatformImage = NSImage
#endif

Generate a QR Code Image#

Use Core Image to create a high-resolution QR code, then colorize it to match your brand.

/// Generate a QR code with brand colors.
/// - Parameters:
/// - string: Content to encode (use a full URL with scheme).
/// - correction: Error correction level (L, M, Q, H). "M" is a good default.
/// - scale: Pixel scale factor (increase for print).
/// - foreground: Dark module color.
/// - background: Light background color.
private func makeQRCode(
from string: String,
correction: String = "M",
scale: CGFloat = 10,
foreground: PlatformColor = .black,
background: PlatformColor = .white,
) -> PlatformImage? {
guard let data = string.data(using: .utf8) else { return nil }
let qr = CIFilter.qrCodeGenerator()
qr.setValue(data, forKey: "inputMessage")
qr.setValue(correction, forKey: "inputCorrectionLevel")
guard let output = qr.outputImage else { return nil }
// Map black/white to brand colors
let falseColor = CIFilter.falseColor()
falseColor.inputImage = output
#if canImport(UIKit)
falseColor.color0 = CIColor(color: foreground)
falseColor.color1 = CIColor(color: background)
#elseif canImport(AppKit)
falseColor.color0 = CIColor(color: foreground) ?? CIColor.black
falseColor.color1 = CIColor(color: background) ?? CIColor.white
#endif
guard let colored = falseColor.outputImage else { return nil }
// Scale up without interpolation
let scaled = colored.transformed(by: CGAffineTransform(scaleX: scale, y: scale))
let context = CIContext(options: [.useSoftwareRenderer: false])
guard let cg = context.createCGImage(scaled, from: scaled.extent) else { return nil }
#if canImport(UIKit)
return UIImage(cgImage: cg, scale: 1.0, orientation: .up)
#elseif canImport(AppKit)
return NSImage(cgImage: cg, size: NSSize(width: cg.width, height: cg.height))
#endif
}

Keep the foreground dark and the background light for reliable scanning.

Insert the QR as an Image Fill#

Create a graphic block, assign it a rect shape, and fill it with your generated QR image.

@MainActor
func insertQRCode(
engine: Engine,
page: DesignBlockID,
urlString: String,
position: CGPoint = .init(x: 200, y: 200),
size: CGFloat = 160,
) async throws -> DesignBlockID {
guard let qr = makeQRCode(from: urlString, correction: "M", scale: 10, foreground: .black, background: .white) else {
throw NSError(domain: "QR", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to generate QR image"])
}
// Get PNG data from the image (platform-specific)
#if canImport(UIKit)
guard let png = qr.pngData() else {
throw NSError(domain: "QR", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to encode QR as PNG"])
}
#elseif canImport(AppKit)
guard let tiffRepresentation = qr.tiffRepresentation,
let bitmap = NSBitmapImageRep(data: tiffRepresentation),
let png = bitmap.representation(using: .png, properties: [:]) else {
throw NSError(domain: "QR", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to encode QR as PNG"])
}
#endif
let fileURL = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
.appendingPathExtension("png")
try png.write(to: fileURL)
// Create a visible graphic block with a rect shape
let graphic = try engine.block.create(.graphic)
let rectShape = try engine.block.createShape(.rect)
try engine.block.setShape(graphic, shape: rectShape)
// Create an image fill and point it to the QR file URL
let imageFill = try engine.block.createFill(.image)
try engine.block.setString(imageFill, property: "fill/image/imageFileURI", value: fileURL.absoluteString)
try engine.block.setFill(graphic, fill: imageFill)
// Size & position (keep square)
try engine.block.setWidth(graphic, value: Float(size))
try engine.block.setHeight(graphic, value: Float(size))
try engine.block.setPositionX(graphic, value: Float(position.x))
try engine.block.setPositionY(graphic, value: Float(position.y))
// Optional metadata for future updates
try? engine.block.setMetadata(graphic, key: "qr/url", value: urlString)
// Add to page
try engine.block.appendChild(to: page, child: graphic)
return graphic
}

The preceding code creates a QR code and then saves it to a temporary directory to generate a file URL the block can use.

Add Optional Metadata#

Store the URL alongside the block for quick updates later. Metadata key values are anything you want. The key and the value must be String types.

// Optional metadata for future updates
try? engine.block.setMetadata(graphic, key: "qr/url", value: urlString)

Update an Existing QR Code#

If data changes, just regenerate the QR image and update the fill URI.

/// Update an existing QR code block with a new URL.
/// - Parameters:
/// - engine: The CE.SDK engine instance.
/// - qrBlock: The existing QR code block to update.
/// - newURL: The new URL to encode.
@MainActor
func updateQRCode(engine: Engine, qrBlock: DesignBlockID, newURL: String) throws {
guard let qr = makeQRCode(from: newURL) else { return }
// Get PNG data from the image (platform-specific)
#if canImport(UIKit)
guard let png = qr.pngData() else { return }
#elseif canImport(AppKit)
guard let tiffRepresentation = qr.tiffRepresentation,
let bitmap = NSBitmapImageRep(data: tiffRepresentation),
let png = bitmap.representation(using: .png, properties: [:]) else { return }
#endif
let fileURL = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
.appendingPathExtension("png")
try png.write(to: fileURL)
let fill = try engine.block.getFill(qrBlock)
try engine.block.setString(fill, property: "fill/image/imageFileURI", value: fileURL.absoluteString)
try? engine.block.setMetadata(qrBlock, key: "qr/url", value: newURL)
}

To generate many QR codes, for instance during a batch run, loop through your data and call insertQRCode for each.

Troubleshooting#

SymptomCauseSolution
QR looks blurryImage scaled too smallIncrease the Core Image scale and block size.
QR won’t scanLow contrast or invalid URLUse dark-on-light colors and percent-encode URLs.
QR not visibleShape missing from blockCall setShape before applying the fill.
App crash writing fileInvalid temp URLAlways use FileManager.default.temporaryDirectory.

Next Steps#

Now that you can generate QR codes, here are some related guides to help you learn more. Explore a complete code sample on GitHub.