callstack / callstack/react-native-bottom-tabs
[iOS] Fabric: SIGABRT on tab press when tab key contains non-ASCII characters (kCFStringEncodingUTF8 passed to -cStringUsingEncoding:)
- Dominant language
- TypeScript
- Stars
- 1.5k
- Forks
- 109
- Avg merge
- 11h 44m
- Merged PRs (30d)
- 8
Description
### Before submitting a new issue
- [x] I tested using the latest version of the library, as the bug might be already fixed.
- [x] I tested using a supported version of react native.
- [x] I checked for possible duplicate issues, with possible answers.
### Bug summary
On the New Architecture (Fabric), pressing (or long-pressing) a tab whose `key` contains any non-ASCII character — Korean, Japanese, Chinese, Cyrillic, emoji, accented Latin, … — crashes the app instantly with SIGABRT.
`RCTTabViewComponentView.mm` builds the event payloads like this ([L241](https://github.com/callstack/react-native-bottom-tabs/blob/1bbcaa2ea50b61f5daa6ccb8fe9669188c982864/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm#L241), [L250](https://github.com/callstack/react-native-bottom-tabs/blob/1bbcaa2ea50b61f5daa6ccb8fe9669188c982864/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm#L250)):
```objc
eventEmitter->onPageSelected(RNCTabViewEventEmitter::OnPageSelected{
.key = [key cStringUsingEncoding:kCFStringEncodingUTF8]
});
```
The problem: `-cStringUsingEncoding:` expects an **`NSStringEncoding`** (`NSUTF8StringEncoding` = `4`), but the code passes **`kCFStringEncodingUTF8`**, which is a **`CFStringEncoding`** constant with the value `0x08000100`. That value is not a valid `NSStringEncoding` (converting between the two families requires `CFStringConvertEncodingToNSStringEncoding()`).
What happens at runtime:
- For pure-ASCII keys the call happens to return a valid pointer (internal 8-bit buffer fast path), so the bug goes unnoticed in English-only apps.
- For keys containing non-ASCII characters, the conversion fails and `cStringUsingEncoding:` returns `NULL` (its documented behavior when the receiver cannot be converted to the given encoding). The generated `OnPageSelected` / `OnTabLongPress` structs declare `key` as `std::string`, and constructing a `std::string` from `NULL` is undefined behavior → `abort()`.
This affects every app using `@bottom-tabs/react-navigation` with non-ASCII screen names: react-navigation derives the route key from the screen name (a screen named `메시지` gets a route key like `메시지-AbC12xyz`), and that route key is exactly what is passed to `onPageSelectedWithKey:` / `onLongPressWithKey:`. Tapping such a tab crashes immediately. Standalone `TabView` usage with non-ASCII `navigationState` route keys crashes the same way.
The old architecture is not affected (the whole file is `#ifdef RCT_NEW_ARCH_ENABLED`).
### Library version
1.4.0 (latest release; the code is unchanged on current `main` — see permalinks above)
### Environment info
```shell
System:
OS: macOS 26.6.1
Binaries:
Node: 22.22.1
npmPackages:
react: 19.1.0
react-native: 0.81.5
expo: ~54.0.0
react-native-bottom-tabs: 1.4.0
@bottom-tabs/react-navigation: 1.4.0
@react-navigation/native: 7.3.14
Settings:
newArchEnabled: true (Fabric)
Tested on: iPhone Simulator (iOS 26)
```
### Steps to reproduce
1. Create a React Native 0.81 app with the New Architecture enabled and install `react-native-bottom-tabs` + `@bottom-tabs/react-navigation`.
2. Add a tab screen whose `name` contains non-ASCII characters, e.g. `메시지` (Korean).
3. Run the app on iOS and tap that tab (long-press also triggers it via `onTabLongPress`).
4. The app aborts immediately — SIGABRT from the `std::string` constructor being fed `NULL` in `RCTTabViewComponentView.mm`.
### Reproducible sample code
```js
import { NavigationContainer } from '@react-navigation/native';
import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation';
import { Text, View } from 'react-native';
const Tab = createNativeBottomTabNavigator();
const Screen = () => (
Hello
);
export default function App() {
return (
{/* Tapping this tab crashes the app on iOS (Fabric): */}
);
}
```
### Proposed fix
Use `-UTF8String`, which returns a proper UTF-8 C string for any `NSString` contents (with a `?: ""` guard for the nil-receiver edge case), at both call sites:
```diff
--- a/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm
+++ b/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm
@@ -238,7 +238,7 @@ - (void)onPageSelectedWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
auto eventEmitter = std::static_pointer_cast(_eventEmitter);
if (eventEmitter) {
eventEmitter->onPageSelected(RNCTabViewEventEmitter::OnPageSelected{
- .key = [key cStringUsingEncoding:kCFStringEncodingUTF8]
+ .key = std::string([key UTF8String] ?: "")
});
}
}
@@ -247,7 +247,7 @@ - (void)onLongPressWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
auto eventEmitter = std::static_pointer_cast(_eventEmitter);
if (eventEmitter) {
eventEmitter->onTabLongPress(RNCTabViewEventEmitter::OnTabLongPress {
- .key = [key cStringUsingEncoding:kCFStringEncodingUTF8]
+ .key = std::string([key UTF8String] ?: "")
});
}
}
```
We are running exactly this as a `patch-package` patch in production and it fully resolves the crash. Happy to open a PR if you'd like.
Contributor guide
Assessment
This issue has not been assessed yet.