Creating an iOS Barcode and QR Code Scanner with SwiftUI on M1 Mac
Tags: ios, swiftui, qrcode, swift
Published: 2022-03-04 05:48:51

**SwiftUI** is a robust UI framework designed for constructing native applications across all Apple platforms. This article will guide you through developing an iOS QR code scanner utilizing [SwiftUI](https://developer.apple.com/xcode/swiftui/), [Dynamsoft Camera Enhancer](https://www.dynamsoft.com/camera-enhancer/docs/mobile/programming/ios/guide/guide.html) and [Dynamsoft Barcode Reader](https://www.dynamsoft.com/barcode-reader/docs/mobile/programming/objectivec-swift/user-guide.html?lang=swift) on an M1 Mac mini.

<video src="https://www.dynamsoft.com/codepool/img/2022/03/ios-swiftui-camera-qr-code-scanner.mp4" controls="controls" muted="muted" style="max-height:640px;max-width:100%;"></video>

## Prerequisites
Before starting, ensure you have the following tools and resources:
- **Xcode**: The integrated development environment (IDE) for macOS, required for iOS app development. 
- **CocoaPods**: A dependency manager for Swift and Objective-C Cocoa projects. Follow these steps to install:
    1. Install [Homebrew](https://docs.brew.sh/Installation) (if not already installed) by running the following command in your terminal:
         ```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
``` 
    2. Install `CocoaPods` using Homebrew with the command below:
        ```bash
brew install cocoapods
```
       
- A **Trial License Key** for the Dynamsoft iOS Barcode SDK is available [here](https://www.dynamsoft.com/customer/license/trialLicense/?product=dbr&source=codepool). This key is necessary to unlock the full capabilities of the SDK for development and testing purposes.
    

## Crash Course for Implementing an iOS Barcode QR Code Scanner with SwiftUI
Dynamsoft Camera Enhancer provides a camera view that simplifies starting a camera session with just a few lines of Swift code. The SDK can work alongside Dynamsoft Barcode Reader to decode various barcode types, including QR codes, DataMatrix, PDF417, and more, directly from the camera frame. The following sections will guide you through integrating the camera view and barcode reader into a SwiftUI project step by step.


### Step 1. Initialize CocoaPods in Your Project
1. Create a new iOS SwiftUI project and name it `qrscanner`.
2. Navigate to your Xcode project directory in the terminal.
3. Execute the following command to create a `Podfile` file, which will manage your project's dependencies:
    
    ```bash
    pod init
    ```

4. In the Podfile, include the `DynamsoftCore`, `DynamsoftImageProcessing`, `DynamsoftLicense`, `DynamsoftBarcodeReader`, `DynamsoftCaptureVisionRouter` and `DynamsoftCameraEnhancer` modules:
        
    ```ruby
    target 'qrscanner' do
        use_frameworks!
    
        pod 'DynamsoftCore','3.0.20'
        pod 'DynamsoftImageProcessing','2.0.21'
        pod 'DynamsoftLicense','3.0.30'
        pod 'DynamsoftBarcodeReader','10.0.21'
        pod 'DynamsoftCaptureVisionRouter','2.0.21'
        pod 'DynamsoftCameraEnhancer','4.0.2'
    end
    ```

5. Install the specified dependencies:
        
    ```bash
    pod install
    ```

6. To proceed, open the `qrscanner.xcworkspace` file in Xcode:
    
    ```bash
    open qrscanner.xcworkspace
    ```

### Step 2. Configure Camera Access Permission for SwiftUI Project
Navigate to `TARGETS > Info` tab in Xcode and add the key `Privacy - Camera Usage Description`:

 ```xml
Privacy - Camera Usage Description
```

![iOS camera access permission](https://www.dynamsoft.com/codepool/img/2022/03/ios-camera-access-permission.png)

### Step 3. Set the License Key for Dynamsoft Barcode Reader
The official sample code for the Dynamsoft iOS Barcode SDK is written using Storyboard and UIKit, initializing the license key in the [AppDelegate.swift](https://github.com/Dynamsoft/barcode-reader-mobile-samples/blob/main/ios/HelloWorld/DecodeWithCameraEnhancer/DecodeWithCameraEnhancer/AppDelegate.swift) file. Unlike UIKit, SwiftUI does not use an `AppDelegate.swift` file by default for app lifecycle management. However, you can still implement similar functionality in SwiftUI using the App protocol and the `@UIApplicationDelegateAdaptor` property wrapper to integrate UIKit AppDelegate functionality.
  
1. Add an `AppDelegate.swift`  file to your project and configure the `DynamsoftBarcodeReader` license key within the `application(_:didFinishLaunchingWithOptions:)` method:
    
    ```swift
    import UIKit
    import DynamsoftLicense
    
    class AppDelegate: UIResponder, UIApplicationDelegate, LicenseVerificationListener {
    
        func onLicenseVerified(_ isSuccess: Bool, error: Error?) {
            if !isSuccess {
                if let error = error {
                    print("\(error.localizedDescription)")
                }
            }
        }
    
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            // Request a trial license: https://www.dynamsoft.com/customer/license/trialLicense?product=dbr
            LicenseManager.initLicense("LICENSE-KEY", verificationDelegate: self)
            return true
        }
    }
    
    ```

    Remember to replace `LICENSE-KEY` with your actual Dynamsoft Barcode Reader license key.

2. In your `qrscannerApp.swift` file, utilize the `@UIApplicationDelegateAdaptor` to designate your `AppDelegate` class as the delegate for `UIApplication`. This step integrates your `AppDelegate` with the SwiftUI app lifecycle.
    
    ```swift
    import SwiftUI

    @main
    struct qrscannerApp: App {
        @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    }
    ```
    
### Step 4. Integrate Dynamsoft Barcode Reader and Dynamsoft Camera Enhancer
1. Create a `CameraManager.swift` file:
    
    ```swift
    import Foundation
    import UIKit
    import DynamsoftCameraEnhancer
    import DynamsoftCaptureVisionRouter
    import DynamsoftBarcodeReader
    import DynamsoftCore
    
    class CameraManager: NSObject, ObservableObject, CapturedResultReceiver {
        @Published var results = "No QR Code found"
        private var cameraView:CameraView!
    }
    ```

    The `CapturedResultReceiver` protocol is utilized to receive captured results from the camera frame.

    ```swift
    func onDecodedBarcodesReceived(_ result: DecodedBarcodesResult) {
        var message = ""
        
        if let items = result.items, items.count > 0 {
            let layer = cameraView.getDrawingLayer(DrawingLayerId.DBR.rawValue)
            layer?.clearDrawingItems()
            for item in items {
                message += String(format:"\nFormat: %@\nText: %@\n", item.formatString, item.text)
                let quadDrawingItem = QuadDrawingItem.init(quadrilateral: item.location)
                let textDrawingItem = TextDrawingItem.init(text: item.text, topLeftPoint: item.location.points[0] as! CGPoint, width: 100, height: 10)
                layer?.addDrawingItems([quadDrawingItem, textDrawingItem])
            }
        }
        
        DispatchQueue.main.async {
            self.results = message
        }
    }    
    ```
    
2. Instantiate `DynamsoftCameraEnhancer` and bind it with a `CameraView` instance. The `CameraView` is a `UIView` subclass that contains the camera view and drawing layers for displaying the barcode results. The `DynamsoftCaptureVisionRouter` wraps the barcode reader to process the camera frames and decode the barcodes:
    
    ```swift
    private var dce = CameraEnhancer()
    private var cvr = CaptureVisionRouter()
    
    init(frame: CGRect) {
        super.init()
        setUpCamera(frame: frame)
        setUpDCV()
    }
    
    func setUpCamera(frame: CGRect) {
        cameraView = .init(frame: frame)
        cameraView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        let layer = cameraView.getDrawingLayer(DrawingLayerId.DBR.rawValue)
        layer?.visible = true
        dce.cameraView = cameraView
    }
    
    func setUpDCV() {
        // Set the camera enhancer as the input.
        try! cvr.setInput(dce)
        // Add CapturedResultReceiver to receive the result callback when a video frame is processed.
        cvr.addResultReceiver(self)
    }
    
    func getCameraView() -> CameraView { 
        return cameraView
    }
    ```

3. Manage the view lifecycle to switch the camera session on and off:
    
    ```swift
    func viewDidAppear() {
        dce.open()
        cvr.startCapturing(PresetTemplate.readBarcodes.rawValue) { isSuccess, error in
            if (!isSuccess) {
                if let error = error {
                    self.showResult("Error", error.localizedDescription)
                }
            }
        }
    }

    func viewDidDisappear() {
        dce.close()
        cvr.stopCapturing()
    }
    ```


### Step 5. Create a SwiftUI Camera View Capable of Scanning QR Codes
Since `CameraView` is a `UIView` subclass, it needs to be wrapped in a `UIViewRepresentable` struct for use in SwiftUI.

```swift
import Foundation
import SwiftUI
import DynamsoftCameraEnhancer

struct DynamsoftCameraView: UIViewRepresentable {
    var cameraManager: CameraManager
    func makeUIView(context: Context) -> CameraView {
        let dceView = cameraManager.getCameraView()
        return dceView
    }

    func updateUIView(_ uiView: CameraView, context: Context) {
        
    }
}
```

## Step 6. Construct the SwiftUI View Hierarchy
In the `ContentView.swift` file, create a `ContentView` struct containing a `DynamsoftCameraView` instance.

```swift
import SwiftUI

struct ContentView: View {
    @ObservedObject private var cameraManager = CameraManager(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
    
    var body: some View {
        ZStack() {
            DynamsoftCameraView(cameraManager: cameraManager).onAppear() {
                cameraManager.viewDidAppear()
            }.onDisappear(){
                cameraManager.viewDidDisappear()
            }
            VStack {
                Text("iOS QR Code Scanner").font(.title).foregroundColor(.orange)
                Spacer()
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

### Step 7. Running the Barcode QR Code Scanner on an iPhone or iPad
Configure the signing settings for your Xcode project, then deploy and test the app on a physical device.

![ios swiftui camera qr code scanner](https://www.dynamsoft.com/codepool/img/2022/03/ios-swiftui-camera-qr-code-scanner.jpg)


## Source Code
[https://github.com/yushulx/ios-swiftui-camera-qr-code-scanner](https://github.com/yushulx/ios-swiftui-camera-qr-code-scanner)