11import Foundation
2+ import UIKit
23
34/// Parses HID (Human Interface Device) keyboard input into barcode scans.
45/// This class handles the core logic for interpreting keyboard input as barcode data,
@@ -9,27 +10,137 @@ final class HIDBarcodeParser {
910 /// Callback that is triggered when a barcode is successfully scanned
1011 let onScan : ( String ) -> Void
1112
13+ private let timeProvider : TimeProvider
14+
1215 private var buffer = " "
16+ private var lastKeyPressTime : Date ?
1317
14- init ( configuration: HIDBarcodeParserConfiguration , onScan: @escaping ( String ) -> Void ) {
18+ init ( configuration: HIDBarcodeParserConfiguration ,
19+ onScan: @escaping ( String ) -> Void ,
20+ timeProvider: TimeProvider = DefaultTimeProvider ( ) ) {
1521 self . configuration = configuration
1622 self . onScan = onScan
23+ self . timeProvider = timeProvider
1724 }
1825
1926 /// Process a key press event
2027 /// - Parameter key: The key that was pressed
21- func processKeyPress( _ key: String ) {
22- if configuration. terminatingStrings. contains ( key) {
23- onScan ( buffer)
24- buffer = " "
28+ func processKeyPress( _ key: UIKey ) {
29+ let currentTime = timeProvider. now ( )
30+
31+ // If characters are entered too slowly, it's probably typing and we should ignore it
32+ if let lastTime = lastKeyPressTime,
33+ currentTime. timeIntervalSince ( lastTime) > configuration. maximumInterCharacterTime {
34+ resetScan ( )
35+ }
36+
37+ lastKeyPressTime = currentTime
38+
39+ let character = key. characters
40+ if configuration. terminatingStrings. contains ( character) {
41+ processScan ( )
2542 } else {
26- buffer. append ( key)
43+ guard !excludedKeys. contains ( key. keyCode) else { return }
44+ buffer. append ( character)
2745 }
2846 }
2947
48+ private let excludedKeys : [ UIKeyboardHIDUsage ] = [
49+ . keyboardCapsLock,
50+ . keyboardF1,
51+ . keyboardF2,
52+ . keyboardF3,
53+ . keyboardF4,
54+ . keyboardF5,
55+ . keyboardF6,
56+ . keyboardF7,
57+ . keyboardF8,
58+ . keyboardF9,
59+ . keyboardF10,
60+ . keyboardF11,
61+ . keyboardF12,
62+ . keyboardPrintScreen,
63+ . keyboardScrollLock,
64+ . keyboardPause,
65+ . keyboardInsert,
66+ . keyboardHome,
67+ . keyboardPageUp,
68+ . keyboardDeleteForward,
69+ . keyboardEnd,
70+ . keyboardPageDown,
71+ . keyboardRightArrow,
72+ . keyboardLeftArrow,
73+ . keyboardDownArrow,
74+ . keyboardUpArrow,
75+ . keypadNumLock,
76+ . keyboardApplication,
77+ . keyboardPower,
78+ . keyboardF13,
79+ . keyboardF14,
80+ . keyboardF15,
81+ . keyboardF16,
82+ . keyboardF17,
83+ . keyboardF18,
84+ . keyboardF19,
85+ . keyboardF20,
86+ . keyboardF21,
87+ . keyboardF22,
88+ . keyboardF23,
89+ . keyboardF24,
90+ . keyboardExecute,
91+ . keyboardHelp,
92+ . keyboardMenu,
93+ . keyboardSelect,
94+ . keyboardStop,
95+ . keyboardAgain,
96+ . keyboardUndo,
97+ . keyboardCut,
98+ . keyboardCopy,
99+ . keyboardPaste,
100+ . keyboardFind,
101+ . keyboardMute,
102+ . keyboardVolumeUp,
103+ . keyboardVolumeDown,
104+ . keyboardLockingCapsLock,
105+ . keyboardLockingNumLock,
106+ . keyboardLockingScrollLock,
107+ . keyboardAlternateErase,
108+ . keyboardSysReqOrAttention,
109+ . keyboardCancel,
110+ . keyboardClear,
111+ . keyboardPrior,
112+ . keyboardSeparator,
113+ . keyboardOut,
114+ . keyboardOper,
115+ . keyboardClearOrAgain,
116+ . keyboardCrSelOrProps,
117+ . keyboardExSel,
118+ . keyboardLeftControl,
119+ . keyboardLeftShift,
120+ . keyboardLeftAlt,
121+ . keyboardLeftGUI,
122+ . keyboardRightControl,
123+ . keyboardRightShift,
124+ . keyboardRightAlt,
125+ . keyboardRightGUI,
126+ . keyboard_Reserved
127+ ]
128+
30129 /// Cancel the current scan and clear the buffer
31130 func cancel( ) {
131+ resetScan ( )
132+ }
133+
134+ private func resetScan( ) {
32135 buffer = " "
136+ lastKeyPressTime = nil
137+ }
138+
139+ private func processScan( ) {
140+ if buffer. count >= configuration. minimumBarcodeLength {
141+ onScan ( buffer)
142+ }
143+ resetScan ( )
33144 }
34145}
35146
@@ -38,8 +149,17 @@ struct HIDBarcodeParserConfiguration {
38149 /// Strings that indicate the end of a barcode scan
39150 let terminatingStrings : Set < String >
40151
152+ /// Minimum length to consider scanned input complete
153+ let minimumBarcodeLength : Int
154+
155+ /// Maximum time between scanned keystrokes
156+ /// After this time elapses, any further keystrokes result in the scan being rejected
157+ let maximumInterCharacterTime : TimeInterval
158+
41159 /// Default configuration suitable for most barcode scanners
42160 static let `default` = HIDBarcodeParserConfiguration (
43- terminatingStrings: [ " \r " , " \n " ]
161+ terminatingStrings: [ " \r " , " \n " ] ,
162+ minimumBarcodeLength: 4 ,
163+ maximumInterCharacterTime: 0.2
44164 )
45165}
0 commit comments