callstack / callstack/react-native-bottom-tabs

[iOS] Fabric: SIGABRT on tab press when tab key contains non-ASCII characters (kCFStringEncodingUTF8 passed to -cStringUsingEncoding:)

Open
#560 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.