QR Code Generation with CIFilter and Custom Deep Link
While adding the class-join feature to our Music Theory Learning app, I extended the Core Image approach described in "SwiftUI QR Code Generation" to generate QR codes for deep linking. No extra library required, just a `CIFilter` and a few lines of Swift.
Creating a scannable QR code in SwiftUI takes only a few lines using `CIFilter.qrCodeGenerator()`. Combined with a custom URL scheme (`musicnotes://join?code=...`), students can scan the code and join a class with a single tap; all without a third-party dependency.
The sample code also includes an example of handling the incoming deep link in `SceneDelegate`.
struct QRCodeView: View {
let url: String
private let context = CIContext()
private let filter = CIFilter.qrCodeGenerator()
func generateQRCode(from string: String) -> UIImage {
filter.message = Data(string.utf8)
filter.setValue("H", forKey: "inputCorrectionLevel") // High error correction
if let outputImage = filter.outputImage {
let transform = CGAffineTransform(scaleX: 10, y: 10)
let scaledImage = outputImage.transformed(by: transform)
if let cgImage = context.createCGImage(scaledImage, from: scaledImage.extent) {
return UIImage(cgImage: cgImage)
}
}
return UIImage(systemName: "xmark.circle") ?? UIImage()
}
var body: some View {
Image(uiImage: generateQRCode(from: url))
.interpolation(.none) // crucial – keeps pixels crisp
.resizable()
.scaledToFit()
}
}
// Deep link target: musicnotes://join?code=MUSIC-A3B7K2
// Receiving the link
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, openURLContexts URLContexts: Set< UIOpenURLContext >) {
if let ctx = URLContexts.first(where: { $0.url.scheme == "musicnotes" }) {
let url = ctx.url
if url.host == "join" || url.path.hasPrefix("/join") {
// Handle join class URLs: musicnotes://join?code=MUSIC-A3B7K2
handleJoinClassURL(url)
}
}
}
private func handleJoinClassURL(_ url: URL) {
// Parse the class code from URL parameters
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let codeParam = components.queryItems?.first(where: { $0.name == "code" }),
let classCode = codeParam.value else {
logger.debug("\(#function): Invalid join URL: missing code parameter")
return
}
// Handle the class code in the app
// ...
}
}
