@nitra/zebra 7.1.0 → 7.1.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.
|
@@ -61,17 +61,17 @@ import ExternalAccessory
|
|
|
61
61
|
public var errorDescription: String? {
|
|
62
62
|
switch self {
|
|
63
63
|
case .addressMissing:
|
|
64
|
-
return "
|
|
64
|
+
return "Printer address is not specified. Pass address to print()."
|
|
65
65
|
case .zplEmpty:
|
|
66
|
-
return "ZPL
|
|
66
|
+
return "ZPL command is empty."
|
|
67
67
|
case .accessoryNotFound(let address):
|
|
68
|
-
return "
|
|
68
|
+
return "Device not found or not connected: \(address)"
|
|
69
69
|
case .protocolMissing(let address):
|
|
70
|
-
return "
|
|
70
|
+
return "No supported protocol found for device: \(address)"
|
|
71
71
|
case .sessionFailed(let reason):
|
|
72
|
-
return "
|
|
72
|
+
return "Failed to open session with printer: \(reason)"
|
|
73
73
|
case .writeFailed(let reason):
|
|
74
|
-
return "
|
|
74
|
+
return "Failed to send data: \(reason)"
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -99,10 +99,17 @@ import ExternalAccessory
|
|
|
99
99
|
super.init()
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
/// Запускає
|
|
102
|
+
/// Запускає стріми — обидва (input і output) відкриваються й додаються в RunLoop, як у документації Apple; інакше сесія може не звільнятися коректно.
|
|
103
103
|
func start() {
|
|
104
|
+
let runLoop = RunLoop.current
|
|
105
|
+
let mode: RunLoop.Mode = .default
|
|
106
|
+
if let input = session.inputStream {
|
|
107
|
+
input.delegate = self
|
|
108
|
+
input.schedule(in: runLoop, forMode: mode)
|
|
109
|
+
input.open()
|
|
110
|
+
}
|
|
104
111
|
output.delegate = self
|
|
105
|
-
output.schedule(in:
|
|
112
|
+
output.schedule(in: runLoop, forMode: mode)
|
|
106
113
|
output.open()
|
|
107
114
|
owner?.log("StreamWriter started, bytes=", data.count)
|
|
108
115
|
startTime = Date()
|
|
@@ -119,14 +126,29 @@ import ExternalAccessory
|
|
|
119
126
|
close(with: .success(PrintResult(address: address)))
|
|
120
127
|
}
|
|
121
128
|
|
|
122
|
-
/// Закриває стріми та викликає completion один
|
|
129
|
+
/// Закриває стріми та викликає completion один раз.
|
|
130
|
+
/// Порядок важливий для коректного звільнення EASession у iOS — інакше повторне відкриття сесії до того ж аксесуара може повертати nil.
|
|
123
131
|
func close(with result: Result<PrintResult, ZebraError>) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
let runLoop = RunLoop.current
|
|
133
|
+
let mode: RunLoop.Mode = .default
|
|
134
|
+
// Даємо RunLoop обробити останні події перед закриттям
|
|
135
|
+
runLoop.run(mode: mode, before: Date().addingTimeInterval(0.02))
|
|
136
|
+
|
|
137
|
+
// 1) Вимикаємо делегата, щоб під час закриття не викликалися зворотні виклики
|
|
128
138
|
output.delegate = nil
|
|
129
|
-
|
|
139
|
+
// 2) Знімаємо output з RunLoop, потім закриваємо
|
|
140
|
+
output.remove(from: runLoop, forMode: mode)
|
|
141
|
+
output.close()
|
|
142
|
+
|
|
143
|
+
// 3) InputStream теж треба коректно закрити (зняти з RunLoop і делегата), інакше сесія не звільняється
|
|
144
|
+
if let input = session.inputStream {
|
|
145
|
+
input.delegate = nil
|
|
146
|
+
input.remove(from: runLoop, forMode: mode)
|
|
147
|
+
input.close()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 4) Затримка, щоб iOS встиг звільнити EASession до наступного відкриття
|
|
151
|
+
Thread.sleep(forTimeInterval: 0.25)
|
|
130
152
|
if let completion = completion {
|
|
131
153
|
completion(result)
|
|
132
154
|
}
|
|
@@ -135,6 +157,7 @@ import ExternalAccessory
|
|
|
135
157
|
}
|
|
136
158
|
|
|
137
159
|
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
|
|
160
|
+
// Події input-потоку ігноруємо (потрібен лише для коректного життєвого циклу сесії)
|
|
138
161
|
guard aStream === output else { return }
|
|
139
162
|
if Date().timeIntervalSince(startTime) > timeout {
|
|
140
163
|
owner?.log("StreamWriter timeout")
|
|
@@ -239,36 +262,27 @@ import ExternalAccessory
|
|
|
239
262
|
}
|
|
240
263
|
|
|
241
264
|
/// Друкує ZPL на принтері за вказаною адресою (без зчитування з налаштувань).
|
|
265
|
+
/// Відкриття/закриття EASession виконується на main run loop для коректного повторного друку.
|
|
242
266
|
public func printZpl(address: String, zpl: String, completion: @escaping (Result<PrintResult, ZebraError>) -> Void) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
payload += "\r\n"
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Debug: log exact ZPL content and hex bytes
|
|
263
|
-
let visibleZpl = payload.replacingOccurrences(of: "\r", with: "<CR>").replacingOccurrences(of: "\n", with: "<LF>\n")
|
|
264
|
-
self.log("ZPL payload (visible):\n\(visibleZpl)")
|
|
265
|
-
let zplBytes = Array(payload.utf8)
|
|
266
|
-
let hex = zplBytes.map { String(format: "%02X", $0) }.joined()
|
|
267
|
-
self.log("ZPL payload (hex):", hex)
|
|
268
|
-
|
|
269
|
-
self.log("printZpl called with address=\(trimmedAddress.isEmpty ? "<empty>" : trimmedAddress)", "zplLength=\(payload.utf8.count)")
|
|
267
|
+
let trimmedAddress = address.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
268
|
+
if trimmedAddress.isEmpty {
|
|
269
|
+
completion(.failure(.addressMissing))
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
let trimmedZpl = zpl.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
273
|
+
if trimmedZpl.isEmpty {
|
|
274
|
+
completion(.failure(.zplEmpty))
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
var payload = trimmedZpl
|
|
278
|
+
if !payload.hasSuffix("\r\n") {
|
|
279
|
+
payload += "\r\n"
|
|
280
|
+
}
|
|
281
|
+
let zplBytes = Array(payload.utf8)
|
|
282
|
+
log("printZpl called with address=\(trimmedAddress)", "zplLength=\(zplBytes.count)")
|
|
270
283
|
|
|
271
|
-
|
|
284
|
+
DispatchQueue.main.async { [weak self] in
|
|
285
|
+
guard let self = self else { return }
|
|
272
286
|
let accessories = EAAccessoryManager.shared().connectedAccessories
|
|
273
287
|
self.log("EA connectedAccessories count=", accessories.count)
|
|
274
288
|
|