react-native-bluetooth-escpos-printer-fork 0.0.17 → 0.0.19

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.
package/README.md CHANGED
@@ -28,6 +28,21 @@ Link the plugin to your RN project
28
28
  react-native link react-native-bluetooth-escpos-printer
29
29
  ```
30
30
 
31
+ ### Step3 (iOS only) ###
32
+ Add Bluetooth permissions to your `Info.plist`:
33
+
34
+ ```xml
35
+ <key>NSBluetoothAlwaysUsageDescription</key>
36
+ <string>This app needs Bluetooth to discover and connect to printers</string>
37
+ <key>NSBluetoothPeripheralUsageDescription</key>
38
+ <string>This app needs Bluetooth to connect to printer peripherals</string>
39
+ ```
40
+
41
+ **Note:**
42
+ - For iOS 13+, `NSBluetoothAlwaysUsageDescription` is required.
43
+ - For iOS 12 and below, `NSBluetoothPeripheralUsageDescription` is required.
44
+ - It's recommended to include both for maximum compatibility.
45
+
31
46
  ### Manual linking (Android) ###
32
47
  Ensure your build files match the following requirements:
33
48
 
@@ -73,8 +88,12 @@ async function, checks whether Bluetooth service is enabled.
73
88
  });
74
89
  ```
75
90
 
76
- * enableBluetooth ==> ``` diff + ANDROID ONLY ```
77
- async function, enables the bluetooth service, returns the devices information already bound and paired. ``` diff - IOS would just resovle with nil ```
91
+ * enableBluetooth ==>
92
+ async function, enables the bluetooth service, returns the devices information already bound and paired.
93
+
94
+ **Platform differences:**
95
+ - **Android:** If Bluetooth is off, shows system dialog to enable it. Returns array of paired devices after enabling (or immediately if already enabled).
96
+ - **iOS:** Cannot programmatically enable Bluetooth (must be done in Settings by user). Returns empty array `[]` since iOS Core Bluetooth doesn't have a "paired devices" concept. Use `scanDevices()` to discover available devices.
78
97
 
79
98
  ```javascript
80
99
  BluetoothManager.enableBluetooth().then((r)=>{
@@ -94,8 +113,12 @@ BluetoothManager.enableBluetooth().then((r)=>{
94
113
  });
95
114
  ```
96
115
 
97
- * disableBluetooth ==> ``` diff + ANDROID ONLY ```
98
- async function ,disables the bluetooth service. ``` diff - IOS would just resovle with nil ```
116
+ * disableBluetooth ==>
117
+ async function, disables the bluetooth service.
118
+
119
+ **Platform differences:**
120
+ - **Android:** Programmatically disables Bluetooth and stops any active connections.
121
+ - **iOS:** Cannot programmatically disable Bluetooth (must be done in Settings or Control Center by user). This method resolves successfully but does nothing on iOS.
99
122
 
100
123
  ```javascript
101
124
  BluetoothManager.disableBluetooth().then(()=>{
@@ -139,6 +162,36 @@ BluetoothManager.scanDevices()
139
162
  });
140
163
  ```
141
164
 
165
+ **Device Information Structure:**
166
+
167
+ Each device object returned contains:
168
+ - `name` (string): Device name
169
+ - `address` (string): Device MAC address (Android) or UUID (iOS)
170
+ - `deviceClass` (number): Bluetooth device class
171
+ - `majorDeviceClass` (number): Major device class category
172
+ - `rssi` (number, iOS only): Signal strength indicator
173
+
174
+ **Filtering for Printers:**
175
+
176
+ To identify printer devices, check the `majorDeviceClass`:
177
+ ```javascript
178
+ const MAJOR_CLASS_IMAGING = 1536; // 0x0600 - Printers, scanners, cameras
179
+ const DEVICE_CLASS_PRINTER = 1664; // 0x0680 - Specifically printers
180
+
181
+ // Filter found devices to show only printers
182
+ const printers = ss.found.filter(device =>
183
+ device.majorDeviceClass === MAJOR_CLASS_IMAGING ||
184
+ device.deviceClass === DEVICE_CLASS_PRINTER
185
+ );
186
+ ```
187
+
188
+ **Platform differences:**
189
+ - **Android:** Device classes are provided by the OS based on the device's Bluetooth profile
190
+ - **iOS:** Device classes are inferred from:
191
+ 1. Advertised service UUIDs (SPP, ESC/POS services)
192
+ 2. Device name patterns (contains "print", "pos", "thermal", "receipt", etc.)
193
+ 3. If no printer indicators are found, device is marked as "unknown" (class 7936)
194
+
142
195
  * connect ==>
143
196
  async function, connects the specified device, if not bound, bound dailog prompts.
144
197
 
@@ -162,8 +215,10 @@ async function, connects the specified device, if not bound, bound dailog prompt
162
215
  * unpair ==>
163
216
  async function, disconnects and unpairs the specified devices
164
217
 
