expo-camera 55.0.15 → 55.0.17

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 (53) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/android/build.gradle +2 -2
  3. package/expo-module.config.json +3 -2
  4. package/ios/CameraViewModule.swift +3 -6
  5. package/ios/Common/ExpoCameraUtils.swift +15 -3
  6. package/ios/Current/BarcodeScanner.swift +37 -17
  7. package/ios/Current/BarcodeScannerUtils.swift +0 -36
  8. package/ios/Current/CameraPhotoCapture.swift +2 -0
  9. package/ios/Current/ExpoBarcodeScannerProvider.swift +13 -0
  10. package/ios/Current/MetaDataDelegate.swift +22 -44
  11. package/ios/ExpoCamera.podspec +2 -13
  12. package/ios/ExpoCameraBarcodeScanning.podspec +29 -0
  13. package/ios/barcode-scanning/ExpoCameraZXingProvider.swift +44 -0
  14. package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15.module → 55.0.17/expo.modules.camera-55.0.17.module} +7 -7
  15. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.17/expo.modules.camera-55.0.17.module.md5 +1 -0
  16. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.17/expo.modules.camera-55.0.17.module.sha1 +1 -0
  17. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.17/expo.modules.camera-55.0.17.module.sha256 +1 -0
  18. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.17/expo.modules.camera-55.0.17.module.sha512 +1 -0
  19. package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15.pom → 55.0.17/expo.modules.camera-55.0.17.pom} +1 -1
  20. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.17/expo.modules.camera-55.0.17.pom.md5 +1 -0
  21. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.17/expo.modules.camera-55.0.17.pom.sha1 +1 -0
  22. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.17/expo.modules.camera-55.0.17.pom.sha256 +1 -0
  23. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.17/expo.modules.camera-55.0.17.pom.sha512 +1 -0
  24. package/local-maven-repo/host/exp/exponent/expo.modules.camera/maven-metadata.xml +4 -4
  25. package/local-maven-repo/host/exp/exponent/expo.modules.camera/maven-metadata.xml.md5 +1 -1
  26. package/local-maven-repo/host/exp/exponent/expo.modules.camera/maven-metadata.xml.sha1 +1 -1
  27. package/local-maven-repo/host/exp/exponent/expo.modules.camera/maven-metadata.xml.sha256 +1 -1
  28. package/local-maven-repo/host/exp/exponent/expo.modules.camera/maven-metadata.xml.sha512 +1 -1
  29. package/package.json +2 -2
  30. package/prebuilds/output/debug/xcframeworks/ExpoCamera.tar.gz +0 -0
  31. package/prebuilds/output/debug/xcframeworks/ExpoCameraBarcodeScanning.tar.gz +0 -0
  32. package/prebuilds/output/release/xcframeworks/ExpoCamera.tar.gz +0 -0
  33. package/prebuilds/output/release/xcframeworks/ExpoCameraBarcodeScanning.tar.gz +0 -0
  34. package/spm.config.json +35 -11
  35. package/ios/Current/BarcodeScannerUnavailable.swift +0 -40
  36. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.15/expo.modules.camera-55.0.15.module.md5 +0 -1
  37. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.15/expo.modules.camera-55.0.15.module.sha1 +0 -1
  38. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.15/expo.modules.camera-55.0.15.module.sha256 +0 -1
  39. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.15/expo.modules.camera-55.0.15.module.sha512 +0 -1
  40. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.15/expo.modules.camera-55.0.15.pom.md5 +0 -1
  41. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.15/expo.modules.camera-55.0.15.pom.sha1 +0 -1
  42. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.15/expo.modules.camera-55.0.15.pom.sha256 +0 -1
  43. package/local-maven-repo/host/exp/exponent/expo.modules.camera/55.0.15/expo.modules.camera-55.0.15.pom.sha512 +0 -1
  44. /package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15-sources.jar → 55.0.17/expo.modules.camera-55.0.17-sources.jar} +0 -0
  45. /package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15-sources.jar.md5 → 55.0.17/expo.modules.camera-55.0.17-sources.jar.md5} +0 -0
  46. /package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15-sources.jar.sha1 → 55.0.17/expo.modules.camera-55.0.17-sources.jar.sha1} +0 -0
  47. /package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15-sources.jar.sha256 → 55.0.17/expo.modules.camera-55.0.17-sources.jar.sha256} +0 -0
  48. /package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15-sources.jar.sha512 → 55.0.17/expo.modules.camera-55.0.17-sources.jar.sha512} +0 -0
  49. /package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15.aar → 55.0.17/expo.modules.camera-55.0.17.aar} +0 -0
  50. /package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15.aar.md5 → 55.0.17/expo.modules.camera-55.0.17.aar.md5} +0 -0
  51. /package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15.aar.sha1 → 55.0.17/expo.modules.camera-55.0.17.aar.sha1} +0 -0
  52. /package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15.aar.sha256 → 55.0.17/expo.modules.camera-55.0.17.aar.sha256} +0 -0
  53. /package/local-maven-repo/host/exp/exponent/expo.modules.camera/{55.0.15/expo.modules.camera-55.0.15.aar.sha512 → 55.0.17/expo.modules.camera-55.0.17.aar.sha512} +0 -0
