CodeScanner.swift (4022B)
1 // 2 // CodeScanner.swift 3 // https://github.com/twostraws/CodeScanner 4 // 5 // Created by Paul Hudson on 14/12/2021. 6 // Copyright © 2021 Paul Hudson. All rights reserved. 7 // 8 9 import AVFoundation 10 import SwiftUI 11 12 /// An enum describing the ways CodeScannerView can hit scanning problems. 13 public enum ScanError: Error { 14 /// The camera could not be accessed. 15 case badInput 16 17 /// The camera was not capable of scanning the requested codes. 18 case badOutput 19 20 /// Initialization failed. 21 case initError(_ error: Error) 22 } 23 24 /// The result from a successful scan: the string that was scanned, and also the type of data that was found. 25 /// The type is useful for times when you've asked to scan several different code types at the same time, because 26 /// it will report the exact code type that was found. 27 public struct ScanResult { 28 /// The contents of the code. 29 public let string: String 30 31 /// The type of code that was matched. 32 public let type: AVMetadataObject.ObjectType 33 } 34 35 /// The operating mode for CodeScannerView. 36 public enum ScanMode { 37 /// Scan exactly one code, then stop. 38 case once 39 40 /// Scan each code no more than once. 41 case oncePerCode 42 43 /// Keep scanning all codes until dismissed. 44 case continuous 45 } 46 47 /// A SwiftUI view that is able to scan barcodes, QR codes, and more, and send back what was found. 48 /// To use, set `codeTypes` to be an array of things to scan for, e.g. `[.qr]`, and set `completion` to 49 /// a closure that will be called when scanning has finished. This will be sent the string that was detected or a `ScanError`. 50 /// For testing inside the simulator, set the `simulatedData` property to some test data you want to send back. 51 public struct CodeScannerView: UIViewControllerRepresentable { 52 53 public let codeTypes: [AVMetadataObject.ObjectType] 54 public let scanMode: ScanMode 55 public let scanInterval: Double 56 public let showViewfinder: Bool 57 public var simulatedData = "" 58 public var shouldVibrateOnSuccess: Bool 59 public var isTorchOn: Bool 60 public var isGalleryPresented: Binding<Bool> 61 public var videoCaptureDevice: AVCaptureDevice? 62 public var completion: (Result<ScanResult, ScanError>) -> Void 63 64 public init( 65 codeTypes: [AVMetadataObject.ObjectType], 66 scanMode: ScanMode = .once, 67 scanInterval: Double = 2.0, 68 showViewfinder: Bool = false, 69 simulatedData: String = "", 70 shouldVibrateOnSuccess: Bool = true, 71 isTorchOn: Bool = false, 72 isGalleryPresented: Binding<Bool> = .constant(false), 73 videoCaptureDevice: AVCaptureDevice? = AVCaptureDevice.default(for: .video), 74 completion: @escaping (Result<ScanResult, ScanError>) -> Void 75 ) { 76 self.codeTypes = codeTypes 77 self.scanMode = scanMode 78 self.showViewfinder = showViewfinder 79 self.scanInterval = scanInterval 80 self.simulatedData = simulatedData 81 self.shouldVibrateOnSuccess = shouldVibrateOnSuccess 82 self.isTorchOn = isTorchOn 83 self.isGalleryPresented = isGalleryPresented 84 self.videoCaptureDevice = videoCaptureDevice 85 self.completion = completion 86 } 87 88 public func makeCoordinator() -> ScannerCoordinator { 89 ScannerCoordinator(parent: self) 90 } 91 92 public func makeUIViewController(context: Context) -> ScannerViewController { 93 let viewController = ScannerViewController(showViewfinder: showViewfinder) 94 viewController.delegate = context.coordinator 95 return viewController 96 } 97 98 public func updateUIViewController(_ uiViewController: ScannerViewController, context: Context) { 99 uiViewController.updateViewController( 100 isTorchOn: isTorchOn, 101 isGalleryPresented: isGalleryPresented.wrappedValue 102 ) 103 } 104 105 } 106 107 @available(macCatalyst 14.0, *) 108 struct CodeScannerView_Previews: PreviewProvider { 109 static var previews: some View { 110 CodeScannerView(codeTypes: [.qr]) { result in 111 // do nothing 112 } 113 } 114 }