callstack / callstack/react-native-bottom-tabs
[iOS] Fabric: SIGABRT on tab press when tab key contains non-ASCII characters (kCFStringEncodingUTF8 passed to -cStringUsingEncoding:)
- Langage dominant
- TypeScript
- Étoiles
- 1.5k
- Forks
- 109
- Merge moyen
- 11 h 44 min
- PR mergées (30 j)
- 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.
Guide de contribution
Ouvrir le guide de contribution
Piste de recherche
Commencez dans packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm, aux sites d’appel de onPageSelectedWithKey: et onLongPressWithKey:. Reproduisez le problème avec l’exemple React Native fourni en utilisant l’onglet non ASCII 메시지, puis vérifiez que le fait d’appuyer dessus et d’effectuer un appui long ne provoque plus d’abandon sous Fabric et que la clé émise reste un UTF-8 valide.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- ios, objective-c, react-native
- Domaine
- mobile
- Type d'issue
- Bug
- Difficulté
- 2/5
- Temps estimé
- 1-3 heures
- Activité
- Calme
- Clarté
- Clairement spécifiée
- Accessibilité débutants
- 75/100