Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Sources/OSBarcodeLib/Models/OSBARCScanParameters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@ public struct OSBARCScanParameters {
// The optional hint, to scan a specific format (e.g. only qr code). `Nil` or `unknown` value means it can scan all.
public let hint: OSBARCScannerHint?

// The optional list of hints, to restrict scanning to a specific set of formats (e.g. only qr code and ean-13). When non-empty, takes precedence over `hint`. `.unknown` anywhere in the list means it can scan all.
public let hints: [OSBARCScannerHint]?

public init(scanInstructions: String,
scanButtonText: String?,
cameraDirection: OSBARCCameraModel,
scanOrientation: OSBARCOrientationModel,
hint: OSBARCScannerHint?) {
hint: OSBARCScannerHint?,
hints: [OSBARCScannerHint]? = nil) {
self.scanInstructions = scanInstructions
self.scanButtonText = scanButtonText
self.cameraDirection = cameraDirection
self.scanOrientation = scanOrientation
self.hint = hint
self.hints = hints
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,27 @@ final class OSBARCCaptureOutputDecoder: NSObject, AVCaptureVideoDataOutputSample
private let scanThroughButton: Bool
/// Indicates if scanning is enabled (when there's a Scan Button).
private var scanButtonEnabled: Bool
/// A hint, to scan a specific format (e.g. only qr code). `Nil` or `unknown` value means it can scan all.
private var hint: OSBARCScannerHint?
/// A list of hints, to restrict scanning to a specific set of formats (e.g. only qr code and ean-13). An empty list means it can scan all.
private var hints: [OSBARCScannerHint]

/// The publisher's cancellable instance collector.
private var cancellables: Set<AnyCancellable> = []

convenience init(_ scanResult: Binding<OSBARCScanResult>, _ scanThroughButton: Bool, _ scanButtonEnabled: Bool = false, andHint hint: OSBARCScannerHint? = nil) {
self.init(scanResult, scanThroughButton, scanButtonEnabled, andHints: hint.map { [$0] } ?? [])
}

/// Constructor.
/// - Parameters:
/// - scanResult: Binding object with the value to return.
/// - scanThroughButton: Boolean indicating if scanning should be performed automatically or after clicking the Scan Button.
/// - scanButtonEnabled: Indicates if scanning has already been set on.
/// - hint: The optional hint, to scan a specific format (e.g. only qr code). `Nil` or `unknown` value means it can scan all.
init(_ scanResult: Binding<OSBARCScanResult>, _ scanThroughButton: Bool, _ scanButtonEnabled: Bool = false, andHint hint: OSBARCScannerHint? = nil) {
/// - hints: The list of hints, to restrict scanning to a specific set of formats (e.g. only qr code and ean-13). An empty list means it can scan all.
init(_ scanResult: Binding<OSBARCScanResult>, _ scanThroughButton: Bool, _ scanButtonEnabled: Bool = false, andHints hints: [OSBARCScannerHint]) {
self._scanResult = scanResult
self.scanThroughButton = scanThroughButton
self.scanButtonEnabled = scanButtonEnabled
self.hint = hint
self.hints = hints
super.init()

NotificationCenter.default
Expand Down Expand Up @@ -74,7 +78,7 @@ final class OSBARCCaptureOutputDecoder: NSObject, AVCaptureVideoDataOutputSample
guard error == nil else { return }
self.processClassification(for: request)
})
barcodeRequest.symbologies = (self.hint ?? .unknown).toVNBarcodeSymbologies()
barcodeRequest.symbologies = OSBARCScannerHint.toVNBarcodeSymbologies(from: self.hints)

return barcodeRequest
}()
Expand All @@ -88,7 +92,7 @@ private extension OSBARCCaptureOutputDecoder {
DispatchQueue.main.async {
if let bestResult = request.results?.first as? VNBarcodeObservation, bestResult.confidence > 0.9, let payload = bestResult.payloadStringValue {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
let format = OSBARCScannerHint.fromVNBarcodeSymbology(bestResult.symbology, withHint: self.hint)
let format = OSBARCScannerHint.fromVNBarcodeSymbology(bestResult.symbology, withHints: self.hints)
self.scanResult = OSBARCScanResult(text: payload, format: format)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,29 @@ extension OSBARCScannerHint {
}
}

static func toVNBarcodeSymbologies(from hints: [OSBARCScannerHint]) -> [VNBarcodeSymbology] {
guard !hints.isEmpty else { return OSBARCScannerHint.unknown.toVNBarcodeSymbologies() }
var seen = Set<VNBarcodeSymbology>()
var result: [VNBarcodeSymbology] = []
for hint in hints {
for symbology in hint.toVNBarcodeSymbologies() where !seen.contains(symbology) {
seen.insert(symbology)
result.append(symbology)
}
}
return result
}

static func fromVNBarcodeSymbology(_ symbology: VNBarcodeSymbology, withHint hint: OSBARCScannerHint? = nil) -> OSBARCScannerHint {
return fromVNBarcodeSymbology(symbology, withHints: hint.map { [$0] } ?? [])
}

static func fromVNBarcodeSymbology(_ symbology: VNBarcodeSymbology, withHints hints: [OSBARCScannerHint]) -> OSBARCScannerHint {
if (symbology == .ean13) {
// UPC-A and EAN-13 have similar format, and Apple Vision does not distinguish between the two
// if a specific hint was provided, return that as the format
// when both are allowed by `hints` the returned format is ambiguous; we default to .ean13 to match legacy single-hint behavior
let hint: OSBARCScannerHint? = hints.contains(.ean13) ? .ean13 : (hints.contains(.upcA) ? .upcA : nil)
switch hint {
case .upcA: return .upcA
case .ean13: return .ean13
Expand Down
6 changes: 5 additions & 1 deletion Sources/OSBarcodeLib/Scanner/OSBARCScannerBehaviour.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@ final class OSBARCScannerBehaviour: OSBARCCoordinatable, OSBARCScannerProtocol {
let buttonText = parameters.scanButtonText ?? "" // not having the button enabled is translated into having an empty text.
let shouldShowButton = !buttonText.isEmpty // if empty text is passed, the button is not enabled on the scanner view

let rawHints = parameters.hints?.isEmpty == false ? parameters.hints! : [parameters.hint].compactMap { $0 }
// `.unknown` is the JS-side ALL sentinel; its presence forces scan-all (empty list).
let hints = rawHints.contains(.unknown) ? [] : rawHints

let barcodeDecoder = OSBARCCaptureOutputDecoder(
scanResultBinding,
shouldShowButton,
andHint: parameters.hint
andHints: hints
)
let captureSessionManager = OSBARCCaptureSessionManager(
parameters.cameraDirection,
Expand Down