How to Integrate a Barcode Scanner SDK into an iOS Swift App

In this article, we will explore Dynamsoft’s Ready-to-Use Barcode Scanner API for iOS. We will cover how to integrate the barcode scanner framework, distributed via the Swift Package Manager, into an iOS SwiftUI project, as well as how to customize its functionalities by modifying the source code.

This article is Part 1 in a 1-Part Series.

What you’ll build: A SwiftUI iOS app that launches a single or multi-barcode scanner powered by Dynamsoft’s BarcodeScannerViewController, then customizes the open-source scanner component to capture results manually via a dedicated button.

Key Takeaways

  • Dynamsoft’s ready-to-use BarcodeScannerViewController lets you add live barcode scanning to a SwiftUI app with fewer than 20 lines of integration code.
  • The framework is distributed via Swift Package Manager (https://github.com/Dynamsoft/barcode-reader-spm) and supports both single and multi-barcode scanning modes out of the box.
  • The component is fully open-source — you can clone the source, add it as a sub-project, and override internal behavior such as replacing automatic result return with a manual capture button.
  • Supported barcode formats include QR Code, PDF417, DataMatrix, and all major 1D formats via Dynamsoft Barcode Reader v11.x on iPhone and iPad.

Common Developer Questions

  • How do I integrate a barcode scanner SDK into an iOS Swift app using Swift Package Manager?
  • How do I use BarcodeScannerViewController inside a SwiftUI view with UIViewControllerRepresentable?
  • How do I customize the Dynamsoft iOS barcode scanner to return results manually instead of automatically?

iOS Barcode Scanner Demo

Prerequisites

Setting Up the iOS Project

  1. Create a new iOS project in Xcode and select SwiftUI as the user interface.
  2. Click “File” -> “Add Package Dependencies…“ to add all required frameworks via Swift Package Manager. The package URL is https://github.com/Dynamsoft/barcode-reader-spm.

    Swift Package Manager for barcode reader bundle

Implementing an iOS Barcode Scanner in Minutes

Create a Text view to display the barcode result and two buttons to launch the single barcode scanner and multi-barcode scanner.

import DynamsoftBarcodeReaderBundle
import DynamsoftLicense
import SwiftUI

struct ContentView: View {
    @State private var scanResult: String = "Scan results will appear here"

    var body: some View {
        VStack(spacing: 16) {
            ScrollView {
                Text(scanResult)
                    .font(.system(size: 20))
                    .padding(16)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(Color.white)
                    .foregroundColor(.black)
                    .lineSpacing(4)
            }
            .background(Color.white)
            .cornerRadius(8)
            .shadow(radius: 2)

            Spacer()

            HStack(spacing: 8) {
                Button(action: {
                    presentBarcodeScanner(mode: .single)
                }) {
                    Text("Single Scan")
                        .font(.headline)
                        .foregroundColor(.white)
                        .padding()
                        .frame(maxWidth: .infinity)
                        .background(Color.blue)
                        .cornerRadius(8)
                }

                Button(action: {
                    presentBarcodeScanner(mode: .multiple)

                }) {
                    Text("Multi Scan")
                        .font(.headline)
                        .foregroundColor(.white)
                        .padding()
                        .frame(maxWidth: .infinity)
                        .background(Color.blue)
                        .cornerRadius(8)
                }
            }
        }
        .padding(16)
        .background(Color(UIColor.systemGroupedBackground))
    }
}

The SDK provides a UIKit-based view controller, BarcodeScannerViewController, which allows real-time barcode scanning with a camera preview. To integrate this view controller into a SwiftUI project, we need to create a UIViewControllerRepresentable wrapper:

struct BarcodeScannerView: UIViewControllerRepresentable {
    let config: BarcodeScannerConfig
    var onScannedResult: ((BarcodeScanResult) -> Void)?

    func makeUIViewController(context: Context) -> BarcodeScannerViewController {
        let vc = BarcodeScannerViewController()
        vc.config = config
        vc.onScannedResult = onScannedResult
        return vc
    }

    func updateUIViewController(_ uiViewController: BarcodeScannerViewController, context: Context)
    {}
}

Explanation:

  • BarcodeScannerConfig is a struct that defines the barcode scanner settings.
  • BarcodeScanResult is a struct that stores the scanned barcode data.
  • onScannedResult is a callback function that handles the scanned barcode result.

Now, we can launch the barcode scanner by clicking one of the buttons. Be sure to replace LICENSE-KEY with your own.

func presentBarcodeScanner(mode: ScanningMode) {
        let config = BarcodeScannerConfig()
        config.license = "LICENSE-KEY"
        config.scanningMode = mode

        var scannerView = BarcodeScannerView(config: config)
        scannerView.onScannedResult = { result in
            DispatchQueue.main.async {
                switch result.resultStatus {
                case .finished:
                    let items = result.barcodes

                    if items != nil && items!.count > 0 {
                        var index = 0
                        self.scanResult = ""
                        for item in items! {
                            if item.type == .barcode {
                                self.scanResult +=
                                    "Result \(index): \nFormat: \(item.formatString)\nText: \(item.text)\n\n"
                                index += 1
                            }
                        }
                    }

                case .canceled:
                    self.scanResult = "Scan canceled"
                case .exception:
                    self.scanResult = result.errorString ?? "Unknown error"
                @unknown default:
                    break
                }

                UIApplication.shared.windows.first?.rootViewController?.dismiss(
                    animated: true, completion: nil)
            }
        }

        UIApplication.shared.windows.first?.rootViewController?.present(
            UIHostingController(rootView: scannerView),
            animated: true,
            completion: nil
        )
    }

iOS single barcode scanner

Customize the Open-Source Barcode Scanner Component

The barcode scanner component is open-source. You can access the source code in the GitHub repository.

The following steps demonstrate how to integrate the source code project into an iOS project and apply some customizations.

Step 1: Integrate the Source Code Project into Your iOS Project

  1. Clone the project from the GitHub repository.
  2. Click “File” -> “Add Files” to add the cloned project as a sub-project.

    Add local project as sub-project

  3. Replace the dependent framework with the local project:
    • Select the project target.
    • Navigate to “General” -> “Frameworks, Libraries, and Embedded Content”.
    • Remove the DynamsoftBarcodeReaderBundle and add DynamsoftBarcodeReaderBundle.framework.

    Replace the dependent framework

Step 2: Add a Capture Button to Manually Return Multiple Barcode Results

By default, the prebuilt multi-barcode scanner automatically returns results. To modify this behavior, we will change the logic in BarcodeScannerViewController.swift to allow manual result capture.

  1. Modify handleMultipleResult() to store the scanned results instead of returning them immediately:

     var temporaryResult: DecodedBarcodesResult?
    
     private func handleMultipleResult(_ result: DecodedBarcodesResult) {
         guard let items = result.items, items.count > 0 else {
             stableFrameCount = 1
             return
         }
            
         temporaryResult = result
     //        if items.count >= config.expectedBarcodesCount {
     //            stop()
     //            if config.isBeepEnabled {
     //                Feedback.beep()
     //            }
     //            onScannedResult?(.init(resultStatus: .finished, barcodes: result.items))
     //        } else {
     //            guard let resultitems = referenceItems else {
     //                stableFrameCount = 1
     //                referenceItems = items
     //                return
     //            }
     //            if isStable(items: items, resultitems: resultitems) {
     //                stableFrameCount += 1
     //                if stableFrameCount >= config.maxConsecutiveStableFramesToExit {
     //                    stop()
     //                    if config.isBeepEnabled {
     //                        Feedback.beep()
     //                    }
     //                    onScannedResult?(.init(resultStatus: .finished, barcodes: result.items))
     //                }
     //            } else {
     //                stableFrameCount = 1
     //                referenceItems = items
     //            }
     //        }
         }
    
  2. Add a Capture button to trigger the result return manually:

     lazy var captureButton: UIButton = {
         let bundle = Bundle(for: type(of: self))
         let button = UIButton()
         let captureImage = UIImage(systemName: "camera.circle.fill")
         button.setImage(captureImage?.withRenderingMode(.alwaysOriginal), for: .normal)
         button.tintColor = .systemBlue 
    
         button.setPreferredSymbolConfiguration(
             UIImage.SymbolConfiguration(pointSize: 40, weight: .regular, scale: .large),
             forImageIn: .normal
         )
         button.addTarget(self, action: #selector(onCaptureButtonTouchUp), for: .touchUpInside)
         return button
     }()
    
     @objc func onCaptureButtonTouchUp() {
         if temporaryResult != nil {
             onScannedResult?(.init(resultStatus: .finished, barcodes: temporaryResult?.items))
         } else {
             print("No results to capture.")
         }
     }
    
  3. Integrate the Capture button into the UI:

     private func setupUI() {
         ...
          
         captureButton.isHidden = config.scanningMode == .single
         captureButton.translatesAutoresizingMaskIntoConstraints = false
            
         let stackView = UIStackView(arrangedSubviews: [torchButton, captureButton, cameraButton])
         ...
     }
    
  4. Re-run the project to see the changes:

    iOS multiple barcode scanner

Common Issues & Edge Cases

  • “License key invalid” on first launch: Ensure you replace "LICENSE-KEY" with a valid trial or production license key in presentBarcodeScanner(mode:). An expired or missing key causes the scanner to immediately fire result.resultStatus == .exception with a descriptive error string.
  • Multi-scan returns no results after customization: After commenting out the auto-return logic in handleMultipleResult, temporaryResult is only populated when at least one barcode enters the frame. If the user taps the capture button before any barcode is detected, nothing is returned — consider disabling the capture button until temporaryResult != nil.
  • UIApplication.shared.windows deprecation on iOS 15+: The sample uses UIApplication.shared.windows.first to present the scanner view controller. On iOS 15 and later, use UIApplication.shared.connectedScenes to obtain the active UIWindowScene and avoid the deprecation warning.

Source Code

https://github.com/yushulx/ios-swiftui-barcode-mrz-document-scanner/tree/main/examples/BarcodeScanner