I'll be honest, I'm a React Native developer and not an iOS developer, the issue was solved with Claude Code. I've tested the fix and it works flawlessly on my devices.
--- Issue description below generated by Claude Code ---
Summary
On iOS, the app crashes with EXC_BAD_ACCESS during printer discovery when using Epson TM-m30 printers over LAN. The crash is caused by a race condition in the onDiscovery: callback in EscPosPrinterDiscovery.mm where the mutable _printerList array is passed directly to the React Native bridge, which may serialize it asynchronously while another callback mutates it.
This issue is iOS-specific. We have not observed this crash on Android.
Root Cause Analysis
After extensive debugging, we identified the root cause in the react-native-esc-pos-printer integration code (ios/EscPosPrinterDiscovery.mm), not the Epson ePOS SDK itself:
- The Epson ePOS SDK fires
onDiscovery: delegate callbacks from background threads (this is expected SDK
behavior)
- The current integration code passes
_printerList (an NSMutableArray) directly to the React Native
bridge via sendEventWithName:body:
- The React Native bridge may serialize this array asynchronously after the method returns
- While serialization is in progress, another callback can mutate
_printerList, causing concurrent
modification and EXC_BAD_ACCESS
Note: The Epson ePOS SDK is a closed-source binary. The fix is in the Objective-C++ wrapper code that bridges SDK callbacks to React Native, ensuring thread-safe handling of the mutable array.
Evidence: Adding extensive NSLog statements (which block the thread during I/O) prevented the crash, confirming the timing-sensitive nature of the race condition.
Proposed Fix
Changes to ios/EscPosPrinterDiscovery.mm:
- (void) onDiscovery:(Epos2DeviceInfo *)deviceInfo
{
// Copy all values from deviceInfo IMMEDIATELY on the callback thread.
// Use [copy] to create our own string objects in case SDK reuses internal buffers.
NSString *target = [[deviceInfo getTarget] copy] ?: @"";
NSString *deviceName = [[deviceInfo getDeviceName] copy] ?: @"";
NSString *ipAddress = [[deviceInfo getIpAddress] copy] ?: @"";
NSString *macAddress = [[deviceInfo getMacAddress] copy] ?: @"";
NSString *bdAddress = [[deviceInfo getBdAddress] copy] ?: @"";
int deviceType = [deviceInfo getDeviceType];
NSDictionary *deviceDict = @{
@"target": target,
@"deviceName": deviceName,
@"ipAddress": ipAddress,
@"macAddress": macAddress,
@"bdAddress": bdAddress,
@"deviceType": @(deviceType)
};
// Static lock to serialize access to _printerList
static NSLock *discoveryLock;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
discoveryLock = [[NSLock alloc] init];
});
// Lock, mutate array, take immutable snapshot, unlock
[discoveryLock lock];
[_printerList addObject:deviceDict];
// CRITICAL: Create immutable copy for the bridge.
// This prevents crashes from concurrent modification if the bridge
// serializes asynchronously while another callback mutates _printerList.
NSArray *printerListSnapshot = [_printerList copy];
[discoveryLock unlock];
// Emit event with the immutable snapshot on main queue.
dispatch_async(dispatch_get_main_queue(), ^{
#if RCT_NEW_ARCH_ENABLED
[self emitOnDiscovery:printerListSnapshot];
#else
[self sendEventWithName:@"onDiscovery" body:printerListSnapshot];
#endif
});
}
Key changes:
- Immediate string copying - Use [copy] on all strings from deviceInfo in case the SDK reuses internal
buffers
- NSLock for thread safety - Serialize access to the mutable _printerList
- Immutable snapshot - Pass [_printerList copy] to the bridge instead of the mutable array
- Async dispatch to main queue - Safe because the snapshot is immutable
Related Issues
This may be related to #129 which reports similar EXC_BAD_ACCESS crashes during discovery.
Workaround
For anyone experiencing this issue, you can use pnpm patch (or patch-package for npm/yarn) to apply the fix
above to ios/EscPosPrinterDiscovery.mm.
Steps to reproduce
- Build and run the app on an iOS device
- Have an Epson TM-m30III printer connected via LAN
- Start printer discovery using
startDiscovery()
- The app crashes intermittently with
EXC_BAD_ACCESS
Note: The crash can occur with just one printer on the network, but the frequency increases with multiple printers.
react-native-esc-pos-printer version
4.5.0
React Native version
0.81.5
Platforms
iOS
Workflow
Expo Dev Client
Architecture
Fabric (New Architecture)
Build type
Release app & production bundle
Device
Real device
Device model
iPad (A16), iPadOS 26.2.1
Acknowledgements
Yes
I'll be honest, I'm a React Native developer and not an iOS developer, the issue was solved with Claude Code. I've tested the fix and it works flawlessly on my devices.
--- Issue description below generated by Claude Code ---
Summary
On iOS, the app crashes with
EXC_BAD_ACCESSduring printer discovery when using Epson TM-m30 printers over LAN. The crash is caused by a race condition in theonDiscovery:callback inEscPosPrinterDiscovery.mmwhere the mutable_printerListarray is passed directly to the React Native bridge, which may serialize it asynchronously while another callback mutates it.This issue is iOS-specific. We have not observed this crash on Android.
Root Cause Analysis
After extensive debugging, we identified the root cause in the react-native-esc-pos-printer integration code (
ios/EscPosPrinterDiscovery.mm), not the Epson ePOS SDK itself:onDiscovery:delegate callbacks from background threads (this is expected SDKbehavior)
_printerList(anNSMutableArray) directly to the React Nativebridge via
sendEventWithName:body:_printerList, causing concurrentmodification and
EXC_BAD_ACCESSNote: The Epson ePOS SDK is a closed-source binary. The fix is in the Objective-C++ wrapper code that bridges SDK callbacks to React Native, ensuring thread-safe handling of the mutable array.
Evidence: Adding extensive
NSLogstatements (which block the thread during I/O) prevented the crash, confirming the timing-sensitive nature of the race condition.Proposed Fix
Changes to
ios/EscPosPrinterDiscovery.mm:Key changes:
buffers
Related Issues
This may be related to #129 which reports similar EXC_BAD_ACCESS crashes during discovery.
Workaround
For anyone experiencing this issue, you can use pnpm patch (or patch-package for npm/yarn) to apply the fix
above to ios/EscPosPrinterDiscovery.mm.
Steps to reproduce
startDiscovery()EXC_BAD_ACCESSNote: The crash can occur with just one printer on the network, but the frequency increases with multiple printers.
react-native-esc-pos-printer version
4.5.0
React Native version
0.81.5
Platforms
iOS
Workflow
Expo Dev Client
Architecture
Fabric (New Architecture)
Build type
Release app & production bundle
Device
Real device
Device model
iPad (A16), iPadOS 26.2.1
Acknowledgements
Yes