Blog by Frank

Swift QRCode and Image Processing

QRCode Generator

Generating and scaling up a QR code

//  First, we need to bring in all the Core Image filters using a new import:
import SwiftUI
import CoreImage.CIFilterBuiltins

func generateQRCode(from string: String) -> UIImage {
    
    //  We need two properties to store an active Core Image context
    let context = CIContext()
    //  and an instance of Core Image’s QR code generator filter
    let filter = CIFilter.qrCodeGenerator()
    
    //  Working with Core Image filters requires us to provide some input data.
    //  Our input for the filter will be a string, 
    //  but the input for the filter is Data, 
    //  so we need to convert that.
    filter.message = Data(string.utf8)
    
    //  then convert the output CIImage into a CGImage, 
    //  then that CGImage into a UIImage.
    if let outputImage = filter.outputImage {
        // Return a rect the defines the bounds of non-(0,0,0,0) pixels
        // open var extent: CGRect { get }
        if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
            return UIImage(cgImage: cgimg)
        }
    }
    
    //  If conversion fails for any reason 
    //  we’ll send back the “xmark.circle” image from SF Symbols.
    //  If that can’t be read – which is theoretically possible 
    //  because SF Symbols is stringly typed – then we’ll send back an empty UIImage.
    return UIImage(systemName: "xmark.circle") ?? UIImage()
}

struct ContentView: View {
    var body: some View {
        VStack {
            Image(uiImage: generateQRCode(from: "apple.com"))
            //  take a close look at the QR code – 
            //  do you notice how it’s fuzzy? 
            //  This is because Core Image is generating 
            //  a tiny image, and SwiftUI is trying to 
            //  smooth out the pixels as we scale it up.
            
            //  Line art like QR codes and bar codes is 
            //  a great candidate for disabling image interpolation. 
            //  Try adding and removing .interpolation(.none) modifier 
            //  to the image to see what I mean:
                .interpolation(.none)
                .resizable()
                .scaledToFit()
                .frame(width: 300, height: 300)
        }
    }
}

Extended Reading Materials

Controlling image interpolation in SwiftUI

Image Processing with Core Image and SwiftUI

Integrating Core Image with SwiftUI

Apart from SwiftUI’s Image view, the three other image types are:

There is some interoperability between the various image types:

Why we need these complex procedure?

If we want to create images dynamically, apply Core Image filters, save them to the user’s photo library, and so on, then SwiftUI’s images aren’t up to the job.

Apple gives us three other image types to work with, and cunningly we need to use all three if we want to work with Core Image.

What actually is an Image? As you know, it’s a view, which means it’s something we can position and size inside our SwiftUI view hierarchy. It also handles loading images from our asset catalog and SF Symbols, and it’s capable of loading from a handful of other sources too. However, ultimately it is something that gets displayed – we can’t write its contents to disk or otherwise transform them beyond applying a few simple SwiftUI filters.

If we want to use Core Image, SwiftUI’s Image view is a great end point, but it’s not useful to use elsewhere. That is, if we want to create images dynamically, apply Core Image filters, save them to the user’s photo library, and so on, then SwiftUI’s images aren’t up to the job.

Apple gives us three other image types to work with, and cunningly we need to use all three if we want to work with Core Image. They might sound similar, but there is some subtle distinction between them, and it’s important that you use them correctly if you want to get anything meaningful out of Core Image.