capacitor-camera-view 2.3.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CapacitorCameraView.podspec +1 -1
  2. package/Package.swift +1 -1
  3. package/README.md +341 -48
  4. package/android/build.gradle +0 -1
  5. package/android/src/main/AndroidManifest.xml +0 -1
  6. package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraError.kt +42 -0
  7. package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraView.kt +946 -205
  8. package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraViewPlugin.kt +87 -43
  9. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/BarcodeDetectionResult.kt +28 -2
  10. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/CameraDevice.kt +6 -1
  11. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/CameraSessionConfiguration.kt +15 -1
  12. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/TorchModeState.kt +15 -0
  13. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/WebBoundingRect.kt +1 -1
  14. package/android/src/main/java/com/michaelwolz/capacitorcameraview/utils.kt +73 -19
  15. package/dist/docs.json +487 -30
  16. package/dist/esm/definitions.d.ts +494 -27
  17. package/dist/esm/definitions.js.map +1 -1
  18. package/dist/esm/utils.d.ts +59 -15
  19. package/dist/esm/utils.js +79 -38
  20. package/dist/esm/utils.js.map +1 -1
  21. package/dist/esm/web.d.ts +191 -13
  22. package/dist/esm/web.js +626 -142
  23. package/dist/esm/web.js.map +1 -1
  24. package/dist/plugin.cjs.js +713 -180
  25. package/dist/plugin.cjs.js.map +1 -1
  26. package/dist/plugin.js +713 -180
  27. package/dist/plugin.js.map +1 -1
  28. package/ios/Sources/CameraViewPlugin/CameraError.swift +147 -2
  29. package/ios/Sources/CameraViewPlugin/CameraEvents.swift +41 -19
  30. package/ios/Sources/CameraViewPlugin/CameraSessionConfiguration.swift +29 -1
  31. package/ios/Sources/CameraViewPlugin/CameraViewManager+BarcodeScan.swift +78 -23
  32. package/ios/Sources/CameraViewPlugin/CameraViewManager+DeferredStart.swift +37 -0
  33. package/ios/Sources/CameraViewPlugin/CameraViewManager+Focus.swift +131 -0
  34. package/ios/Sources/CameraViewPlugin/CameraViewManager+Lifecycle.swift +156 -0
  35. package/ios/Sources/CameraViewPlugin/CameraViewManager+PhotoCapture.swift +73 -41
  36. package/ios/Sources/CameraViewPlugin/CameraViewManager+ResolutionSelection.swift +57 -0
  37. package/ios/Sources/CameraViewPlugin/CameraViewManager+Rotation.swift +195 -0
  38. package/ios/Sources/CameraViewPlugin/CameraViewManager+VideoDataOutput.swift +24 -12
  39. package/ios/Sources/CameraViewPlugin/CameraViewManager+VideoRecording.swift +22 -73
  40. package/ios/Sources/CameraViewPlugin/CameraViewManager+Zoom.swift +113 -0
  41. package/ios/Sources/CameraViewPlugin/CameraViewManager.swift +450 -403
  42. package/ios/Sources/CameraViewPlugin/CameraViewPlugin.swift +111 -69
  43. package/ios/Sources/CameraViewPlugin/Utils.swift +61 -7
  44. package/package.json +25 -9
@@ -35,6 +35,7 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
35
35
  CAPPluginMethod(name: "flipCamera", returnType: CAPPluginReturnPromise),
36
36
  CAPPluginMethod(name: "getZoom", returnType: CAPPluginReturnPromise),
37
37
  CAPPluginMethod(name: "setZoom", returnType: CAPPluginReturnPromise),
38
+ CAPPluginMethod(name: "setFocusPoint", returnType: CAPPluginReturnPromise),
38
39
  CAPPluginMethod(name: "getFlashMode", returnType: CAPPluginReturnPromise),
39
40
  CAPPluginMethod(name: "getSupportedFlashModes", returnType: CAPPluginReturnPromise),
