1 | // Copyright 2016-present 650 Industries. All rights reserved.
|
2 |
|
3 | #import <EXBarCodeScanner/EXBarCodeScanner.h>
|
4 | #import <EXBarCodeScanner/EXBarCodeScannerUtils.h>
|
5 | #import <UMBarCodeScannerInterface/UMBarCodeScannerInterface.h>
|
6 | #import <UMCore/UMDefines.h>
|
7 |
|
8 | @interface EXBarCodeScanner() <AVCaptureMetadataOutputObjectsDelegate>
|
9 |
|
10 | @property (nonatomic, strong) AVCaptureMetadataOutput *metadataOutput;
|
11 | @property (nonatomic, weak) AVCaptureSession *session;
|
12 | @property (nonatomic, weak) dispatch_queue_t sessionQueue;
|
13 | @property (nonatomic, copy, nullable) void (^onBarCodeScanned)(NSDictionary*);
|
14 | @property (nonatomic, assign, getter=isScanningBarCodes) BOOL barCodesScanning;
|
15 | @property (nonatomic, strong) NSDictionary<NSString *, id> *settings;
|
16 |
|
17 | @end
|
18 |
|
19 | NSString *const EX_BARCODE_TYPES_KEY = @"barCodeTypes";
|
20 |
|
21 | @implementation EXBarCodeScanner
|
22 |
|
23 | - (instancetype)init
|
24 | {
|
25 | if (self = [super init]) {
|
26 | _settings = [[NSMutableDictionary alloc] initWithDictionary:[[self class] _getDefaultSettings]];
|
27 | }
|
28 | return self;
|
29 | }
|
30 |
|
31 | # pragma mark - JS properties setters
|
32 |
|
33 | - (void)setSettings:(NSDictionary<NSString *, id> *)settings
|
34 | {
|
35 | for (NSString *key in settings) {
|
36 | if ([key isEqualToString:EX_BARCODE_TYPES_KEY]) {
|
37 | NSArray<NSString *> *value = settings[key];
|
38 | NSSet *previousTypes = [NSSet setWithArray:_settings[EX_BARCODE_TYPES_KEY]];
|
39 | NSSet *newTypes = [NSSet setWithArray:value];
|
40 | if (![previousTypes isEqualToSet:newTypes]) {
|
41 | NSMutableDictionary<NSString *, id> *nextSettings = [[NSMutableDictionary alloc] initWithDictionary:_settings];
|
42 | nextSettings[EX_BARCODE_TYPES_KEY] = value;
|
43 | _settings = nextSettings;
|
44 | UM_WEAKIFY(self);
|
45 | [self _runBlockIfQueueIsPresent:^{
|
46 | UM_ENSURE_STRONGIFY(self);
|
47 | [self maybeStartBarCodeScanning];
|
48 | }];
|
49 | }
|
50 | }
|
51 | }
|
52 | }
|
53 |
|
54 | - (void)setIsEnabled:(BOOL)newBarCodeScanning
|
55 | {
|
56 | if ([self isScanningBarCodes] == newBarCodeScanning) {
|
57 | return;
|
58 | }
|
59 | _barCodesScanning = newBarCodeScanning;
|
60 | UM_WEAKIFY(self);
|
61 | [self _runBlockIfQueueIsPresent:^{
|
62 | UM_ENSURE_STRONGIFY(self);
|
63 | if ([self isScanningBarCodes]) {
|
64 | if (self.metadataOutput) {
|
65 | [self _setConnectionsEnabled:true];
|
66 | } else {
|
67 | [self maybeStartBarCodeScanning];
|
68 | }
|
69 | } else {
|
70 | [self _setConnectionsEnabled:false];
|
71 | }
|
72 | }];
|
73 | }
|
74 |
|
75 | # pragma mark - Public API
|
76 |
|
77 | - (void)maybeStartBarCodeScanning
|
78 | {
|
79 | if (!_session || !_sessionQueue || ![self isScanningBarCodes]) {
|
80 | return;
|
81 | }
|
82 |
|
83 | if (!_metadataOutput) {
|
84 | [_session beginConfiguration];
|
85 |
|
86 | AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
|
87 | [metadataOutput setMetadataObjectsDelegate:self queue:_sessionQueue];
|
88 | if ([_session canAddOutput:metadataOutput]) {
|
89 | [_session addOutput:metadataOutput];
|
90 | _metadataOutput = metadataOutput;
|
91 | }
|
92 | [_session commitConfiguration];
|
93 |
|
94 | if (!_metadataOutput) {
|
95 | return;
|
96 | }
|
97 | }
|
98 |
|
99 | NSArray<AVMetadataObjectType> *availableRequestedObjectTypes = @[];
|
100 | NSArray<AVMetadataObjectType> *requestedObjectTypes = @[];
|
101 | NSArray<AVMetadataObjectType> *availableObjectTypes = _metadataOutput.availableMetadataObjectTypes;
|
102 | if (_settings && _settings[EX_BARCODE_TYPES_KEY]) {
|
103 | requestedObjectTypes = [[NSArray alloc] initWithArray:_settings[EX_BARCODE_TYPES_KEY]];
|
104 | }
|
105 |
|
106 | for(AVMetadataObjectType objectType in requestedObjectTypes) {
|
107 | if ([availableObjectTypes containsObject:objectType]) {
|
108 | availableRequestedObjectTypes = [availableRequestedObjectTypes arrayByAddingObject:objectType];
|
109 | }
|
110 | }
|
111 |
|
112 | [_metadataOutput setMetadataObjectTypes:availableRequestedObjectTypes];
|
113 | }
|
114 |
|
115 | - (void)stopBarCodeScanning
|
116 | {
|
117 | if (!_session) {
|
118 | return;
|
119 | }
|
120 |
|
121 | [_session beginConfiguration];
|
122 |
|
123 | if ([_session.outputs containsObject:_metadataOutput]) {
|
124 | [_session removeOutput:_metadataOutput];
|
125 | _metadataOutput = nil;
|
126 | }
|
127 |
|
128 | [_session commitConfiguration];
|
129 |
|
130 | if ([self isScanningBarCodes] && _onBarCodeScanned) {
|
131 | _onBarCodeScanned(nil);
|
132 | }
|
133 | }
|
134 |
|
135 | # pragma mark - Private API
|
136 |
|
137 | - (void)_setConnectionsEnabled:(BOOL)enabled
|
138 | {
|
139 | if (!_metadataOutput) {
|
140 | return;
|
141 | }
|
142 | for (AVCaptureConnection *connection in _metadataOutput.connections) {
|
143 | connection.enabled = enabled;
|
144 | }
|
145 | }
|
146 |
|
147 | - (void)_runBlockIfQueueIsPresent:(void (^)(void))block
|
148 | {
|
149 | if (_sessionQueue) {
|
150 | dispatch_async(_sessionQueue, block);
|
151 | }
|
152 | }
|
153 |
|
154 | # pragma mark - AVCaptureMetadataOutputObjectsDelegate
|
155 |
|
156 | - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
|
157 | fromConnection:(AVCaptureConnection *)connection
|
158 | {
|
159 | if (!_settings || !_settings[EX_BARCODE_TYPES_KEY] || !_metadataOutput) {
|
160 | return;
|
161 | }
|
162 |
|
163 | for(AVMetadataObject *metadata in metadataObjects) {
|
164 | if([metadata isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
|
165 | AVMetadataMachineReadableCodeObject *codeMetadata = (AVMetadataMachineReadableCodeObject *) metadata;
|
166 | for (id barcodeType in _settings[EX_BARCODE_TYPES_KEY]) {
|
167 | if ([metadata.type isEqualToString:barcodeType]) {
|
168 |
|
169 | NSDictionary *event = @{
|
170 | @"type" : codeMetadata.type,
|
171 | @"data" : codeMetadata.stringValue
|
172 | };
|
173 |
|
174 | if (_onBarCodeScanned) {
|
175 | _onBarCodeScanned(event);
|
176 | }
|
177 | return;
|
178 | }
|
179 | }
|
180 | }
|
181 | }
|
182 | }
|
183 |
|
184 | # pragma mark - default settings
|
185 |
|
186 | + (NSDictionary *)_getDefaultSettings
|
187 | {
|
188 | return @{
|
189 | EX_BARCODE_TYPES_KEY: [[EXBarCodeScannerUtils validBarCodeTypes] allValues],
|
190 | };
|
191 | }
|
192 |
|
193 | @end
|