1 import ExpoModulesCore
2 
3 public final class BarCodeScannerModule: Module {
4   public func definition() -> ModuleDefinition {
5     Name("ExpoBarCodeScanner")
6 
7     Constants([
8       "Type": [
9         "front": EXCameraType.front.rawValue,
10         "back": EXCameraType.back.rawValue
11       ],
12       "BarCodeType": EXBarCodeScannerUtils.validBarCodeTypes()
13     ])
14 
15     OnCreate {
16       let permissionsManager = self.appContext?.permissions
17 
18       EXPermissionsMethodsDelegate.register(
19         [EXBareCodeCameraRequester()],
20         withPermissionsManager: permissionsManager
21       )
22     }
23 
24     AsyncFunction("getPermissionsAsync") { (promise: Promise) in
25       EXPermissionsMethodsDelegate.getPermissionWithPermissionsManager(
26         self.appContext?.permissions,
27         withRequester: EXBareCodeCameraRequester.self,
28         resolve: promise.resolver,
29         reject: promise.legacyRejecter
30       )
31     }
32 
33     AsyncFunction("requestPermissionsAsync") { (promise: Promise) in
34       EXPermissionsMethodsDelegate.askForPermission(
35         withPermissionsManager: self.appContext?.permissions,
36         withRequester: EXBareCodeCameraRequester.self,
37         resolve: promise.resolver,
38         reject: promise.legacyRejecter
39       )
40     }
41 
42     AsyncFunction("scanFromURLAsync") { (url: URL, _: [String], promise: Promise) in
43       guard let imageLoader = appContext?.imageLoader else {
44         throw ImageLoaderNotFound()
45       }
46 
47       imageLoader.loadImage(for: url) { error, image in
48         if error != nil {
49           promise.reject(FailedToLoadImage())
50           return
51         }
52 
53         guard let cgImage = image?.cgImage else {
54           promise.reject(FailedToLoadImage())
55           return
56         }
57 
58         guard let detector = CIDetector(
59           ofType: CIDetectorTypeQRCode,
60           context: nil,
61           options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]
62         ) else {
63           promise.reject(InitScannerFailed())
64           return
65         }
66 
67         let ciImage = CIImage(cgImage: cgImage)
68         let features = detector.features(in: ciImage)
69         promise.resolve(self.getResultFrom(features))
70       }
71     }
72 
73     View(EXBarCodeScannerView.self) {
74       Events("onBarCodeScanned")
75 
76       Prop("type") { (view, type: Int) in
77         if view.presetCamera != type {
78           view.presetCamera = type
79         }
80       }
81 
82       Prop("barCodeTypes") { (view, barcodeTypes: [String]) in
83         view.barCodeTypes = barcodeTypes
84       }
85     }
86   }
87 
88   private func getResultFrom(_ features: [CIFeature]) -> [[AnyHashable: Any]?] {
89     var result = [[AnyHashable: Any]?]()
90 
91     for feature in features {
92       if let qrCodeFeature = feature as? CIQRCodeFeature {
93         let item = EXBarCodeScannerUtils.ciQRCodeFeature(
94           toDicitionary: qrCodeFeature,
95           barCodeType: AVMetadataObject.ObjectType.qr.rawValue
96         )
97         result.append(item)
98       }
99     }
100 
101     return result
102   }
103 }
104