package/CHANGELOG.md CHANGED
@@ -10,6 +10,18 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 55.0.17 — 2026-05-04
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [iOS] Fix orientation issue caused by upstream changes. ([#44171](https://github.com/expo/expo/pull/44171) by [@alanjhughes](https://github.com/alanjhughes))
18
+
19
+ ## 55.0.16 — 2026-04-21
20
+
21
+ ### 🐛 Bug fixes
22
+
23
+ - [iOS] Fix inconsistent barcode `type` value returned by ZXing fallback scanner (code39, pdf417, codabar) — it now returns the same format as the AVFoundation scanner (e.g. `"code39"` instead of `"org.iso.Code39"`). ([#44726](https://github.com/expo/expo/pull/44726) by [@jensdev](https://github.com/jensdev))
24
+
13
25
  ## 55.0.15 — 2026-04-09
14
26
 
15
27
  _This version does not introduce any user-facing changes._
@@ -38,6 +50,7 @@ _This version does not introduce any user-facing changes._
38
50
 
39
51
  - [Web] Fix `isAvailableAsync` returning `true` on devices without a camera. ([#43932](https://github.com/expo/expo/pull/43932) by [@alanjhughes](https://github.com/alanjhughes))
40
52
  - [iOS] Added explicit `import React` for xcframework compatibility. ([#44248](https://github.com/expo/expo/pull/44248) by [@chrfalch](https://github.com/chrfalch))
53
+ - [iOS] Extract barcode scanning (ZXingObjC) into a separate `ExpoCameraBarcodeScanning` companion pod. Disabling barcode scanning via config plugin now correctly excludes ZXingObjC from precompiled builds, reducing binary size. (by [@chrfalch](https://github.com/chrfalch)) ([#44766](https://github.com/expo/expo/pull/44766) by [@chrfalch](https://github.com/chrfalch))
41
54
 
42
55
  ## 55.0.9 — 2026-02-25
43
56
 
@@ -4,7 +4,7 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '55.0.15'
7
+ version = '55.0.17'
8
8
 
9
9
  def barcodeScannerEnabled = findProperty('expo.camera.barcode-scanner-enabled')
10
10
  def isBarcodeScannerEnabled = (barcodeScannerEnabled ?: "true").toString() != "false"
@@ -13,7 +13,7 @@ android {
13
13
  namespace "expo.modules.camera"
14
14
  defaultConfig {
15
15
  versionCode 32
16
- versionName "55.0.15"
16
+ versionName "55.0.17"
17
17
  }
18
18
  }
19
19
 
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "platforms": ["apple", "android", "web"],
3
3
  "apple": {
4
- "modules": ["CameraViewModule"]
4
+ "modules": ["CameraViewModule"],
5
+ "podspecPath": ["ios/ExpoCamera.podspec"]
5
6
  },
6
7
  "android": {
7
8
  "modules": ["expo.modules.camera.CameraViewModule"],
8
9
  "publication": {
9
10
  "groupId": "host.exp.exponent",
10
11
  "artifactId": "expo.modules.camera",
11
- "version": "55.0.15",
12
+ "version": "55.0.17",
12
13
  "repository": "local-maven-repo"
13
14
  }
14
15
  }
@@ -148,7 +148,6 @@ public final class CameraViewModule: Module, ScannerResultHandler {
148
148
  }
149
149
 
150
150
  Prop("barcodeScannerEnabled") { (view, scanBarcodes: Bool?) in
151
- #if canImport(ZXingObjC)
152
151
  if let scanBarcodes, view.isScanningBarcodes != scanBarcodes {
153
152
  view.isScanningBarcodes = scanBarcodes
154
153
  return
@@ -156,17 +155,15 @@ public final class CameraViewModule: Module, ScannerResultHandler {
156
155
  if scanBarcodes == nil && view.isScanningBarcodes != false {
157
156
  view.isScanningBarcodes = false
158
157
  }
159
- #endif
160
158
  }
161
159
 
162
160
  Prop("barcodeScannerSettings") { (view, settings: BarcodeSettings?) in
163
- #if canImport(ZXingObjC)
164
161
  if let settings {
162
+ if view.barcodeScanner?.isAvailable == false {
163
+ self.appContext?.jsLogger.warn("Barcode scanning has been disabled")
164
+ }
165
165
  view.setBarcodeScannerSettings(settings: settings)
166
166
  }
167
- #else
168
- self.appContext?.jsLogger.warn("Barcode scanning has been disabled")
169
- #endif
170
167
  }
171
168
 
172
169
  Prop("mute") { (view, muted: Bool?) in
@@ -167,6 +167,16 @@ struct ExpoCameraUtils {
167
167
  return UIImage(cgImage: croppedCgImage, scale: image.scale, orientation: image.imageOrientation)
168
168
  }
169
169
 
170
+ static func normalizeOrientation(of image: UIImage) -> UIImage {
171
+ guard image.imageOrientation != .up else {
172
+ return image
173
+ }
174
+ let renderer = UIGraphicsImageRenderer(size: image.size)
175
+ return renderer.image { _ in
176
+ image.draw(in: CGRect(origin: .zero, size: image.size))
177
+ }
178
+ }
179
+
170
180
  static func write(data: Data, to path: String) -> String? {
171
181
  let url = URL(fileURLWithPath: path)
172
182
  do {
@@ -237,6 +247,8 @@ struct ExpoCameraUtils {
237
247
  throw CameraSavingImageException("Failed to locate `cacheDirectory`")
238
248
  }
239
249
 
250
+ let normalizedImage = normalizeOrientation(of: image)
251
+
240
252
  var result = [String: Any]()
241
253
  let directory = URL(fileURLWithPath: cachesDirectory.path).appendingPathComponent("Camera")
242
254
  let filename = UUID().uuidString.appending(".jpg")
@@ -244,13 +256,13 @@ struct ExpoCameraUtils {
244
256
 
245
257
  FileSystemUtilities.ensureDirExists(at: directory)
246
258
 
247
- guard let data = data(from: image, with: options.metadata ?? [:], quality: Float(options.quality)) else {
259
+ guard let data = data(from: normalizedImage, with: options.metadata ?? [:], quality: Float(options.quality)) else {
248
260
  throw CameraSavingImageException("Image data could not be processed")
249
261
  }
250
262
 
251
263
  result["url"] = fileUrl.absoluteString
252
- result["width"] = image.size.width
253
- result["height"] = image.size.height
264
+ result["width"] = normalizedImage.size.width
265
+ result["height"] = normalizedImage.size.height
254
266
  result["base64"] = options.base64 ? data.base64EncodedString() : nil
255
267
 
256
268
  do {
@@ -2,9 +2,6 @@ import AVFoundation
2
2
 
3
3
  let BARCODE_TYPES_KEY = "barcodeTypes"
4
4
 
5
- #if canImport(ZXingObjC)
6
- import ZXingObjC
7
-
8
5
  class BarcodeScanner: NSObject, BarcodeScanningResponseHandler {
9
6
  private var onBarcodeScanned: (([String: Any]?) -> Void)?
10
7
  var isScanningBarcodes = false
@@ -13,26 +10,37 @@ class BarcodeScanner: NSObject, BarcodeScanningResponseHandler {
13
10
 
14
11
  private let session: AVCaptureSession
15
12
  private let sessionQueue: DispatchQueue
16
- private let zxingCaptureQueue = DispatchQueue(label: "com.zxing.captureQueue")
13
+ private let captureQueue = DispatchQueue(label: "com.expo.barcodeScannerCaptureQueue")
17
14
 
18
15
  private var metadataOutput: AVCaptureMetadataOutput?
19
16
  private var videoDataOutput: AVCaptureVideoDataOutput?
20
17
  private var settings = BarcodeScannerUtils.getDefaultSettings()
21
- private var zxingBarcodeReaders: [AVMetadataObject.ObjectType: ZXReader] = [
22
- AVMetadataObject.ObjectType.pdf417: ZXPDF417Reader(),
23
- AVMetadataObject.ObjectType.code39: ZXCode39Reader()
24
- ]
25
18
  private var previewLayer: AVCaptureVideoPreviewLayer?
26
- private var zxingEnabled = true
19
+ private var barcodeProviderEnabled = true
27
20
  private var delegate: MetaDataDelegate?
28
21
 
22
+ private let barcodeProvider: ExpoBarcodeScannerProvider?
23
+
29
24
  init(session: AVCaptureSession, sessionQueue: DispatchQueue) {
30
25
  self.session = session
31
26
  self.sessionQueue = sessionQueue
27
+ self.barcodeProvider = BarcodeScanner.discoverProvider()
28
+ }
32
29
 
33
- if #available(iOS 15.4, *) {
34
- zxingBarcodeReaders[AVMetadataObject.ObjectType.codabar] = ZXCodaBarReader()
30
+ /// True when a barcode scanner provider is available (the companion pod is linked).
31
+ var isAvailable: Bool {
32
+ return barcodeProvider != nil
33
+ }
34
+
35
+ /// Discovers the optional barcode scanner provider at runtime.
36
+ /// The provider module registers by exposing a class named "ExpoCameraZXingProvider"
37
+ /// that conforms to ExpoBarcodeScannerProvider. Returns nil if the provider pod isn't linked.
38
+ static func discoverProvider() -> ExpoBarcodeScannerProvider? {
39
+ guard let cls = NSClassFromString("ExpoCameraZXingProvider") as? NSObject.Type,
40
+ let instance = cls.init() as? ExpoBarcodeScannerProvider else {
41
+ return nil
35
42
  }
43
+ return instance
36
44
  }
37
45
 
38
46
  func setSettings(_ newSettings: [String: [AVMetadataObject.ObjectType]]) {
@@ -41,8 +49,13 @@ class BarcodeScanner: NSObject, BarcodeScanningResponseHandler {
41
49
  let newTypes = Set(value)
42
50
  if previousTypes != newTypes {
43
51
  settings[BARCODE_TYPES_KEY] = value
44
- let zxingCoveredTypes = Set(zxingBarcodeReaders.keys)
45
- zxingEnabled = !zxingCoveredTypes.isDisjoint(with: newTypes)
52
+ if let barcodeProvider {
53
+ let supportedTypeSet = Set(barcodeProvider.supportedTypes)
54
+ let requestedRawValues = Set(newTypes.map { $0.rawValue })
55
+ barcodeProviderEnabled = !supportedTypeSet.isDisjoint(with: requestedRawValues)
56
+ } else {
57
+ barcodeProviderEnabled = false
58
+ }
46
59
  maybeStartBarcodeScanning()
47
60
  }
48
61
  }
@@ -85,6 +98,10 @@ class BarcodeScanner: NSObject, BarcodeScanningResponseHandler {
85
98
  return
86
99
  }
87
100
 
101
+ guard barcodeProvider != nil else {
102
+ return
103
+ }
104
+
88
105
  if metadataOutput == nil || videoDataOutput == nil {
89
106
  addOutputs()
90
107
  if metadataOutput == nil {
@@ -108,11 +125,15 @@ class BarcodeScanner: NSObject, BarcodeScanningResponseHandler {
108
125
  }
109
126
 
110
127
  private func addOutputs() {
128
+ guard let barcodeProvider else {
129
+ return
130
+ }
131
+
111
132
  delegate = MetaDataDelegate(
112
133
  settings: settings,
113
134
  previewLayer: previewLayer,
114
- zxingBarcodeReaders: zxingBarcodeReaders,
115
- zxingEnabled: zxingEnabled,
135
+ barcodeProvider: barcodeProvider,
136
+ barcodeProviderEnabled: barcodeProviderEnabled,
116
137
  metadataResultHandler: self)
117
138
 
118
139
  session.beginConfiguration()
@@ -129,7 +150,7 @@ class BarcodeScanner: NSObject, BarcodeScanningResponseHandler {
129
150
  let output = AVCaptureVideoDataOutput()
130
151
  output.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
131
152
  output.alwaysDiscardsLateVideoFrames = true
132
- output.setSampleBufferDelegate(delegate, queue: zxingCaptureQueue)
153
+ output.setSampleBufferDelegate(delegate, queue: captureQueue)
133
154
  if session.canAddOutput(output) {
134
155
  session.addOutput(output)
135
156
  videoDataOutput = output
@@ -161,4 +182,3 @@ class BarcodeScanner: NSObject, BarcodeScanningResponseHandler {
161
182
  self.onBarcodeScanned?(result)
162
183
  }
163
184
  }
164
- #endif
@@ -1,7 +1,4 @@
1
1
  import AVFoundation
2
- #if canImport(ZXingObjC)
3
- import ZXingObjC
4
- #endif
5
2
  import VisionKit
6
3
  import Vision
7
4
 
@@ -105,37 +102,4 @@ class BarcodeScannerUtils {
105
102
  ]
106
103
  }
107
104
 
108
- #if canImport(ZXingObjC)
109
- static func zxResultToDictionary(_ barcodeScannerResult: ZXResult) -> [String: Any] {
110
- var result = [String: Any]()
111
- result["type"] = BarcodeScannerUtils.zxingFormatToString(barcodeScannerResult.barcodeFormat)
112
-
113
- var data = ""
114
- for i in 0..<barcodeScannerResult.text.count {
115
- let character = barcodeScannerResult.text[barcodeScannerResult.text.index(barcodeScannerResult.text.startIndex, offsetBy: i)]
116
- if character != "\0" {
117
- data.append(character)
118
- }
119
- }
120
- result["data"] = data
121
-
122
- return result
123
- }
124
-
125
- static func zxingFormatToString(_ format: ZXBarcodeFormat) -> String {
126
- switch format {
127
- case kBarcodeFormatPDF417:
128
- return AVMetadataObject.ObjectType.pdf417.rawValue
129
- case kBarcodeFormatCode39:
130
- return AVMetadataObject.ObjectType.code39.rawValue
131
- case kBarcodeFormatCodabar:
132
- if #available(iOS 15.4, *) {
133
- return AVMetadataObject.ObjectType.codabar.rawValue
134
- }
135
- return "unknown"
136
- default:
137
- return "unknown"
138
- }
139
- }
140
- #endif
141
105
  }
@@ -163,6 +163,7 @@ class CameraPhotoCapture: NSObject, AVCapturePhotoCaptureDelegate {
163
163
  let croppedSize = AVMakeRect(aspectRatio: previewSize, insideRect: cropRect)
164
164
 
165
165
  takenImage = ExpoCameraUtils.crop(image: takenImage, to: croppedSize)
166
+ takenImage = ExpoCameraUtils.normalizeOrientation(of: takenImage)
166
167
 
167
168
  let width = takenImage.size.width
168
169
  let height = takenImage.size.height
@@ -185,6 +186,7 @@ class CameraPhotoCapture: NSObject, AVCapturePhotoCaptureDelegate {
185
186
  response["exif"] = updatedExif
186
187
 
187
188
  var updatedMetadata = metadata
189
+ updatedMetadata[kCGImagePropertyOrientation as String] = 1
188
190
 
189
191
  if let additionalExif = options.additionalExif {
190
192
  for (key, value) in additionalExif {
@@ -0,0 +1,13 @@
1
+ import AVFoundation
2
+
3
+ /// Protocol for barcode scanner providers.
4
+ /// Implement this in a separate module and expose via @objc(ExpoCameraZXingProvider) for runtime discovery.
5
+ ///
6
+ /// Returned dictionaries should have `"type"` and `"data"` keys matching the shape expected by the JS event.
7
+ @objc public protocol ExpoBarcodeScannerProvider {
8
+ /// The AVMetadataObject.ObjectType raw values this provider handles.
9
+ @objc var supportedTypes: [String] { get }
10
+
11
+ /// Scan a video frame for barcodes. Returns result dictionaries or an empty array.
12
+ @objc func scanBarcodes(from image: CGImage) -> [[String: Any]]
13
+ }
@@ -1,28 +1,29 @@
1
- #if canImport(ZXingObjC)
2
1
  import AVFoundation
3
- import ZXingObjC
2
+ import CoreImage
4
3
 
5
4
  class MetaDataDelegate: NSObject, AVCaptureMetadataOutputObjectsDelegate, AVCaptureVideoDataOutputSampleBufferDelegate {
6
5
  private var settings: [String: [AVMetadataObject.ObjectType]]
7
6
  private var previewLayer: AVCaptureVideoPreviewLayer?
8
- private var zxingBarcodeReaders: [AVMetadataObject.ObjectType: ZXReader]
9
- private var zxingEnabled = true
10
- private var zxingFPSProcessed = 6.0
7
+ private let barcodeProvider: ExpoBarcodeScannerProvider
8
+ private var barcodeProviderEnabled = true
9
+ private var barcodeProviderFPSProcessed = 6.0
11
10
  private var lastFrameTimeStamp = 0.0
12
11
  private let responseHandler: BarcodeScanningResponseHandler
13
12
 
13
+ private let ciContext = CIContext()
14
+
14
15
  init(
15
16
  settings: [String: [AVMetadataObject.ObjectType]],
16
17
  previewLayer: AVCaptureVideoPreviewLayer?,
17
- zxingBarcodeReaders: [AVMetadataObject.ObjectType: ZXReader],
18
- zxingEnabled: Bool,
18
+ barcodeProvider: ExpoBarcodeScannerProvider,
19
+ barcodeProviderEnabled: Bool,
19
20
  metadataResultHandler: BarcodeScanningResponseHandler
20
21
  ) {
21
22
  self.settings = settings
22
23
  self.previewLayer = previewLayer
23
- self.zxingEnabled = zxingEnabled
24
+ self.barcodeProviderEnabled = barcodeProviderEnabled
24
25
  self.responseHandler = metadataResultHandler
25
- self.zxingBarcodeReaders = zxingBarcodeReaders
26
+ self.barcodeProvider = barcodeProvider
26
27
  }
27
28
 
28
29
  func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
@@ -30,6 +31,8 @@ class MetaDataDelegate: NSObject, AVCaptureMetadataOutputObjectsDelegate, AVCapt
30
31
  return
31
32
  }
32
33
 
34
+ let barcodeProviderTypes = Set(barcodeProvider.supportedTypes)
35
+
33
36
  for metadata in metadataObjects {
34
37
  var codeMetadata = metadata as? AVMetadataMachineReadableCodeObject
35
38
  if let previewLayer {
@@ -37,7 +40,8 @@ class MetaDataDelegate: NSObject, AVCaptureMetadataOutputObjectsDelegate, AVCapt
37
40
  }
38
41
 
39
42
  for barcodeType in settings {
40
- if zxingBarcodeReaders[barcodeType] != nil {
43
+ // Skip types handled by the barcode provider — those come through captureOutput instead
44
+ if barcodeProviderTypes.contains(barcodeType.rawValue) {
41
45
  continue
42
46
  }
43
47
 
@@ -51,11 +55,11 @@ class MetaDataDelegate: NSObject, AVCaptureMetadataOutputObjectsDelegate, AVCapt
51
55
  }
52
56
 
53
57
  func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
54
- guard zxingEnabled else {
58
+ guard barcodeProviderEnabled else {
55
59
  return
56
60
  }
57
61
 
58
- let kMinMargin = 1.0 / zxingFPSProcessed
62
+ let kMinMargin = 1.0 / barcodeProviderFPSProcessed
59
63
  let presentTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
60
64
  let curFrameTimeStamp = Double(presentTimeStamp.value) / Double(presentTimeStamp.timescale)
61
65
 
@@ -63,42 +67,16 @@ class MetaDataDelegate: NSObject, AVCaptureMetadataOutputObjectsDelegate, AVCapt
63
67
  lastFrameTimeStamp = curFrameTimeStamp
64
68
 
65
69
  if let videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer),
66
- let videoFrameImage = ZXCGImageLuminanceSource.createImage(from: videoFrame) {
67
- self.scanBarcodes(from: videoFrameImage) { barcodeScannerResult in
68
- self.responseHandler.onScanningResult(BarcodeScannerUtils.zxResultToDictionary(barcodeScannerResult))
70
+ let image = createImage(from: videoFrame) {
71
+ for result in barcodeProvider.scanBarcodes(from: image) {
72
+ self.responseHandler.onScanningResult(result)
69
73
  }
70
74
  }
71
75
  }
72
76
  }
73
77
 
74
- func scanBarcodes(from image: CGImage, completion: @escaping (ZXResult) -> Void) {
75
- let source = ZXCGImageLuminanceSource(cgImage: image)
76
- let binarizer = ZXHybridBinarizer(source: source)
77
- let bitmap = ZXBinaryBitmap(binarizer: binarizer)
78
-
79
- var result: ZXResult?
80
-
81
- for reader in zxingBarcodeReaders.values {
82
- result = try? reader.decode(bitmap, hints: nil)
83
- if result != nil {
84
- break
85
- }
86
- }
87
-
88
- if result == nil && bitmap?.rotateSupported == true {
89
- if let rotatedBitmap = bitmap?.rotateCounterClockwise() {
90
- for reader in zxingBarcodeReaders.values {
91
- result = try? reader.decode(rotatedBitmap, hints: nil)
92
- if result != nil {
93
- break
94
- }
95
- }
96
- }
97
- }
98
-
99
- if let result {
100
- completion(result)
101
- }
78
+ private func createImage(from buffer: CVImageBuffer) -> CGImage? {
79
+ let ciImage = CIImage(cvImageBuffer: buffer)
80
+ return ciContext.createCGImage(ciImage, from: ciImage.extent)
102
81
  }
103
82
  }
104
- #endif
@@ -1,9 +1,6 @@
1
1
  require 'json'
2
2
 
3
3
  package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
- podfile_properties = JSON.parse(File.read("#{Pod::Config.instance.installation_root}/Podfile.properties.json")) rescue {}
5
-
6
- barcode_scanner_enabled = podfile_properties.fetch('expo-camera.barcode-scanner-enabled', 'true') != 'false'
7
4
 
8
5
  Pod::Spec.new do |s|
9
6
  s.name = 'ExpoCamera'
@@ -20,22 +17,14 @@ Pod::Spec.new do |s|
20
17
  s.static_framework = true
21
18
 
22
19
  s.dependency 'ExpoModulesCore'
23
- if barcode_scanner_enabled
24
- s.dependency 'ZXingObjC/PDF417'
25
- s.dependency 'ZXingObjC/OneD'
26
- end
27
20
 
28
21
  # Swift/Objective-C compatibility
29
- xcconfig = {
22
+ s.pod_target_xcconfig = {
30
23
  'DEFINES_MODULE' => 'YES',
31
24
  'SWIFT_COMPILATION_MODE' => 'wholemodule'
32
25
  }
33
26
 
34
- if barcode_scanner_enabled
35
- xcconfig['GCC_PREPROCESSOR_DEFINITIONS'] = 'ZXINGOBJC_USE_SUBSPECS ZXINGOBJC_PDF417 ZXINGOBJC_ONED'
36
- end
37
-
38
- s.pod_target_xcconfig = xcconfig
27
+ s.exclude_files = "barcode-scanning/**"
39
28
 
40
29
  if !$ExpoUseSources&.include?(package['name']) && ENV['EXPO_USE_SOURCE'].to_i == 0 && File.exist?("#{s.name}.xcframework") && Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.10.0')
41
30
  s.source_files = "**/*.h"
@@ -0,0 +1,29 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'ExpoCameraBarcodeScanning'
7
+ s.version = package['version']
8
+ s.summary = 'ZXing-based barcode scanning provider for expo-camera'
9
+ s.description = 'Provides PDF417, Code39, and Codabar barcode scanning via ZXingObjC for expo-camera'
10
+ s.license = package['license']
11
+ s.author = package['author']
12
+ s.homepage = package['homepage']
13
+ s.platforms = { :ios => '15.1' }
14
+ s.swift_version = '5.9'
15
+ s.source = { :git => 'https://github.com/expo/expo.git' }
16
+ s.static_framework = true
17
+
18
+ s.dependency 'ExpoCamera'
19
+ s.dependency 'ZXingObjC/PDF417'
20
+ s.dependency 'ZXingObjC/OneD'
21
+
22
+ s.source_files = 'barcode-scanning/**/*.swift'
23
+
24
+ s.pod_target_xcconfig = {
25
+ 'DEFINES_MODULE' => 'YES',
26
+ 'GCC_PREPROCESSOR_DEFINITIONS' => 'ZXINGOBJC_USE_SUBSPECS ZXINGOBJC_PDF417 ZXINGOBJC_ONED',
27
+ 'SWIFT_COMPILATION_MODE' => 'wholemodule'
28
+ }
29
+ end
@@ -0,0 +1,44 @@
1
+ import AVFoundation
2
+ internal import ZXingObjC
3
+ import ExpoCamera
4
+
5
+ @objc(ExpoCameraZXingProvider)
6
+ class ExpoCameraZXingProvider: NSObject, ExpoBarcodeScannerProvider {
7
+ private var readers: [String: ZXReader] = [:]
8
+
9
+ override init() {
10
+ super.init()
11
+ readers[AVMetadataObject.ObjectType.pdf417.rawValue] = ZXPDF417Reader()
12
+ readers[AVMetadataObject.ObjectType.code39.rawValue] = ZXCode39Reader()
13
+ if #available(iOS 15.4, *) {
14
+ readers[AVMetadataObject.ObjectType.codabar.rawValue] = ZXCodaBarReader()
15
+ }
16
+ }
17
+
18
+ var supportedTypes: [String] {
19
+ Array(readers.keys)
20
+ }
21
+
22
+ func scanBarcodes(from image: CGImage) -> [[String: Any]] {
23
+ let source = ZXCGImageLuminanceSource(cgImage: image)
24
+ let binarizer = ZXHybridBinarizer(source: source)
25
+ let bitmap = ZXBinaryBitmap(binarizer: binarizer)
26
+
27
+ for (type, reader) in readers {
28
+ if let result = try? reader.decode(bitmap, hints: nil) {
29
+ return [["type": type, "data": result.text.filter { $0 != "\0" }]]
30
+ }
31
+ }
32
+
33
+ // Retry with rotated image for barcodes at non-standard orientations
34
+ if bitmap?.rotateSupported == true, let rotated = bitmap?.rotateCounterClockwise() {
35
+ for (type, reader) in readers {
36
+ if let result = try? reader.decode(rotated, hints: nil) {
37
+ return [["type": type, "data": result.text.filter { $0 != "\0" }]]
38
+ }
39
+ }
40
+ }
41
+
42
+ return []
43
+ }
44
+ }
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "host.exp.exponent",
5
5
  "module": "expo.modules.camera",
6
- "version": "55.0.15",
6
+ "version": "55.0.17",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -40,8 +40,8 @@
40
40
  ],
41
41
  "files": [
42
42
  {
43
- "name": "expo.modules.camera-55.0.15.aar",
44
- "url": "expo.modules.camera-55.0.15.aar",
43
+ "name": "expo.modules.camera-55.0.17.aar",
44
+ "url": "expo.modules.camera-55.0.17.aar",
45
45
  "size": 247692,
46
46
  "sha512": "ec176be898748075419c264cfd6587d184ae6223dc64d5bec48ec4baeb959eed532faa061ccd62a7efcac779971d5ae5c93c172b6ec042c64c070b3f718953a8",
47
47
  "sha256": "d192b7cd2385ccd27c32b317970562c10c7288b4455532b2676fd9c05860c151",
@@ -146,8 +146,8 @@
146
146
  ],
147
147
  "files": [
148
148
  {
149
- "name": "expo.modules.camera-55.0.15.aar",
150
- "url": "expo.modules.camera-55.0.15.aar",
149
+ "name": "expo.modules.camera-55.0.17.aar",
150
+ "url": "expo.modules.camera-55.0.17.aar",
151
151
  "size": 247692,
152
152
  "sha512": "ec176be898748075419c264cfd6587d184ae6223dc64d5bec48ec4baeb959eed532faa061ccd62a7efcac779971d5ae5c93c172b6ec042c64c070b3f718953a8",
153
153
  "sha256": "d192b7cd2385ccd27c32b317970562c10c7288b4455532b2676fd9c05860c151",
@@ -166,8 +166,8 @@
166
166
  },
167
167
  "files": [
168
168
  {
169
- "name": "expo.modules.camera-55.0.15-sources.jar",
170
- "url": "expo.modules.camera-55.0.15-sources.jar",
169
+ "name": "expo.modules.camera-55.0.17-sources.jar",
170
+ "url": "expo.modules.camera-55.0.17-sources.jar",
171
171
  "size": 29083,
172
172
  "sha512": "026c8ca43c56694a89b33c7a57a7b0ad7b8c4a8552911f5198cf167a1835f1006a32501d17502cc4ffa545b83b2ff73a442090d5de819dbf7ccfa7279ce8f36b",
173
173
  "sha256": "fb6fc10073864be50cdcf3f26efa6904cadbb709bb471a3db65d819110a649c7",
@@ -0,0 +1 @@
1
+ 3ba000088bf2e662d8c80fb36011d632d200cccc662f426a9974d6d3340873ab
@@ -0,0 +1 @@
1
+ 5d779bed2377d54f6b03ac6a6678159b7ee1820f496f79f678f659f99be6694e144b76ae2fd67bbc8064d79f6b5336266b06a65a28a57b16f6b993359693f833
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>host.exp.exponent</groupId>
11
11
  <artifactId>expo.modules.camera</artifactId>
12
- <version>55.0.15</version>
12
+ <version>55.0.17</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.camera</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -0,0 +1 @@
1
+ 92b0c61a472a096ddf24dbf4adb72d2a8c0e09fd7dc9d4ee5eae73395395adc6
@@ -0,0 +1 @@
1
+ 09bb25257c37e8e12687ae31edec4b399ad171b7f6487d9bb4ec2efbc3a2f804dd0730809152cef37d421e684f064684ac231790a36fea0c5bbfcf0142afca59
@@ -3,11 +3,11 @@
3
3
  <groupId>host.exp.exponent</groupId>
4
4
  <artifactId>expo.modules.camera</artifactId>
5
5
  <versioning>
6
- <latest>55.0.15</latest>
7
- <release>55.0.15</release>
6
+ <latest>55.0.17</latest>
7
+ <release>55.0.17</release>
8
8
  <versions>
9
- <version>55.0.15</version>
9
+ <version>55.0.17</version>
10
10
  </versions>
11
- <lastUpdated>20260409135100</lastUpdated>
11
+ <lastUpdated>20260504210932</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- e9faad40fed14629b5ce97dfccf8185c
1
+ 6d14964bb67e6c84a210c2945e5334e0
@@ -1 +1 @@
1
- d74892eaa65467ea141b275df4b926a832dac940
1
+ 8fdd72afb4727b7b9c883bc43aa1c3b87db4282d
@@ -1 +1 @@
1
- 149a621f539e89250fed23d1de1b074d74734c042afc7f19ee53907fad5927f5
1
+ d01185a7f761f309435f24732b5c7f36b40f157ad6066eed6879813304450a5d
@@ -1 +1 @@
1
- 1bfb5ed3b719a39d74adbb26b10180651e844d3236f5e08c0ace616b2923f6d252ed33d885e836167b05f04e65ff59263787277b5f6fe77cc23f4afd6555395f
1
+ 5b6276821e441d21a10ad8d3b0fc8b2b9650ec4913b153d7eb6b8913211a35587ed83b94b322ae70bc6cbb62b0a4618e13607ec7ab0073883f79760c2f02e8ec
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-camera",
3
- "version": "55.0.15",
3
+ "version": "55.0.17",
4
4
  "description": "A React component that renders a preview for the device's either front or back camera. Camera's parameters like zoom, auto focus, white balance and flash mode are adjustable. With expo-camera, one can also take photos and record videos that are saved to the app's cache. Morever, the component is also capable of detecting faces and bar codes appearing on the preview.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -50,5 +50,5 @@
50
50
  "optional": true
51
51
  }
52
52
  },
53
- "gitHead": "b0ada30fd6a819c5f98a23d99b1b89a2ed68743d"
53
+ "gitHead": "6ab08fe32823d60ad90b3ca2821b4d99a3ae06e9"
54
54
  }
package/spm.config.json CHANGED
@@ -13,32 +13,56 @@
13
13
  "Hermes",
14
14
  "expo-modules-core/ExpoModulesCore"
15
15
  ],
16
- "spmPackages": [
17
- {
18
- "url": "https://github.com/zxingify/zxingify-objc.git",
19
- "productName": "ZXingObjC",
20
- "version": {
21
- "exact": "3.6.9"
22
- }
23
- }
24
- ],
25
16
  "targets": [
26
17
  {
27
18
  "type": "swift",
28
19
  "name": "ExpoCamera",
29
20
  "path": "ios",
30
21
  "pattern": "**/*.swift",
22
+ "exclude": ["barcode-scanning/**"],
31
23
  "dependencies": [
32
24
  "ReactNativeDependencies",
33
25
  "React",
34
26
  "Hermes",
35
- "expo-modules-core/ExpoModulesCore",
36
- "ZXingObjC"
27
+ "expo-modules-core/ExpoModulesCore"
37
28
  ],
38
29
  "linkedFrameworks": [
39
30
  "Foundation",
40
31
  "UIKit",
41
32
  "AVFoundation"
33
+ ]
34
+ }
35
+ ]
36
+ },
37
+ {
38
+ "name": "ExpoCameraBarcodeScanning",
39
+ "podName": "ExpoCameraBarcodeScanning",
40
+ "platforms": [
41
+ "iOS(.v15)"
42
+ ],
43
+ "autolinkWhen": {
44
+ "podfileProperty": "expo.camera.barcode-scanner-enabled",
45
+ "disabledValue": "false"
46
+ },
47
+ "externalDependencies": [],
48
+ "spmPackages": [
49
+ {
50
+ "url": "https://github.com/zxingify/zxingify-objc.git",
51
+ "productName": "ZXingObjC",
52
+ "version": {
53
+ "exact": "3.6.9"
54
+ }
55
+ }
56
+ ],
57
+ "targets": [
58
+ {
59
+ "type": "swift",
60
+ "name": "ExpoCameraBarcodeScanning",
61
+ "path": "ios/barcode-scanning",
62
+ "pattern": "**/*.swift",
63
+ "dependencies": [
64
+ "ExpoCamera",
65
+ "ZXingObjC"
42
66
  ],
43
67
  "compilerFlags": {
44
68
  "swift": [
@@ -1,40 +0,0 @@
1
- #if !canImport(ZXingObjC)
2
- import AVFoundation
3
-
4
- class BarcodeScanner: NSObject, BarcodeScanningResponseHandler {
5
- private var onBarcodeScanned: (([String: Any]?) -> Void)?
6
- var isScanningBarcodes = false
7
-
8
- init(session: AVCaptureSession, sessionQueue: DispatchQueue) {}
9
-
10
- func setSettings(_ newSettings: [String: [AVMetadataObject.ObjectType]]) {}
11
-
12
- func setPreviewLayer(layer: AVCaptureVideoPreviewLayer) {}
13
-
14
- func setIsEnabled(_ enabled: Bool) {
15
- isScanningBarcodes = enabled
16
- if enabled {
17
- onBarcodeScanned?(nil)
18
- }
19
- }
20
-
21
- func setConnection(enabled: Bool) {}
22
-
23
- func setOnBarcodeScanned(_ onBarcodeScanned: @escaping ([String: Any]?) -> Void) {
24
- self.onBarcodeScanned = onBarcodeScanned
25
- }
26
-
27
- func maybeStartBarcodeScanning() {}
28
-
29
- func stopBarcodeScanning() {
30
- if isScanningBarcodes {
31
- isScanningBarcodes = false
32
- onBarcodeScanned?(nil)
33
- }
34
- }
35
-
36
- func onScanningResult(_ result: [String: Any]) {
37
- onBarcodeScanned?(result)
38
- }
39
- }
40
- #endif
@@ -1 +0,0 @@
1
- 98ecef2f29437da2479e695c9488bf8de60d4c594e44521b74de663480363e05
@@ -1 +0,0 @@
1
- 07b524c0504680bde71c644af3678feff97a682b6f0f45bed13d2ec7b01faa13b1af8597cd4b2a2bc881d968fff888d912d25c2bdd01be2763997d832ccaf666
@@ -1 +0,0 @@
1
- 5ee450d71dcb402ca3304f3d6be39127fd6aa2375ba28485e50c164626df12d5
@@ -1 +0,0 @@
1
- 8a873b76d4eba416eb86fa4bdfa21e84dba9ed1b8e702f281180e38cacda634ebf6113fbf08020b332305c6b7bdf5c139ecef3253ebbeff11a9869e6c41e11f4