134 lines
3.6 KiB
Swift
134 lines
3.6 KiB
Swift
//
|
|
// QRCodeGenerator.swift
|
|
// Bedrock
|
|
//
|
|
// Generates QR codes from string payloads.
|
|
//
|
|
|
|
import SwiftUI
|
|
#if canImport(CoreImage)
|
|
import CoreImage
|
|
import CoreImage.CIFilterBuiltins
|
|
#endif
|
|
|
|
/// Generates QR codes from string data.
|
|
///
|
|
/// Use this to create QR codes for URLs, vCards, or any text payload.
|
|
///
|
|
/// ## Example
|
|
///
|
|
/// ```swift
|
|
/// if let qrImage = QRCodeGenerator.generate(from: "https://example.com") {
|
|
/// Image(decorative: qrImage, scale: 1.0)
|
|
/// }
|
|
/// ```
|
|
public enum QRCodeGenerator {
|
|
|
|
/// The CIContext used for rendering.
|
|
#if canImport(CoreImage)
|
|
private static let context = CIContext()
|
|
#endif
|
|
|
|
/// Error correction levels for QR codes.
|
|
public enum CorrectionLevel: String, Sendable {
|
|
/// Low (7% recovery)
|
|
case low = "L"
|
|
/// Medium (15% recovery)
|
|
case medium = "M"
|
|
/// Quartile (25% recovery)
|
|
case quartile = "Q"
|
|
/// High (30% recovery)
|
|
case high = "H"
|
|
}
|
|
|
|
/// Generates a QR code image from a string payload.
|
|
/// - Parameters:
|
|
/// - payload: The string to encode.
|
|
/// - correctionLevel: Error correction level (default: medium).
|
|
/// - scale: Scale factor for the output image (default: 10).
|
|
/// - Returns: A CGImage of the QR code, or nil if generation fails.
|
|
public static func generate(
|
|
from payload: String,
|
|
correctionLevel: CorrectionLevel = .medium,
|
|
scale: CGFloat = 10
|
|
) -> CGImage? {
|
|
#if canImport(CoreImage)
|
|
let data = Data(payload.utf8)
|
|
let filter = CIFilter.qrCodeGenerator()
|
|
filter.setValue(data, forKey: "inputMessage")
|
|
filter.correctionLevel = correctionLevel.rawValue
|
|
|
|
guard let outputImage = filter.outputImage else { return nil }
|
|
|
|
let scaledImage = outputImage.transformed(
|
|
by: CGAffineTransform(scaleX: scale, y: scale)
|
|
)
|
|
|
|
return context.createCGImage(scaledImage, from: scaledImage.extent)
|
|
#else
|
|
_ = payload
|
|
_ = correctionLevel
|
|
_ = scale
|
|
return nil
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// MARK: - SwiftUI View
|
|
|
|
/// A SwiftUI view that displays a QR code.
|
|
///
|
|
/// ## Example
|
|
///
|
|
/// ```swift
|
|
/// QRCodeImageView(payload: card.vCardPayload)
|
|
/// .frame(width: 200, height: 200)
|
|
/// ```
|
|
public struct QRCodeImageView: View {
|
|
/// The string to encode as a QR code.
|
|
public let payload: String
|
|
|
|
/// Error correction level.
|
|
public let correctionLevel: QRCodeGenerator.CorrectionLevel
|
|
|
|
/// Creates a QR code view.
|
|
/// - Parameters:
|
|
/// - payload: The string to encode.
|
|
/// - correctionLevel: Error correction level (default: medium).
|
|
public init(
|
|
payload: String,
|
|
correctionLevel: QRCodeGenerator.CorrectionLevel = .medium
|
|
) {
|
|
self.payload = payload
|
|
self.correctionLevel = correctionLevel
|
|
}
|
|
|
|
public var body: some View {
|
|
if let cgImage = QRCodeGenerator.generate(
|
|
from: payload,
|
|
correctionLevel: correctionLevel
|
|
) {
|
|
Image(decorative: cgImage, scale: 1.0)
|
|
.interpolation(.none)
|
|
.resizable()
|
|
.scaledToFit()
|
|
.accessibilityLabel("QR code")
|
|
} else {
|
|
Image(systemName: "qrcode")
|
|
.font(.largeTitle)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
VStack(spacing: Design.Spacing.large) {
|
|
QRCodeImageView(payload: "https://example.com")
|
|
.frame(width: 150, height: 150)
|
|
|
|
QRCodeImageView(payload: "BEGIN:VCARD\nFN:John Doe\nEND:VCARD")
|
|
.frame(width: 150, height: 150)
|
|
}
|
|
.padding()
|
|
}
|