40
41
  CAPPluginMethod(name: "setFlashMode", returnType: CAPPluginReturnPromise),
@@ -54,25 +55,41 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
54
55
  public func cameraDidDetectBarcode(_ event: BarcodeDetectedEvent) {
55
56
  notifyListeners("barcodeDetected", data: event.toDictionary())
56
57
  }
58
+
59
+ public func cameraWasInterrupted(reason: String) {
60
+ notifyListeners("cameraInterrupted", data: ["reason": reason])
61
+ }
62
+
63
+ public func cameraInterruptionEnded() {
64
+ notifyListeners("cameraResumed", data: [:])
65
+ }
66
+
67
+ public func cameraRuntimeError(message: String, code: Int?) {
68
+ var data: [String: Any] = ["message": message]
69
+ if let code = code {
70
+ data["code"] = code
71
+ }
72
+ notifyListeners("cameraRuntimeError", data: data)
73
+ }
57
74
 
58
75
  @objc func start(_ call: CAPPluginCall) {
59
76
  guard let webView = self.webView else {
60
- call.reject("Cannot find web view")
77
+ call.reject("Cannot find web view", CameraError.missingWebView.code)
61
78
  return
62
79
  }
63
-
80
+
64
81
  maybeRequestCameraAccess { [weak self] granted in
65
82
  guard granted else {
66
- call.reject("Camera access denied")
83
+ call.reject("Camera access denied", CameraError.permissionDenied.code)
67
84
  return
68
85
  }
69
-
86
+
70
87
  self?.implementation.startSession(
71
88
  configuration: sessionConfigFromPluginCall(call),
72
89
  webView: webView,
73
90
  completion: { (error) in
74
91
  if let error = error {
75
- call.reject("Failed to start camera preview", nil, error)
92
+ call.reject("Failed to start camera preview", error.cameraErrorCode, error)
76
93
  return
77
94
  }
78
95
  call.resolve()
@@ -97,22 +114,22 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
97
114
  let saveToFile = call.getBool("saveToFile", false)
98
115
 
99
116
  guard quality >= 0.0 && quality <= 100.0 else {
100
- call.reject("Quality must be between 0 and 100")
117
+ call.reject("Quality must be between 0 and 100", CameraError.invalidArgument.code)
101
118
  return
102
119
  }
103
-
120
+
104
121
  // Use optimized Data-based capture to avoid double JPEG encoding
105
122
  implementation.capturePhotoData(completion: { [weak self] (data, error) in
106
123
  if let error = error {
107
- call.reject("Failed to capture image", nil, error)
124
+ call.reject("Failed to capture image", error.cameraErrorCode, error)
108
125
  return
109
126
  }
110
-
127
+
111
128
  guard let originalData = data else {
112
- call.reject("No image data")
129
+ call.reject("No image data", CameraError.captureOutputMissing.code)
113
130
  return
114
131
  }
115
-
132
+
116
133
  // Determine final image data based on quality setting
117
134
  // For quality >= 90%, use original camera JPEG data to avoid re-encoding
118
135
  // For lower quality, re-encode to reduce file size
@@ -121,30 +138,34 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
121
138
  // Use original JPEG data from camera (avoids quality loss and CPU overhead)
122
139
  imageData = originalData
123
140
  } else {
124
- // Re-encode at lower quality for smaller file size
125
- guard let image = UIImage(data: originalData),
126
- let compressedData = image.jpegData(compressionQuality: quality / 100.0) else {
127
- call.reject("Failed to compress image")
141
+ // Re-encode at lower quality for smaller file size. Uses ImageIO
142
+ // directly (rather than UIImage.jpegData) so EXIF/GPS metadata
143
+ // from the original camera JPEG survives the re-encode.
144
+ guard let compressedData = reencodeJPEG(data: originalData, compressionQuality: quality / 100.0) else {
145
+ call.reject("Failed to compress image", CameraError.imageCompressionFailed.code)
128
146
  return
129
147
  }
130
148
  imageData = compressedData
131
149
  }
132
-
150
+
133
151
  if saveToFile {
134
152
  // Use TempFileManager for tracked temp files with automatic cleanup
135
153
  let tempFileURL = TempFileManager.shared.createTempImageFile()
136
154
  do {
137
155
  try imageData.write(to: tempFileURL)
138
-
156
+
139
157
  // Convert file URL to webView-accessible path using Capacitor bridge
140
158
  guard let webPath = self?.bridge?.portablePath(fromLocalURL: tempFileURL)?.absoluteString else {
141
- call.reject("Failed to create web-accessible path")
159
+ call.reject("Failed to create web-accessible path", CameraError.pathConversionFailed.code)
142
160
  return
143
161
  }
144
-
145
- call.resolve(["webPath": webPath])
162
+
163
+ call.resolve([
164
+ "path": tempFileURL.absoluteString,
165
+ "webPath": webPath,
166
+ ])
146
167
  } catch {
147
- call.reject("Failed to save image to file", nil, error)
168
+ call.reject("Failed to save image to file", CameraError.fileWriteFailed.code, error)
148
169
  }
149
170
  } else {
150
171
  // Return as base64
@@ -160,41 +181,44 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
160
181
  let saveToFile = call.getBool("saveToFile", false)
161
182
 
162
183
  guard quality >= 0.0 && quality <= 100.0 else {
163
- call.reject("Quality must be between 0 and 100")
184
+ call.reject("Quality must be between 0 and 100", CameraError.invalidArgument.code)
164
185
  return
165
186
  }
166
-
187
+
167
188
  implementation.captureSnapshot { [weak self] (image, error) in
168
189
  if let error = error {
169
- call.reject("Failed to capture frame", nil, error)
190
+ call.reject("Failed to capture frame", error.cameraErrorCode, error)
170
191
  return
171
192
  }
172
-
193
+
173
194
  guard let image = image else {
174
- call.reject("No frame data")
195
+ call.reject("No frame data", CameraError.captureOutputMissing.code)
175
196
  return
176
197
  }
177
-
198
+
178
199
  guard let imageData = image.jpegData(compressionQuality: quality / 100.0) else {
179
- call.reject("Failed to compress image")
200
+ call.reject("Failed to compress image", CameraError.imageCompressionFailed.code)
180
201
  return
181
202
  }
182
-
203
+
183
204
  if saveToFile {
184
205
  // Use TempFileManager for tracked temp files with automatic cleanup
185
206
  let tempFileURL = TempFileManager.shared.createTempImageFile()
186
207
  do {
187
208
  try imageData.write(to: tempFileURL)
188
-
209
+
189
210
  // Convert file URL to webView-accessible path using Capacitor bridge
190
211
  guard let webPath = self?.bridge?.portablePath(fromLocalURL: tempFileURL)?.absoluteString else {
191
- call.reject("Failed to create web-accessible path")
212
+ call.reject("Failed to create web-accessible path", CameraError.pathConversionFailed.code)
192
213
  return
193
214
  }
194
-
195
- call.resolve(["webPath": webPath])
215
+
216
+ call.resolve([
217
+ "path": tempFileURL.absoluteString,
218
+ "webPath": webPath,
219
+ ])
196
220
  } catch {
197
- call.reject("Failed to save sample to file", nil, error)
221
+ call.reject("Failed to save sample to file", CameraError.fileWriteFailed.code, error)
198
222
  }
199
223
  } else {
200
224
  // Return as base64
@@ -210,14 +234,14 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
210
234
  let videoQuality = call.getString("videoQuality") ?? "highest"
211
235
 
212
236
  guard let parsedVideoQuality = VideoRecordingQuality(rawValue: videoQuality) else {
213
- call.reject("Invalid videoQuality. Use one of: lowest, sd, hd, fhd, uhd, highest")
237
+ call.reject("Invalid videoQuality. Use one of: lowest, sd, hd, fhd, uhd, highest", CameraError.invalidArgument.code)
214
238
  return
215
239
  }
216
-
240
+
217
241
  if enableAudio {
218
242
  maybeRequestMicrophoneAccess { [weak self] granted in
219
243
  guard granted else {
220
- call.reject("Microphone access denied")
244
+ call.reject("Microphone access denied", CameraError.permissionDenied.code)
221
245
  return
222
246
  }
223
247
  self?.doStartRecording(call: call, enableAudio: true, videoQuality: parsedVideoQuality)
@@ -226,7 +250,7 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
226
250
  doStartRecording(call: call, enableAudio: false, videoQuality: parsedVideoQuality)
227
251
  }
228
252
  }
229
-
253
+
230
254
  private func doStartRecording(
231
255
  call: CAPPluginCall,
232
256
  enableAudio: Bool,
@@ -234,34 +258,37 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
234
258
  ) {
235
259
  implementation.startRecording(enableAudio: enableAudio, videoQuality: videoQuality) { error in
236
260
  if let error = error {
237
- call.reject("Failed to start recording", nil, error)
261
+ call.reject("Failed to start recording", error.cameraErrorCode, error)
238
262
  return
239
263
  }
240
264
  call.resolve()
241
265
  }
242
266
  }
243
-
267
+
244
268
  @objc func stopRecording(_ call: CAPPluginCall) {
245
269
  implementation.stopRecording { [weak self] (outputURL, error) in
246
270
  if let error = error {
247
- call.reject("Failed to stop recording", nil, error)
271
+ call.reject("Failed to stop recording", error.cameraErrorCode, error)
248
272
  return
249
273
  }
250
-
274
+
251
275
  guard let outputURL = outputURL else {
252
- call.reject("No output file URL")
276
+ call.reject("No output file URL", CameraError.captureOutputMissing.code)
253
277
  return
254
278
  }
255
-
279
+
256
280
  guard let webPath = self?.bridge?.portablePath(fromLocalURL: outputURL)?.absoluteString else {
257
- call.reject("Failed to create web-accessible path")
281
+ call.reject("Failed to create web-accessible path", CameraError.pathConversionFailed.code)
258
282
  return
259
283
  }
260
-
261
- call.resolve(["webPath": webPath])
284
+
285
+ call.resolve([
286
+ "path": outputURL.absoluteString,
287
+ "webPath": webPath,
288
+ ])
262
289
  }
263
290
  }
264
-
291
+
265
292
  @objc func getAvailableDevices(_ call: CAPPluginCall) {
266
293
  let devices = implementation.getAvailableDevices()
267
294
 
@@ -281,12 +308,12 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
281
308
  }
282
309
 
283
310
  @objc func flipCamera(_ call: CAPPluginCall) {
284
- do {
285
- try implementation.flipCamera()
311
+ implementation.flipCamera { error in
312
+ if let error = error {
313
+ call.reject("Failed to switch camera", error.cameraErrorCode, error)
314
+ return
315
+ }
286
316
  call.resolve()
287
- } catch {
288
- call.reject("Failed to switch camera", nil, error)
289
- return
290
317
  }
291
318
  }
292
319
 
@@ -302,21 +329,36 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
302
329
 
303
330
  @objc func setZoom(_ call: CAPPluginCall) {
304
331
  guard let level = call.getDouble("level") else {
305
- call.reject("Zoom level must be provided")
332
+ call.reject("Zoom level must be provided", CameraError.invalidArgument.code)
306
333
  return
307
334
  }
308
-
335
+
309
336
  let ramp = call.getBool("ramp") ?? false
310
-
337
+
311
338
  do {
312
339
  try implementation.setZoomFactor(level, ramp: ramp)
313
340
  call.resolve()
314
341
  } catch {
315
- call.reject("Failed to set zoom level", nil, error)
342
+ call.reject("Failed to set zoom level", error.cameraErrorCode, error)
316
343
  return
317
344
  }
318
345
  }
319
346
 
347
+ @objc func setFocusPoint(_ call: CAPPluginCall) {
348
+ guard let pointX = call.getDouble("x"), let pointY = call.getDouble("y") else {
349
+ call.reject("Focus point x and y must be provided", CameraError.invalidArgument.code)
350
+ return
351
+ }
352
+
353
+ implementation.setFocusPoint(x: CGFloat(pointX), y: CGFloat(pointY)) { error in
354
+ if let error = error {
355
+ call.reject("Failed to set focus point", error.cameraErrorCode, error)
356
+ return
357
+ }
358
+ call.resolve()
359
+ }
360
+ }
361
+
320
362
  @objc func getFlashMode(_ call: CAPPluginCall) {
321
363
  let flashMode = implementation.getFlashMode()
322
364
 
@@ -327,7 +369,7 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
327
369
 
328
370
  @objc func getSupportedFlashModes(_ call: CAPPluginCall) {
329
371
  let supportedFlashModes = implementation.getSupportedFlashModes()
330
- let supportedFlashModeStrArr = supportedFlashModes.map { flashModeToStrMap[$0] }
372
+ let supportedFlashModeStrArr = supportedFlashModes.compactMap { flashModeToStrMap[$0] }
331
373
 
332
374
  call.resolve([
333
375
  "flashModes": supportedFlashModeStrArr
@@ -336,20 +378,20 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
336
378
 
337
379
  @objc func setFlashMode(_ call: CAPPluginCall) {
338
380
  guard let mode = call.getString("mode") else {
339
- call.reject("Flash mode must be provided")
381
+ call.reject("Flash mode must be provided", CameraError.invalidArgument.code)
340
382
  return
341
383
  }
342
-
384
+
343
385
  guard let flashMode = strToFlashModeMap[mode] else {
344
- call.reject("Invalid flash mode")
386
+ call.reject("Invalid flash mode", CameraError.invalidArgument.code)
345
387
  return
346
388
  }
347
-
389
+
348
390
  do {
349
391
  try implementation.setFlashMode(flashMode)
350
392
  call.resolve()
351
393
  } catch {
352
- call.reject("Failed to set flash mode", nil, error)
394
+ call.reject("Failed to set flash mode", error.cameraErrorCode, error)
353
395
  }
354
396
  }
355
397
 
@@ -370,22 +412,22 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin, CameraEventDelegate
370
412
 
371
413
  @objc func setTorchMode(_ call: CAPPluginCall) {
372
414
  guard let enabled = call.getBool("enabled") else {
373
- call.reject("Enabled parameter is required")
415
+ call.reject("Enabled parameter is required", CameraError.invalidArgument.code)
374
416
  return
375
417
  }
376
-
418
+
377
419
  let level = call.getFloat("level") ?? 1.0
378
-
420
+
379
421
  guard level >= 0.0 && level <= 1.0 else {
380
- call.reject("Level must be between 0.0 and 1.0")
422
+ call.reject("Level must be between 0.0 and 1.0", CameraError.invalidArgument.code)
381
423
  return
382
424
  }
383
-
425
+
384
426
  do {
385
427
  try implementation.setTorchMode(enabled: enabled, level: level)
386
428
  call.resolve()
387
429
  } catch {
388
- call.reject("Failed to set torch mode", nil, error)
430
+ call.reject("Failed to set torch mode", error.cameraErrorCode, error)
389
431
  }
390
432
  }
391
433
 
@@ -1,4 +1,11 @@
1
1
  import AVFoundation
2
+ import ImageIO
3
+ import os
4
+ import UniformTypeIdentifiers
5
+
6
+ /// Shared logger for camera plugin diagnostics (visible in Console.app / `log stream`),
7
+ /// used instead of `print()` so failures are visible in production logs.
8
+ internal let cameraViewLogger = Logger(subsystem: "com.michaelwolz.capacitorcameraview", category: "CameraViewManager")
2
9
 
3
10
  // MARK: - Camera Type Conversion
4
11
 
@@ -57,25 +64,66 @@ public func convertToNativeCameraTypes(_ stringTypes: [String]) -> [AVCaptureDev
57
64
  return stringTypes.compactMap { convertToNativeCameraType($0) }
58
65
  }
59
66
 
60
- /// Creates a temporary file URL for storing captured images
61
- /// @deprecated Use TempFileManager.shared.createTempImageFile() for automatic cleanup
62
- public func createTempImageFile() throws -> URL {
63
- let timestamp = Int(Date().timeIntervalSince1970 * 1000)
64
- let fileName = "camera_capture_\(timestamp).jpg"
65
- let tempDir = FileManager.default.temporaryDirectory
66
- return tempDir.appendingPathComponent(fileName)
67
+ // MARK: - Image Re-encoding
68
+
69
+ /// Re-encodes JPEG data at a lower compression quality while preserving the
70
+ /// original image metadata (EXIF, TIFF, GPS, orientation, etc.).
71
+ ///
72
+ /// Uses ImageIO directly instead of round-tripping through `UIImage`/
73
+ /// `UIImage.jpegData(compressionQuality:)`, which silently drops all metadata
74
+ /// from the source JPEG.
75
+ ///
76
+ /// - Parameters:
77
+ /// - data: The source JPEG data.
78
+ /// - compressionQuality: The lossy compression quality (0.0-1.0).
79
+ /// - Returns: The re-encoded JPEG data, or nil if the source data or
80
+ /// destination could not be created.
81
+ public func reencodeJPEG(data: Data, compressionQuality: CGFloat) -> Data? {
82
+ guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
83
+ return nil
84
+ }
85
+
86
+ let destinationData = NSMutableData()
87
+ guard let destination = CGImageDestinationCreateWithData(
88
+ destinationData,
89
+ UTType.jpeg.identifier as CFString,
90
+ 1,
91
+ nil
92
+ ) else {
93
+ return nil
94
+ }
95
+
96
+ // Start from the source image's own metadata so EXIF/GPS/orientation
97
+ // survive the re-encode, then layer the compression quality on top.
98
+ var properties = (CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString: Any]) ?? [:]
99
+ properties[kCGImageDestinationLossyCompressionQuality] = compressionQuality
100
+
101
+ CGImageDestinationAddImageFromSource(destination, source, 0, properties as CFDictionary)
102
+
103
+ guard CGImageDestinationFinalize(destination) else {
104
+ return nil
105
+ }
106
+
107
+ return destinationData as Data
67
108
  }
68
109
 
69
110
  // MARK: - Barcode Type Conversion
70
111
 
71
112
  /// All supported barcode types for detection.
72
113
  /// These are enabled by default when no specific types are configured.
114
+ ///
115
+ /// Note: there is no UPC-A entry. AVFoundation has no UPC-A metadata object
116
+ /// type and reports UPC-A codes as `.ean13` (a UPC-A value is an EAN-13 with a
117
+ /// leading `0`), so on iOS the `upcA` union member is neither requestable nor
118
+ /// emitted — such codes arrive as `ean13`. `.codabar` is available since iOS
119
+ /// 15.4; the deployment target is 16, so no availability guard is needed.
73
120
  public let ALL_SUPPORTED_BARCODE_TYPES: [AVMetadataObject.ObjectType] = [
74
121
  .qr,
75
122
  .code128,
76
123
  .code39,
77
124
  .code39Mod43,
78
125
  .code93,
126
+ .codabar,
79
127
  .ean8,
80
128
  .ean13,
81
129
  .interleaved2of5,
@@ -101,6 +149,10 @@ public func convertToNativeBarcodeType(_ stringType: String) -> AVMetadataObject
101
149
  return .code39Mod43
102
150
  case "code93":
103
151
  return .code93
152
+ case "codabar":
153
+ return .codabar
154
+ // Note: "upcA" is intentionally unmapped. AVFoundation has no UPC-A metadata
155
+ // type and reports UPC-A codes as .ean13, so it cannot be requested on iOS.
104
156
  case "ean8":
105
157
  return .ean8
106
158
  case "ean13":
@@ -137,6 +189,8 @@ public func convertToStringBarcodeType(_ barcodeType: AVMetadataObject.ObjectTyp
137
189
  return "code39Mod43"
138
190
  case .code93:
139
191
  return "code93"
192
+ case .codabar:
193
+ return "codabar"
140
194
  case .ean8:
141
195
  return "ean8"
142
196
  case .ean13:
package/package.json CHANGED
@@ -1,11 +1,20 @@
1
1
  {
2
2
  "name": "capacitor-camera-view",
3
- "version": "2.3.1",
3
+ "version": "3.0.0",
4
4
  "description": "A Capacitor plugin for embedding a live camera feed directly into your app.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
7
7
  "types": "dist/esm/index.d.ts",
8
8
  "unpkg": "dist/plugin.js",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/esm/index.d.ts",
12
+ "import": "./dist/esm/index.js",
13
+ "require": "./dist/plugin.cjs.js"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
17
+ "sideEffects": false,
9
18
  "files": [
10
19
  "android/src/main/",
11
20
  "android/build.gradle",
@@ -29,23 +38,29 @@
29
38
  "plugin",
30
39
  "native"
31
40
  ],
41
+ "packageManager": "pnpm@11.18.0",
42
+ "engines": {
43
+ "node": ">=24",
44
+ "pnpm": ">=11"
45
+ },
32
46
  "scripts": {
33
- "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
47
+ "verify": "pnpm run verify:ios && pnpm run verify:android && pnpm run verify:web",
34
48
  "verify:ios": "xcodebuild -scheme CapacitorCameraView -destination generic/platform=iOS",
35
49
  "verify:android": "cd android && ./gradlew clean build test && cd ..",
36
- "verify:web": "npm run build",
37
- "lint": "npm run eslint",
38
- "lint:ios": "npm run swiftlint -- lint",
39
- "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
50
+ "verify:web": "pnpm run build",
51
+ "lint": "pnpm run eslint",
52
+ "lint:ios": "pnpm run swiftlint lint",
53
+ "fmt": "pnpm run eslint --fix && pnpm run prettier --write && pnpm run swiftlint --fix --format",
40
54
  "eslint": "eslint . --ext ts",
41
55
  "prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
42
56
  "prettier:check": "prettier --check \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
43
57
  "swiftlint": "node-swiftlint",
58
+ "test": "vitest run",
44
59
  "docgen": "docgen --api CameraViewPlugin --output-readme README.md --output-json dist/docs.json",
45
- "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
60
+ "build": "pnpm run clean && pnpm run docgen && tsc && rollup -c rollup.config.mjs",
46
61
  "clean": "rimraf ./dist",
47
62
  "watch": "tsc --watch",
48
- "prepublishOnly": "npm run build"
63
+ "prepublishOnly": "pnpm run build"
49
64
  },
50
65
  "devDependencies": {
51
66
  "@capacitor/android": "^8.3.1",
@@ -76,7 +91,8 @@
76
91
  "rollup": "^4.60.2",
77
92
  "semantic-release": "^25.0.3",
78
93
  "swiftlint": "^2.0.0",
79
- "typescript": "^5.9.3"
94
+ "typescript": "~6.0.3",
95
+ "vitest": "^4.1.10"
80
96
  },
81
97
  "peerDependencies": {
82
98
  "@capacitor/core": ">=8.0.0"