@nitra/zebra 8.2.0 → 8.2.1

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.
@@ -131,6 +131,8 @@ public class Zebra {
131
131
  out.write(data, offset, len);
132
132
  }
133
133
  out.flush();
134
+ // Затримка перед закриттям: дає Bluetooth-стеку та принтеру час відправити/прийняти дані (інакше перший друк іноді не виконується, лише другий).
135
+ Thread.sleep(400);
134
136
 
135
137
  Logger.info("Zebra", "ZPL успішно відправлено: " + data.length + " байт");
136
138
  callback.onSuccess(addrTrimmed, data.length);
@@ -86,7 +86,8 @@ import ExternalAccessory
86
86
  }
87
87
  }
88
88
 
89
- /// Відповідає за запис байтів у OutputStream з таймаутом та звітує через completion
89
+ /// Відповідає за запис байтів у OutputStream з таймаутом та звітує через completion.
90
+ /// Закриття потоків і сесії гарантовано виконується один раз (ідемпотентний close), у т.ч. по таймеру при відсутності подій.
90
91
  private class StreamWriter: NSObject, StreamDelegate {
91
92
  private let output: OutputStream
92
93
  private let data: [UInt8]
@@ -96,6 +97,9 @@ import ExternalAccessory
96
97
  private var completion: ((Result<PrintResult, ZebraError>) -> Void)?
97
98
  private var startTime = Date()
98
99
  private let session: EASession
100
+ private var didClose = false
101
+ private let closeLock = NSLock()
102
+ private var timeoutTimer: Timer?
99
103
 
100
104
  init(owner _: Zebra, session: EASession, output: OutputStream, data: [UInt8], address: String, timeout: TimeInterval = 5.0, completion: @escaping (Result<PrintResult, ZebraError>) -> Void) {
101
105
  self.session = session
@@ -108,6 +112,7 @@ import ExternalAccessory
108
112
  }
109
113
 
110
114
  /// Запускає стріми — обидва (input і output) відкриваються й додаються в RunLoop, як у документації Apple; інакше сесія може не звільнятися коректно.
115
+ /// Таймер гарантує закриття при відсутності подій потоку (наприклад, зависання принтера).
111
116
  func start() {
112
117
  let runLoop = RunLoop.current
113
118
  let mode: RunLoop.Mode = .default
@@ -121,11 +126,15 @@ import ExternalAccessory
121
126
  output.open()
122
127
  logDebug("StreamWriter started, bytes= \(self.data.count)")
123
128
  startTime = Date()
129
+ timeoutTimer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { [weak self] _ in
130
+ logDebug("StreamWriter timeout (no events)")
131
+ self?.close(with: .failure(.writeFailed("Таймаут запису")))
132
+ }
124
133
  }
125
134
 
126
- /// Після запису всіх байтів дає час на флаш буфера (iOS може не встигнути відправити на пристрій при миттєвому закритті, особливо при запуску без Xcode).
135
+ /// Після запису всіх байтів дає час на флаш буфера (iOS може не встигнути відправити на пристрій при миттєвому закритті; при першому друці принтер ще "прокидається" — потрібно більше часу).
127
136
  private func flushThenClose() {
128
- let flushDuration: TimeInterval = 0.75
137
+ let flushDuration: TimeInterval = 1.5
129
138
  let deadline = Date().addingTimeInterval(flushDuration)
130
139
  while Date() < deadline {
131
140
  RunLoop.current.run(mode: .default, before: Date().addingTimeInterval(0.1))
@@ -134,9 +143,20 @@ import ExternalAccessory
134
143
  close(with: .success(PrintResult(address: address)))
135
144
  }
136
145
 
137
- /// Закриває стріми та викликає completion один раз.
146
+ /// Закриває стріми та викликає completion один раз (ідемпотентно).
138
147
  /// Порядок важливий для коректного звільнення EASession у iOS — інакше повторне відкриття сесії до того ж аксесуара може повертати nil.
139
148
  func close(with result: Result<PrintResult, ZebraError>) {
149
+ closeLock.lock()
150
+ guard !didClose else {
151
+ closeLock.unlock()
152
+ return
153
+ }
154
+ didClose = true
155
+ closeLock.unlock()
156
+
157
+ timeoutTimer?.invalidate()
158
+ timeoutTimer = nil
159
+
140
160
  let runLoop = RunLoop.current
141
161
  let mode: RunLoop.Mode = .default
142
162
  // Даємо RunLoop обробити останні події перед закриттям
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/zebra",
3
- "version": "8.2.0",
3
+ "version": "8.2.1",
4
4
  "description": "Zebra printer",
5
5
  "keywords": [
6
6
  "capacitor",