218
+ **Note for iOS:** Due to iOS security restrictions, this method can only disconnect the device but cannot programmatically unpair it. The device will remain paired at the iOS system level. Users must unpair manually from iOS Settings > Bluetooth if needed. On Android, full unpair functionality is supported.
219
+
165
220
  ```javascript
166
- BluetoothManager.connect(rowData.address)
221
+ BluetoothManager.unpaire(rowData.address)
167
222
  .then((s)=>{
168
223
  //success here
169
224
  },
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <classpath>
3
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
4
+ <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
5
+ <classpathentry kind="output" path="bin/default"/>
6
+ </classpath>
File without changes
@@ -0,0 +1,2 @@
1
+ #Sat Jan 31 21:27:45 CET 2026
2
+ gradle.version=6.1.1
File without changes
package/android/.project CHANGED
@@ -5,6 +5,11 @@
5
5
  <projects>
6
6
  </projects>
7
7
  <buildSpec>
8
+ <buildCommand>
9
+ <name>org.eclipse.jdt.core.javabuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
8
13
  <buildCommand>
9
14
  <name>org.eclipse.buildship.core.gradleprojectbuilder</name>
10
15
  <arguments>
@@ -12,6 +17,18 @@
12
17
  </buildCommand>
13
18
  </buildSpec>
14
19
  <natures>
20
+ <nature>org.eclipse.jdt.core.javanature</nature>
15
21
  <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
16
22
  </natures>
23
+ <filteredResources>
24
+ <filter>
25
+ <id>1769891269456</id>
26
+ <name></name>
27
+ <type>30</type>
28
+ <matcher>
29
+ <id>org.eclipse.core.resources.regexFilterMatcher</id>
30
+ <arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
31
+ </matcher>
32
+ </filter>
33
+ </filteredResources>
17
34
  </projectDescription>
@@ -1,2 +1,13 @@
1
+ arguments=--init-script /Users/lukaszprivate/.local/share/opencode/bin/jdtls/config_mac/org.eclipse.osgi/59/0/.cp/gradle/init/init.gradle
2
+ auto.sync=false
3
+ build.scans.enabled=false
4
+ connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
1
5
  connection.project.dir=
2
6
  eclipse.preferences.version=1
7
+ gradle.user.home=
8
+ java.home=/Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home
9
+ jvm.arguments=
10
+ offline.mode=false
11
+ override.workspace.settings=true
12
+ show.console.view=true
13
+ show.executions.view=true
@@ -0,0 +1,4 @@
1
+ eclipse.preferences.version=1
2
+ org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
3
+ org.eclipse.jdt.core.compiler.compliance=11
4
+ org.eclipse.jdt.core.compiler.source=11
package/ios/ImageUtils.m CHANGED
@@ -190,12 +190,24 @@ int p6[] = { 0, 0x02 };
190
190
  **/
191
191
  + (NSData *)eachLinePixToCmd:(unsigned char *)src nWidth:(NSInteger) nWidth nHeight:(NSInteger) nHeight nMode:(NSInteger) nMode
192
192
  {
193
- NSLog(@"SIZE OF SRC: %lu",sizeof(&src));
194
193
  NSInteger nBytesPerLine = (int)nWidth/8;
195
194
  unsigned char * data = malloc(nHeight*(8+nBytesPerLine));
196
- // const char* srcData = (const char*)[src bytes];
197
195
  NSInteger k = 0;
198
- // NSMutableString *toLog = [[NSMutableString alloc] init];
196
+
197
+ // Log first few lines to see actual pixel distribution
198
+ int totalBlackPixelsInFirstLines = 0;
199
+ for(int line = 0; line < MIN(10, nHeight); line++) {
200
+ int blackInLine = 0;
201
+ for(int x = 0; x < nWidth; x++) {
202
+ if(src[line * nWidth + x] == 1) blackInLine++;
203
+ }
204
+ if(blackInLine > 0) {
205
+ NSLog(@"Line %d: %d/%d pixels are black", line, blackInLine, (int)nWidth);
206
+ }
207
+ totalBlackPixelsInFirstLines += blackInLine;
208
+ }
209
+ NSLog(@"First 10 lines: %d total black pixels", totalBlackPixelsInFirstLines);
210
+
199
211
  for(int i=0;i<nHeight;i++){
200
212
  NSInteger var10 = i*(8+nBytesPerLine);
201
213
  //GS v 0 m xL xH yL yH d1....dk 打印光栅位图
@@ -207,19 +219,31 @@ int p6[] = { 0, 0x02 };
207
219
  data[var10 + 5] = (unsigned char)(nBytesPerLine / 256);//xH
208
220
  data[var10 + 6] = 1;//yL
209
221
  data[var10 + 7] = 0;//yH
210
- // for(int l=0;l<8;l++){
211
- // NSInteger d =data[var10 + l];
212
- // [toLog appendFormat:@"%ld,",(long)d];
213
- // }
214
222
 
215
223
  for (int j = 0; j < nBytesPerLine; ++j) {
216
224
  data[var10 + 8 + j] = (int) (p0[src[k]] + p1[src[k + 1]] + p2[src[k + 2]] + p3[src[k + 3]] + p4[src[k + 4]] + p5[src[k + 5]] + p6[src[k + 6]] + src[k + 7]);
217
225
  k =k+8;
218
- // [toLog appendFormat:@"%ld,",(long)data[var10+8+j]];
219
226
  }
220
- // [toLog appendString:@"\n\r"];
221
227
  }
222
- // NSLog(@"line datas: %@",toLog);
228
+
229
+ // Find and log first line with actual black pixels
230
+ for(int i = 0; i < nHeight; i++) {
231
+ NSInteger var10 = i*(8+nBytesPerLine);
232
+ BOOL hasBlack = NO;
233
+ for(int j = 0; j < MIN(20, nBytesPerLine); j++) {
234
+ if(data[var10 + 8 + j] != 0) {
235
+ hasBlack = YES;
236
+ break;
237
+ }
238
+ }
239
+ if(hasBlack) {
240
+ NSLog(@"First line with black pixels is line %d, first 10 bytes: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
241
+ i, data[var10+8], data[var10+9], data[var10+10], data[var10+11], data[var10+12],
242
+ data[var10+13], data[var10+14], data[var10+15], data[var10+16], data[var10+17]);
243
+ break;
244
+ }
245
+ }
246
+
223
247
  return [NSData dataWithBytes:data length:nHeight*(8+nBytesPerLine)];
224
248
  }
225
249
 
@@ -242,24 +266,25 @@ int p6[] = { 0, 0x02 };
242
266
  }
243
267
 
244
268
  int grayave = graytotal / ysize / xsize;
269
+ NSLog(@"Threshold: average gray = %d (pixels lighter than this become white)", grayave);
270
+
245
271
  k = 0;
246
- // NSMutableString *logStr = [[NSMutableString alloc]init];
247
- // int oneCount = 0;
272
+ int oneCount = 0;
248
273
  for(i = 0; i < ysize; ++i) {
249
274
  for(j = 0; j < xsize; ++j) {
250
275
  gray = orgpixels[k] & 255;
251
276
  if(gray > grayave) {
252
- despixels[k] = 0;
277
+ despixels[k] = 0; // White (don't print)
253
278
  } else {
254
- despixels[k] = 1;
255
- // oneCount++;
279
+ despixels[k] = 1; // Black (print)
280
+ oneCount++;
256
281
  }
257
282
 
258
283
  ++k;
259
- // [logStr appendFormat:@"%d,",despixels[k]];
260
284
  }
261
285
  }
262
- // NSLog(@"despixels [with 1 count:%d]: %@",oneCount,logStr);
286
+ NSLog(@"Thresholding complete: %d/%d pixels are black (%.1f%%)",
287
+ oneCount, (int)(xsize*ysize), (oneCount * 100.0 / (xsize*ysize)));
263
288
  return despixels;
264
289
  }
265
290
  +(NSData *)pixToTscCmd:(uint8_t *)src width:(NSInteger) width
@@ -12,7 +12,7 @@
12
12
 
13
13
 
14
14
  - (void) didWriteDataToBle: (BOOL)success
15
- {NSLog(@"PrintImageBleWriteDelete diWriteDataToBle: %d",success?1:0);
15
+ {
16
16
  if(success){
17
17
  if(_now == -1){
18
18
  if(_pendingResolve) {_pendingResolve(nil); _pendingResolve=nil;}
@@ -42,20 +42,20 @@
42
42
  -(void) print
43
43
  {
44
44
  @synchronized (self) {
45
- NSInteger sizePerLine = (int)(_width/8);
46
- // do{
47
- // if(sizePerLine+_now>=[_toPrint length]){
48
- // sizePerLine = [_toPrint length] - _now;
49
- // }
50
- // if(sizePerLine>0){
51
- NSData *subData = [_toPrint subdataWithRange:NSMakeRange(_now, sizePerLine)];
52
- NSLog(@"Write data:%@",subData);
53
- [RNBluetoothManager writeValue:subData withDelegate:self];
54
- //}
55
- _now = _now+sizePerLine;
56
- [NSThread sleepForTimeInterval:0.01f];
45
+ // Each line contains: 8 bytes (ESC/POS raster command header) + (width/8) bytes (pixel data)
46
+ NSInteger sizePerLine = 8 + (int)(_width/8);
47
+ NSData *subData = [_toPrint subdataWithRange:NSMakeRange(_now, sizePerLine)];
57
48
 
49
+ // Log first line to debug
50
+ if(_now == 0) {
51
+ NSUInteger logLen = MIN(20, [subData length]);
52
+ NSData *logData = [subData subdataWithRange:NSMakeRange(0, logLen)];
53
+ NSLog(@"First image line (%lu bytes): %@", (unsigned long)[subData length], logData);
54
+ }
55
+
56
+ [RNBluetoothManager writeValue:subData withDelegate:self];
57
+ _now = _now+sizePerLine;
58
+ [NSThread sleepForTimeInterval:0.05f]; // 50ms delay between lines to avoid overwhelming printer buffer
58
59
  }
59
- //}while(_now<[_toPrint length]);
60
60
  }
61
61
  @end
@@ -488,7 +488,8 @@ RCT_EXPORT_METHOD(printPic:(NSString *) base64encodeStr withOptions:(NSDictionar
488
488
  //mBitmap.getHeight() * width / mBitmap.getWidth();
489
489
  NSInteger imgHeight = jpgImage.size.height;
490
490
  NSInteger imagWidth = jpgImage.size.width;
491
- NSInteger width = nWidth;//((int)(((nWidth*0.86)+7)/8))*8-7;
491
+ // Round width to multiple of 8 (required for raster image format)
492
+ NSInteger width = ((nWidth + 7) / 8) * 8;
492
493
  CGSize size = CGSizeMake(width, imgHeight*width/imagWidth);
493
494
  UIImage *scaled = [ImageUtils imageWithImage:jpgImage scaledToFillSize:size];
494
495
  if(paddingLeft>0){
@@ -497,8 +498,16 @@ RCT_EXPORT_METHOD(printPic:(NSString *) base64encodeStr withOptions:(NSDictionar
497
498
  }
498
499
 
499
500
  unsigned char * graImage = [ImageUtils imageToGreyImage:scaled];
501
+ NSLog(@"Grayscale conversion complete");
502
+
500
503
  unsigned char * formatedData = [ImageUtils format_K_threshold:graImage width:size.width height:size.height];
504
+ NSLog(@"Thresholding complete, generating ESC/POS commands");
505
+
501
506
  NSData *dataToPrint = [ImageUtils eachLinePixToCmd:formatedData nWidth:size.width nHeight:size.height nMode:0];
507
+ NSLog(@"Image print: %lux%lu pixels, %lu bytes total, %lu bytes per line",
508
+ (unsigned long)size.width, (unsigned long)size.height,
509
+ (unsigned long)[dataToPrint length], (unsigned long)(8 + size.width/8));
510
+
502
511
  PrintImageBleWriteDelegate *delegate = [[PrintImageBleWriteDelegate alloc] init];
503
512
  delegate.pendingResolve = resolve;
504
513
  delegate.pendingReject = reject;
@@ -543,6 +552,9 @@ RCT_EXPORT_METHOD(printQRCode:(NSString *)content
543
552
  uint8_t * graImage = [ImageUtils imageToGreyImage:[UIImage imageWithCGImage:image]];
544
553
  unsigned char * formatedData = [ImageUtils format_K_threshold:graImage width:size height:size];
545
554
  NSData *dataToPrint = [ImageUtils eachLinePixToCmd:formatedData nWidth:size nHeight:size nMode:0];
555
+ NSLog(@"QR code print: %lux%lu pixels, %lu bytes total, %lu bytes per line",
556
+ (unsigned long)size, (unsigned long)size,
557
+ (unsigned long)[dataToPrint length], (unsigned long)(8 + size/8));
546
558
  PrintImageBleWriteDelegate *delegate = [[PrintImageBleWriteDelegate alloc] init];
547
559
  delegate.pendingResolve=resolve;
548
560
  delegate.pendingReject = reject;
@@ -560,6 +572,7 @@ RCT_EXPORT_METHOD(printEscPosCommands:(NSString *)base64encodeStr
560
572
  if(RNBluetoothManager.isConnected){
561
573
  @try{
562
574
  NSData *decoded = [[NSData alloc] initWithBase64EncodedString:base64encodeStr options:0];
575
+
563
576
  pendingResolve = resolve;
564
577
  pendingReject = reject;
565
578
  [RNBluetoothManager writeValue:decoded withDelegate:self];
@@ -19,6 +19,7 @@
19
19
  @property (nonatomic,copy) RCTPromiseResolveBlock scanResolveBlock;
20
20
  @property (nonatomic,copy) RCTPromiseRejectBlock scanRejectBlock;
21
21
  @property (strong,nonatomic) NSMutableDictionary <NSString *,CBPeripheral *> *foundDevices;
22
+ @property (strong,nonatomic) NSMutableDictionary <NSString *,NSDictionary *> *foundDevicesMetadata;
22
23
  @property (strong,nonatomic) NSString *waitingConnect;
23
24
  @property (nonatomic,copy) RCTPromiseResolveBlock connectResolveBlock;
24
25
  @property (nonatomic,copy) RCTPromiseRejectBlock connectRejectBlock;
@@ -21,23 +21,104 @@ static NSArray<CBUUID *> *supportServices = nil;
21
21
  static NSDictionary *writeableCharactiscs = nil;
22
22
  bool hasListeners;
23
23
  static CBPeripheral *connected;
24
+ static CBCharacteristic *cachedWriteCharacteristic = nil; // Cache the writable characteristic
24
25
  static RNBluetoothManager *instance;
25
26
  static NSObject<WriteDataToBleDelegate> *writeDataDelegate;// delegate of write data resule;
26
27
  static NSData *toWrite;
27
28
  static NSTimer *timer;
28
29
 
30
+ -(instancetype)init {
31
+ if (self = [super init]) {
32
+ // Initialize CBCentralManager eagerly so it's ready when methods are called
33
+ // This triggers the centralManagerDidUpdateState callback immediately
34
+ [self centralManager];
35
+ [self initSupportServices];
36
+ }
37
+ return self;
38
+ }
39
+
29
40
  +(Boolean)isConnected{
30
41
  return !(connected==nil);
31
42
  }
32
43
 
44
+ // Helper method to write data with automatic chunking for BLE MTU limits
45
+ +(BOOL)writeDataWithChunking:(NSData *)data
46
+ toCharacteristic:(CBCharacteristic *)characteristic
47
+ onPeripheral:(CBPeripheral *)peripheral {
48
+ CBCharacteristicWriteType writeType = (characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse)
49
+ ? CBCharacteristicWriteWithoutResponse
50
+ : CBCharacteristicWriteWithResponse;
51
+
52
+ // BLE has MTU limitations - need to chunk data if it's too large
53
+ // The minimum BLE MTU is 23 bytes, with 3 bytes overhead, leaving 20 bytes usable
54
+ // maximumWriteValueLengthForType often returns incorrect values for WriteWithoutResponse
55
+ // so we force a conservative chunk size
56
+ NSUInteger maxWriteLength = 20; // Safe default that works with all BLE devices
57
+
58
+ NSUInteger dataLength = [data length];
59
+
60
+ if(dataLength <= maxWriteLength) {
61
+ // Data fits in one write - no chunking needed
62
+ [peripheral writeValue:data forCharacteristic:characteristic type:writeType];
63
+ return YES;
64
+ } else {
65
+ // Need to chunk the data
66
+ NSLog(@"BLE chunking: %lu bytes -> %lu byte chunks", dataLength, maxWriteLength);
67
+ NSUInteger offset = 0;
68
+ BOOL allSuccess = YES;
69
+ NSUInteger chunkCount = 0;
70
+
71
+ while(offset < dataLength && allSuccess) {
72
+ NSUInteger chunkSize = MIN(maxWriteLength, dataLength - offset);
73
+ NSData *chunk = [data subdataWithRange:NSMakeRange(offset, chunkSize)];
74
+
75
+ @try {
76
+ [peripheral writeValue:chunk forCharacteristic:characteristic type:writeType];
77
+ chunkCount++;
78
+
79
+ // Small delay between chunks to avoid overwhelming the device
80
+ if(writeType == CBCharacteristicWriteWithoutResponse) {
81
+ [NSThread sleepForTimeInterval:0.02]; // 20ms delay
82
+ }
83
+ }
84
+ @catch(NSException *e) {
85
+ NSLog(@"BLE chunk write error at offset %lu: %@", offset, e);
86
+ allSuccess = NO;
87
+ }
88
+
89
+ offset += chunkSize;
90
+ }
91
+
92
+ if(allSuccess) {
93
+ NSLog(@"BLE chunking complete: %lu chunks sent", chunkCount);
94
+ }
95
+ return allSuccess;
96
+ }
97
+ }
98
+
33
99
  +(void)writeValue:(NSData *) data withDelegate:(NSObject<WriteDataToBleDelegate> *) delegate
34
100
  {
35
101
  @try{
36
102
  writeDataDelegate = delegate;
37
103
  toWrite = data;
38
104
  connected.delegate = instance;
39
- [connected discoverServices:supportServices];
40
- // [connected writeValue:data forCharacteristic:[writeableCharactiscs objectForKey:supportServices[0]] type:CBCharacteristicWriteWithoutResponse];
105
+
106
+ // If we have a cached writable characteristic, use it directly
107
+ if(cachedWriteCharacteristic && connected){
108
+ // Use helper method to write with chunking
109
+ BOOL success = [RNBluetoothManager writeDataWithChunking:toWrite
110
+ toCharacteristic:cachedWriteCharacteristic
111
+ onPeripheral:connected];
112
+
113
+ if(writeDataDelegate) [writeDataDelegate didWriteDataToBle:success];
114
+ toWrite = nil;
115
+ return;
116
+ }
117
+
118
+ // No cached characteristic, need to discover services first
119
+ NSLog(@"No cached characteristic, discovering services...");
120
+ // Discover all services (not just SPP which is for Classic BT, not BLE)
121
+ [connected discoverServices:nil];
41
122
  }
42
123
  @catch(NSException *e){
43
124
  NSLog(@"error in writing data to %@,issue:%@",connected,e);
@@ -119,7 +200,11 @@ RCT_EXPORT_METHOD(isBluetoothEnabled:(RCTPromiseResolveBlock)resolve
119
200
  RCT_EXPORT_METHOD(enableBluetooth:(RCTPromiseResolveBlock)resolve
120
201
  rejecter:(RCTPromiseRejectBlock)reject)
121
202
  {
122
- resolve(nil);
203
+ // iOS doesn't allow programmatic Bluetooth enabling
204
+ // Bluetooth must be enabled from Settings by the user
205
+ // Return empty array to match Android's return type (array of paired devices)
206
+ // Since iOS Core Bluetooth doesn't have "paired devices" concept, we return empty array
207
+ resolve(@[]);
123
208
  }
124
209
  //disableBluetooth
125
210
  RCT_EXPORT_METHOD(disableBluetooth:(RCTPromiseResolveBlock)resolve
@@ -141,15 +226,50 @@ RCT_EXPORT_METHOD(scanDevices:(RCTPromiseResolveBlock)resolve
141
226
  }
142
227
  self.scanResolveBlock = resolve;
143
228
  self.scanRejectBlock = reject;
229
+
230
+ // Initialize metadata dictionary if needed
231
+ if(!self.foundDevicesMetadata){
232
+ self.foundDevicesMetadata = [[NSMutableDictionary alloc] init];
233
+ }
234
+
144
235
  if(connected && connected.identifier){
145
- NSDictionary *idAndName =@{@"address":connected.identifier.UUIDString,@"name":connected.name?connected.name:@""};
146
- NSDictionary *peripheralStored = @{connected.identifier.UUIDString:connected};
236
+ NSString *name = connected.name ? connected.name : @"";
237
+ NSString *address = connected.identifier.UUIDString;
238
+
239
+ // Create device info with device class for connected device
240
+ NSMutableDictionary *deviceInfo = [[NSMutableDictionary alloc] init];
241
+ [deviceInfo setObject:address forKey:@"address"];
242
+ [deviceInfo setObject:name forKey:@"name"];
243
+ [deviceInfo setObject:@(0) forKey:@"rssi"]; // Unknown RSSI for already connected device
244
+
245
+ // Try to infer if it's a printer from the name
246
+ BOOL isPrinter = NO;
247
+ NSString *upperName = name.uppercaseString;
248
+ if ([upperName containsString:@"PRINT"] ||
249
+ [upperName containsString:@"POS"] ||
250
+ [upperName containsString:@"ESCPOS"] ||
251
+ [upperName containsString:@"THERMAL"] ||
252
+ [upperName containsString:@"RECEIPT"]) {
253
+ isPrinter = YES;
254
+ }
255
+
256
+ if (isPrinter) {
257
+ [deviceInfo setObject:@(1536) forKey:@"majorDeviceClass"]; // Imaging
258
+ [deviceInfo setObject:@(1664) forKey:@"deviceClass"]; // Printer
259
+ } else {
260
+ [deviceInfo setObject:@(7936) forKey:@"majorDeviceClass"]; // Unknown
261
+ [deviceInfo setObject:@(7936) forKey:@"deviceClass"]; // Unknown
262
+ }
263
+
264
+ NSDictionary *peripheralStored = @{address:connected};
147
265
  if(!self.foundDevices){
148
266
  self.foundDevices = [[NSMutableDictionary alloc] init];
149
267
  }
150
268
  [self.foundDevices addEntriesFromDictionary:peripheralStored];
269
+ [self.foundDevicesMetadata setObject:deviceInfo forKey:address];
270
+
151
271
  if(hasListeners){
152
- [self sendEventWithName:EVENT_DEVICE_FOUND body:@{@"device":idAndName}];
272
+ [self sendEventWithName:EVENT_DEVICE_FOUND body:@{@"device":deviceInfo}];
153
273
  }
154
274
  }
155
275
  [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@NO}];
@@ -183,6 +303,13 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
183
303
  rejecter:(RCTPromiseRejectBlock)reject)
184
304
  {
185
305
  NSLog(@"Trying to connect....%@",address);
306
+
307
+ // Check if Bluetooth is ready
308
+ if(!self.centralManager || self.centralManager.state!=CBManagerStatePoweredOn){
309
+ reject(@"BLUETOOTH_NOT_READY",@"Bluetooth is not enabled or not ready. Please enable Bluetooth and try again.",nil);
310
+ return;
311
+ }
312
+
186
313
  [self callStop];
187
314
  if(connected){
188
315
  NSString *connectedAddress =connected.identifier.UUIDString;
@@ -205,6 +332,13 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
205
332
  // Callbacks:
206
333
  // centralManager:didConnectPeripheral:
207
334
  // centralManager:didFailToConnectPeripheral:error:
335
+
336
+ // Set connection timeout (30 seconds)
337
+ if(timer && timer.isValid){
338
+ [timer invalidate];
339
+ timer = nil;
340
+ }
341
+ timer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(handleConnectTimeout) userInfo:nil repeats:NO];
208
342
  }else{
209
343
  //starts the scan.
210
344
  _waitingConnect = address;
@@ -212,35 +346,115 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
212
346
  [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@NO}];
213
347
  //Callbacks:
214
348
  //centralManager:didDiscoverPeripheral:advertisementData:RSSI:
349
+
350
+ // Set scan + connection timeout (30 seconds)
351
+ if(timer && timer.isValid){
352
+ [timer invalidate];
353
+ timer = nil;
354
+ }
355
+ timer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(handleConnectTimeout) userInfo:nil repeats:NO];
356
+ }
357
+ }
358
+ //disconnect(address)
359
+ RCT_EXPORT_METHOD(disconnect:(NSString *)address
360
+ findEventsWithResolver:(RCTPromiseResolveBlock)resolve
361
+ rejecter:(RCTPromiseRejectBlock)reject)
362
+ {
363
+ NSLog(@"Trying to disconnect device: %@",address);
364
+
365
+ // Check if Bluetooth is ready
366
+ if(!self.centralManager || self.centralManager.state!=CBManagerStatePoweredOn){
367
+ reject(@"BLUETOOTH_NOT_READY",@"Bluetooth is not enabled or not ready.",nil);
368
+ return;
369
+ }
370
+
371
+ if(connected && [connected.identifier.UUIDString isEqualToString:address]){
372
+ [self.centralManager cancelPeripheralConnection:connected];
373
+ // Note: disconnection is async, but we resolve immediately like Android does
374
+ // The didDisconnectPeripheral callback will handle setting connected = nil
375
+ resolve(address);
376
+ } else {
377
+ // Device not connected or different device
378
+ NSLog(@"Device not connected or different device: %@",address);
379
+ resolve(address);
215
380
  }
216
381
  }
217
382
  //unpaire(address)
383
+ RCT_EXPORT_METHOD(unpaire:(NSString *)address
384
+ findEventsWithResolver:(RCTPromiseResolveBlock)resolve
385
+ rejecter:(RCTPromiseRejectBlock)reject)
386
+ {
387
+ NSLog(@"Trying to unpair/disconnect device: %@",address);
388
+ if(connected && [connected.identifier.UUIDString isEqualToString:address]){
389
+ [self.centralManager cancelPeripheralConnection:connected];
390
+ // Note: Core Bluetooth on iOS doesn't support programmatic unpairing
391
+ // The connection will be cancelled, but the device remains paired at OS level
392
+ // Users must unpair manually from iOS Settings if needed
393
+ connected = nil;
394
+ resolve(address);
395
+ } else {
396
+ // Device not connected, just resolve
397
+ NSLog(@"Device not connected, nothing to unpair: %@",address);
398
+ resolve(address);
399
+ }
400
+ }
218
401
 
219
402
 
403
+ -(void)handleConnectTimeout{
404
+ NSLog(@"Connection timeout - device not found or unable to connect");
405
+ if(self.centralManager.isScanning){
406
+ [self.centralManager stopScan];
407
+ }
408
+ if(self.connectRejectBlock){
409
+ RCTPromiseRejectBlock rjBlock = self.connectRejectBlock;
410
+ rjBlock(@"CONNECTION_TIMEOUT",@"Could not connect to device - timeout after 30 seconds",nil);
411
+ self.connectRejectBlock = nil;
412
+ self.connectResolveBlock = nil;
413
+ }
414
+ _waitingConnect = nil;
415
+ if(timer && timer.isValid){
416
+ [timer invalidate];
417
+ timer = nil;
418
+ }
419
+ }
420
+
220
421
  -(void)callStop{
221
422
  if(self.centralManager.isScanning){
222
423
  [self.centralManager stopScan];
223
- NSMutableArray *devices = [[NSMutableArray alloc] init];
224
- for(NSString *key in self.foundDevices){
225
- NSLog(@"insert found devies:%@ =>%@",key,[self.foundDevices objectForKey:key]);
424
+ }
425
+
426
+ NSMutableArray *devices = [[NSMutableArray alloc] init];
427
+ for(NSString *key in self.foundDevices){
428
+ NSLog(@"insert found devices:%@ =>%@",key,[self.foundDevices objectForKey:key]);
429
+ NSDictionary *deviceInfo = [self.foundDevicesMetadata objectForKey:key];
430
+ if (deviceInfo) {
431
+ [devices addObject:deviceInfo];
432
+ } else {
226
433
  NSString *name = [self.foundDevices objectForKey:key].name;
227
- if(!name){
228
- name = @"";
229
- }
230
- [devices addObject:@{@"address":key,@"name":name}];
231
- }
232
- NSError *error = nil;
233
- NSData* jsonData = [NSJSONSerialization dataWithJSONObject:devices options:NSJSONWritingPrettyPrinted error:&error];
234
- NSString * jsonStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
235
- if(hasListeners){
236
- [self sendEventWithName:EVENT_DEVICE_DISCOVER_DONE body:@{@"found":jsonStr,@"paired":@"[]"}];
237
- }
238
- if(self.scanResolveBlock){
239
- RCTPromiseResolveBlock rsBlock = self.scanResolveBlock;
240
- rsBlock(@{@"found":jsonStr,@"paired":@"[]"});
241
- self.scanResolveBlock = nil;
434
+ if(!name) name = @"";
435
+ [devices addObject:@{@"address":key, @"name":name}];
242
436
  }
243
437
  }
438
+
439
+ NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
440
+ [result setObject:@[] forKey:@"paired"];
441
+ [result setObject:devices forKey:@"found"];
442
+
443
+ NSError *error = nil;
444
+ NSData *resultJsonData = [NSJSONSerialization dataWithJSONObject:result options:0 error:&error];
445
+ NSString *resultJsonStr = [[NSString alloc] initWithData:resultJsonData encoding:NSUTF8StringEncoding];
446
+
447
+ if(hasListeners){
448
+ NSData *foundJsonData = [NSJSONSerialization dataWithJSONObject:devices options:0 error:&error];
449
+ NSString *foundJsonStr = [[NSString alloc] initWithData:foundJsonData encoding:NSUTF8StringEncoding];
450
+ [self sendEventWithName:EVENT_DEVICE_DISCOVER_DONE body:@{@"found":foundJsonStr, @"paired":@"[]"}];
451
+ }
452
+ if(self.scanResolveBlock){
453
+ RCTPromiseResolveBlock rsBlock = self.scanResolveBlock;
454
+ rsBlock(resultJsonStr);
455
+ self.scanResolveBlock = nil;
456
+ }
457
+
244
458
  if(timer && timer.isValid){
245
459
  [timer invalidate];
246
460
  timer = nil;
@@ -251,9 +465,11 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
251
465
  - (void) initSupportServices
252
466
  {
253
467
  if(!supportServices){
254
- CBUUID *issc = [CBUUID UUIDWithString: @"49535343-FE7D-4AE5-8FA9-9FAFD205E455"];
255
- supportServices = [NSArray arrayWithObject:issc];/*ISSC*/
256
- writeableCharactiscs = @{issc:@"49535343-8841-43F4-A8D4-ECBE34729BB3"};
468
+ // Use standard SPP (Serial Port Profile) UUID - same as Android
469
+ CBUUID *spp = [CBUUID UUIDWithString: @"00001101-0000-1000-8000-00805F9B34FB"];
470
+ supportServices = [NSArray arrayWithObject:spp];
471
+ // SPP uses the same UUID for service and characteristic
472
+ writeableCharactiscs = @{spp:@"00001101-0000-1000-8000-00805F9B34FB"};
257
473
  }
258
474
  }
259
475
 
@@ -284,19 +500,108 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
284
500
  * CBCentralManagerDelegate
285
501
  **/
286
502
  - (void)centralManagerDidUpdateState:(CBCentralManager *)central{
287
- NSLog(@"%ld",(long)central.state);
503
+ NSString *stateString;
504
+ switch (central.state) {
505
+ case CBManagerStateUnknown:
506
+ stateString = @"Unknown";
507
+ break;
508
+ case CBManagerStateResetting:
509
+ stateString = @"Resetting";
510
+ break;
511
+ case CBManagerStateUnsupported:
512
+ stateString = @"Unsupported";
513
+ break;
514
+ case CBManagerStateUnauthorized:
515
+ stateString = @"Unauthorized";
516
+ break;
517
+ case CBManagerStatePoweredOff:
518
+ stateString = @"PoweredOff";
519
+ break;
520
+ case CBManagerStatePoweredOn:
521
+ stateString = @"PoweredOn";
522
+ break;
523
+ default:
524
+ stateString = @"Unknown";
525
+ break;
526
+ }
527
+ NSLog(@"Bluetooth State Changed: %@ (%ld)", stateString, (long)central.state);
288
528
  }
289
529
 
290
530
  - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI{
291
531
  NSLog(@"did discover peripheral: %@",peripheral);
292
- NSDictionary *idAndName =@{@"address":peripheral.identifier.UUIDString,@"name":peripheral.name?peripheral.name:@""};
532
+
533
+ // Extract device information with enhanced metadata
534
+ NSMutableDictionary *deviceInfo = [[NSMutableDictionary alloc] init];
535
+ [deviceInfo setObject:peripheral.identifier.UUIDString forKey:@"address"];
536
+ [deviceInfo setObject:(peripheral.name ? peripheral.name : @"") forKey:@"name"];
537
+
538
+ // Add RSSI for signal strength
539
+ [deviceInfo setObject:RSSI forKey:@"rssi"];
540
+
541
+ // Try to infer device class from advertising data
542
+ // iOS BLE doesn't expose Classic Bluetooth device class, but we can make educated guesses
543
+ NSInteger deviceClass = 0;
544
+ NSInteger majorDeviceClass = 0;
545
+
546
+ // Check if device advertises printer-related services
547
+ NSArray *serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey];
548
+ BOOL isPrinter = NO;
549
+
550
+ if (serviceUUIDs && serviceUUIDs.count > 0) {
551
+ for (CBUUID *uuid in serviceUUIDs) {
552
+ NSString *uuidString = uuid.UUIDString.uppercaseString;
553
+ // Serial Port Profile (SPP) UUID - common for printers
554
+ // Also check for other printer-related service UUIDs
555
+ if ([uuidString isEqualToString:@"00001101-0000-1000-8000-00805F9B34FB"] ||
556
+ [uuidString isEqualToString:@"49535343-FE7D-4AE5-8FA9-9FAFD205E455"] ||
557
+ [uuidString containsString:@"18F0"]) {
558
+ isPrinter = YES;
559
+ break;
560
+ }
561
+ }
562
+ }
563
+
564
+ // Check device name for printer keywords
565
+ NSString *name = peripheral.name ? peripheral.name.uppercaseString : @"";
566
+ if ([name containsString:@"PRINT"] ||
567
+ [name containsString:@"POS"] ||
568
+ [name containsString:@"ESCPOS"] ||
569
+ [name containsString:@"THERMAL"] ||
570
+ [name containsString:@"RECEIPT"]) {
571
+ isPrinter = YES;
572
+ }
573
+
574
+ if (isPrinter) {
575
+ // Android Bluetooth Class values for printers:
576
+ // Major Device Class: 0x0600 (Imaging - printers, scanners, cameras, etc.)
577
+ // Device Class: 0x000680 (Imaging/Printer)
578
+ majorDeviceClass = 1536; // 0x0600 in decimal
579
+ deviceClass = 1664; // 0x0680 in decimal
580
+ } else {
581
+ // Unknown/Unclassified device
582
+ // Major Device Class: 0x1F00 (Uncategorized)
583
+ majorDeviceClass = 7936; // 0x1F00 in decimal
584
+ deviceClass = 7936;
585
+ }
586
+
587
+ [deviceInfo setObject:@(deviceClass) forKey:@"deviceClass"];
588
+ [deviceInfo setObject:@(majorDeviceClass) forKey:@"majorDeviceClass"];
589
+
590
+ // Store the peripheral object for connection
293
591
  NSDictionary *peripheralStored = @{peripheral.identifier.UUIDString:peripheral};
294
592
  if(!self.foundDevices){
295
593
  self.foundDevices = [[NSMutableDictionary alloc] init];
296
594
  }
297
595
  [self.foundDevices addEntriesFromDictionary:peripheralStored];
596
+
597
+ // Store device metadata for later retrieval
598
+ if(!self.foundDevicesMetadata){
599
+ self.foundDevicesMetadata = [[NSMutableDictionary alloc] init];
600
+ }
601
+ [self.foundDevicesMetadata setObject:deviceInfo forKey:peripheral.identifier.UUIDString];
602
+
298
603
  if(hasListeners){
299
- [self sendEventWithName:EVENT_DEVICE_FOUND body:@{@"device":idAndName}];
604
+ [self sendEventWithName:EVENT_DEVICE_FOUND body:@{@"device":deviceInfo}];
300
605
  }
301
606
  if(_waitingConnect && [_waitingConnect isEqualToString: peripheral.identifier.UUIDString]){
302
607
  [self.centralManager connectPeripheral:peripheral options:nil];
@@ -308,10 +613,24 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
308
613
  NSLog(@"did connected: %@",peripheral);
309
614
  connected = peripheral;
310
615
  NSString *pId = peripheral.identifier.UUIDString;
616
+
617
+ // Set peripheral delegate to receive service/characteristic callbacks
618
+ peripheral.delegate = self;
619
+
311
620
  if(_waitingConnect && [_waitingConnect isEqualToString: pId] && self.connectResolveBlock){
312
- NSLog(@"Predefined the support services, stop to looking up services.");
313
- // peripheral.delegate=self;
314
- // [peripheral discoverServices:nil];
621
+ // Cancel connection timeout timer if it's running
622
+ if(timer && timer.isValid){
623
+ [timer invalidate];
624
+ timer = nil;
625
+ }
626
+
627
+ // Pre-discover services to prepare for future write operations
628
+ // This matches Android's behavior where the connection is fully ready after connect
629
+ // Pass nil to discover ALL services (not just SPP which is for Classic BT, not BLE)
630
+ NSLog(@"Pre-discovering all services for faster write operations...");
631
+ [peripheral discoverServices:nil];
632
+
633
+ // Resolve promise immediately (iOS BLE connects fast, service discovery happens async)
315
634
  self.connectResolveBlock(nil);
316
635
  _waitingConnect = nil;
317
636
  self.connectRejectBlock = nil;
@@ -324,6 +643,9 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
324
643
  }
325
644
 
326
645
  - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
646
+ // Clear cached characteristic when disconnecting
647
+ cachedWriteCharacteristic = nil;
648
+
327
649
  if(!connected && _waitingConnect && [_waitingConnect isEqualToString:peripheral.identifier.UUIDString]){
328
650
  if(self.connectRejectBlock){
329
651
  RCTPromiseRejectBlock rjBlock = self.connectRejectBlock;
@@ -345,6 +667,9 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
345
667
  }
346
668
 
347
669
  - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
670
+ // Clear cached characteristic when connection fails
671
+ cachedWriteCharacteristic = nil;
672
+
348
673
  if(self.connectRejectBlock){
349
674
  RCTPromiseRejectBlock rjBlock = self.connectRejectBlock;
350
675
  rjBlock(@"",@"",error);
@@ -412,38 +737,59 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
412
737
  */
413
738
  - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error{
414
739
  if(toWrite && connected
415
- && [connected.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]
416
- && [service.UUID.UUIDString isEqualToString:supportServices[0].UUIDString]){
740
+ && [connected.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]){
417
741
  if(error){
418
- NSLog(@"Discrover charactoreristics error:%@",error);
742
+ NSLog(@"Discover characteristics error:%@",error);
419
743
  if(writeDataDelegate)
420
744
  {
421
745
  [writeDataDelegate didWriteDataToBle:false];
422
746
  return;
423
747
  }
424
748
  }
749
+
750
+ // Try to write to any service that has writable characteristics
751
+ // Don't filter by supportServices - many BLE printers use custom UUIDs
752
+ NSLog(@"Checking service %@ for writable characteristics...", service.UUID.UUIDString);
753
+
754
+ // Auto-detect writable characteristic by checking properties
425
755
  for(CBCharacteristic *cc in service.characteristics){
426
- NSLog(@"Characterstic found: %@ in service: %@" ,cc,service.UUID.UUIDString);
427
- if([cc.UUID.UUIDString isEqualToString:[writeableCharactiscs objectForKey: supportServices[0]]]){
756
+ NSLog(@"Characteristic found: %@ in service: %@" ,cc,service.UUID.UUIDString);
757
+
758
+ // Check if characteristic supports write operations
759
+ BOOL canWrite = (cc.properties & CBCharacteristicPropertyWrite) ||
760
+ (cc.properties & CBCharacteristicPropertyWriteWithoutResponse);
761
+
762
+ if(canWrite){
428
763
  @try{
429
- [connected writeValue:toWrite forCharacteristic:cc type:CBCharacteristicWriteWithoutResponse];
430
- if(writeDataDelegate) [writeDataDelegate didWriteDataToBle:true];
431
- if(toWrite){
432
- NSLog(@"Value wrote: %lu",[toWrite length]);
433
- }
764
+ NSLog(@"Writing %lu bytes to characteristic %@ in service %@",[toWrite length], cc.UUID.UUIDString, service.UUID.UUIDString);
765
+
766
+ // Use helper method to write with chunking
767
+ BOOL success = [RNBluetoothManager writeDataWithChunking:toWrite
768
+ toCharacteristic:cc
769
+ onPeripheral:connected];
770
+
771
+ // Cache this characteristic for future writes
772
+ cachedWriteCharacteristic = cc;
773
+ NSLog(@"Cached characteristic %@ for future writes", cc.UUID.UUIDString);
774
+
775
+ if(writeDataDelegate) [writeDataDelegate didWriteDataToBle:success];
776
+ toWrite = nil; // Clear the write buffer
777
+ return; // Success, exit after first successful write
434
778
  }
435
779
  @catch(NSException *e){
436
- NSLog(@"ERRO IN WRITE VALUE: %@",e);
437
- [writeDataDelegate didWriteDataToBle:false];
780
+ NSLog(@"ERROR IN WRITE VALUE: %@",e);
781
+ [writeDataDelegate didWriteDataToBle:false];
438
782
  }
439
783
  }
440
784
  }
441
785
 
442
-
786
+ // If we get here, no writable characteristic was found in this service
787
+ NSLog(@"No writable characteristic found in service: %@", service.UUID.UUIDString);
788
+ // Don't call didWriteDataToBle:false here - there might be other services to check
443
789
  }
444
790
 
445
791
  if(error){
446
- NSLog(@"Discrover charactoreristics error:%@",error);
792
+ NSLog(@"Discover characteristics error:%@",error);
447
793
  return;
448
794
  }
449
795
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-bluetooth-escpos-printer-fork",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "React-Native plugin for the bluetooth ESC/POS printers.",
5
5
  "main": "index.js",
6
6
  "scripts": {