nexjax
v1.0.7
Published
Automatically configure iOS project settings (Team ID, version, display name, build number, iPhone-only) for React Native projects
Maintainers
Readme
Nexjax
A lightweight tool to automatically configure iOS project settings for React Native projects. Works with any React Native project (Expo, bare React Native, etc.).
Why Nexjax?
The Problem
When developing React Native apps for iOS, you face a frustrating cycle:
- Every
expo prebuild --cleanwipes your Xcode settings - Team ID, code signing, and other configurations are lost - Manual reconfiguration is tedious - Open Xcode, navigate to signing settings, select team, fix Info.plist values... every single time
- CI/CD pipelines become fragile - Automated builds break when Xcode settings aren't properly configured
Why Not EAS or Other Solutions?
| Solution | Limitation | |----------|------------| | EAS Build | Requires Expo account, cloud-based builds, costs money for higher tiers, overkill for simple local builds | | Fastlane | Heavy Ruby dependency, complex setup, steep learning curve, designed for full CI/CD pipelines | | Manual Xcode | Time-consuming, error-prone, doesn't survive prebuild, can't be automated | | Expo Config Plugins | Only works with Expo, requires understanding plugin system, can be complex for simple tasks |
The Nexjax Approach
Nexjax takes a different philosophy:
- Zero dependencies - Pure Node.js, no Ruby, no cloud accounts, no external services
- Instant setup - One command:
npx nexjax YOUR_TEAM_ID - Works offline - No internet required, no accounts needed
- Universal - Works with Expo, bare React Native, or any hybrid setup
- Survives rebuilds - Stores config in
ios.config.json, reapply with single command - CI/CD friendly - Simple, predictable, easy to integrate into any pipeline
- Focused scope - Does one thing well: configure iOS project settings
Perfect For
- Developers who prebuild frequently and are tired of reconfiguring Xcode
- Teams who want simple, reliable iOS configuration without cloud dependencies
- CI/CD pipelines that need deterministic, offline-capable builds
- Projects that don't need full EAS/Fastlane but still want automation
Features
- ✅ Auto-configure Team ID - Sets DEVELOPMENT_TEAM in Xcode project
- ✅ Auto-fill Version - Updates CFBundleShortVersionString from app.json (Expo) or package.json (bare RN)
- ✅ Auto-fill Build Number - Updates CFBundleVersion from app.json or config
- ✅ Auto-fill Display Name - Updates CFBundleDisplayName from app.json or package.json
- ✅ iPhone-only Support - Removes iPad orientations automatically
- ✅ Zero Dependencies - Pure Node.js, no external dependencies
- ✅ Works with any RN project - Supports both Expo (app.json) and bare React Native (package.json only)
Installation
From npmjs (default)
npm install --save-dev nexjax
# or
yarn add --dev nexjaxFrom npm github registry
Configure npm home directory to use github packages for the @leonurium scope:
# ~/.npmrc
@leonurium:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKENthen install:
npm install --save-dev @leonurium/nexjax
# or
yarn add --dev @leonurium/nexjaxQuick Start
1. First Time Setup
Get your Team ID:
- Open Xcode → Settings → Accounts
- Select your Apple ID
- Copy your Team ID (format:
ABC123DEF4)
Run the tool:
npx nexjax YOUR_TEAM_IDOr if installed locally:
npx nexjax YOUR_TEAM_IDEdit
ios.config.json(created automatically):{ "teamId": "YOUR_TEAM_ID", "version": "1.0.0", "displayName": "MyApp", "buildNumber": "1", "iphoneOnly": true, "permissions": { "NSPhotoLibraryUsageDescription": "We need access to your photos...", "NSCameraUsageDescription": "We need access to your camera..." } }Note: For Expo projects, permission strings can also be set in
app.json→expo.ios.infoPlistand nexjax will automatically read them!
2. Configure Permission Strings (Optional)
For Expo projects, add permissions in app.json:
{
"expo": {
"ios": {
"infoPlist": {
"NSPhotoLibraryUsageDescription": "We need access to your photos...",
"NSCameraUsageDescription": "We need access to your camera..."
}
}
}
}For bare RN projects, add permissions in ios.config.json:
{
"permissions": {
"NSPhotoLibraryUsageDescription": "We need access to your photos...",
"NSCameraUsageDescription": "We need access to your camera..."
}
}3. After Building iOS Project
After running npx expo prebuild or any iOS build command:
npx nexjaxOr add to your build scripts in package.json:
{
"scripts": {
"ios:build": "react-native run-ios && npx nexjax",
"ios:configure": "npx nexjax"
}
}Configuration
Config File: ios.config.json
The tool creates ios.config.json in your project root with these settings:
{
"teamId": "ABC123DEF4",
"bundleIdentifier": "com.example.app",
"version": "1.0.0",
"displayName": "MyApp",
"buildNumber": "1",
"iphoneOnly": true,
"permissions": {
"NSPhotoLibraryUsageDescription": "We need access to your photos...",
"NSCameraUsageDescription": "We need access to your camera...",
"NSMicrophoneUsageDescription": "We need access to your microphone..."
}
}Auto-sync with app.json / package.json
The tool automatically reads from configuration files:
For Expo projects (with app.json):
expo.version→ Used for CFBundleShortVersionStringexpo.name→ Used for CFBundleDisplayNameexpo.ios.buildNumber→ Used for CFBundleVersionexpo.ios.supportsTablet→ Determines iPhone-only settingexpo.ios.infoPlist.*→ Permission strings (all optional)
For bare React Native projects (package.json only):
package.json.version→ Used for CFBundleShortVersionStringpackage.json.name→ Used for CFBundleDisplayName
Permission Strings (Optional)
Nexjax can automatically configure iOS permission strings in Info.plist. Each permission is optional - only add the ones you need.
Configuration Priority:
ios.config.json→permissions(always works, overrides app.json)app.json→expo.ios.infoPlist(Expo projects only)
For Expo projects: Can use either app.json or ios.config.json (or both - ios.config.json overrides)
For bare RN projects: Use ios.config.json → permissions (no app.json available)
Supported permission keys:
NSPhotoLibraryUsageDescription- Photo library accessNSPhotoLibraryAddUsageDescription- Save photos to libraryNSCameraUsageDescription- Camera accessNSMicrophoneUsageDescription- Microphone accessNSLocationWhenInUseUsageDescription- Location when in useNSLocationAlwaysUsageDescription- Location alwaysNSContactsUsageDescription- Contacts accessNSCalendarsUsageDescription- Calendar accessNSRemindersUsageDescription- Reminders accessNSMotionUsageDescription- Motion & FitnessNSHealthShareUsageDescription- Health data readNSHealthUpdateUsageDescription- Health data writeNSBluetoothPeripheralUsageDescription- Bluetooth (deprecated, use NSBluetoothAlwaysUsageDescription)NSBluetoothAlwaysUsageDescription- Bluetooth alwaysNSSpeechRecognitionUsageDescription- Speech recognitionNSFaceIDUsageDescription- Face IDNSAppleMusicUsageDescription- Apple MusicNSSiriUsageDescription- SiriNSUserTrackingUsageDescription- App Tracking Transparency
Generic Info.plist Booleans (e.g. ITSAppUsesNonExemptEncryption)
Nexjax can also manage arbitrary boolean keys in Info.plist (for example, ITSAppUsesNonExemptEncryption).
From Expo app.json:
{
"expo": {
"ios": {
"infoPlist": {
"ITSAppUsesNonExemptEncryption": false,
"UIRequiresFullScreen": true
}
}
}
}From ios.config.json:
{
"infoPlist": {
"ITSAppUsesNonExemptEncryption": false,
"UIRequiresFullScreen": true
}
}Priority:
ios.config.json→infoPlist(overrides)app.json→expo.ios.infoPlist(boolean entries)
Priority Order
For all settings (version, displayName, buildNumber, etc.):
ios.config.json- Primary source (can override everything)app.json(Expo) orpackage.json(bare RN) - Source of truth- Command line argument - Overrides Team ID only
For permission strings specifically:
ios.config.json→permissions- Always works (Expo & bare RN)app.json→expo.ios.infoPlist- Expo projects only (if no ios.config.json permissions)
Note:
- If
app.jsonexists, it takes priority overpackage.jsonfor version/name - If no
app.json, nexjax reads frompackage.json(bare RN projects) - Permission strings work the same way:
ios.config.jsonalways works,app.jsonis optional (Expo only)
Usage Examples
Basic Usage
# Configure with Team ID
npx nexjax YOUR_TEAM_ID
# Re-configure (uses saved Team ID from config)
npx nexjaxWith npm scripts
{
"scripts": {
"prebuild:ios": "cd ios && pod install",
"postbuild:ios": "npx nexjax",
"rebuild:ios": "npm run prebuild:ios && npm run postbuild:ios"
}
}With Expo
{
"scripts": {
"prebuild:ios": "npx expo prebuild --platform ios --clean",
"postbuild:ios": "npx nexjax",
"rebuild:ios": "npm run prebuild:ios && npm run postbuild:ios"
}
}What It Does
1. Team ID Configuration
- Updates
DEVELOPMENT_TEAMinios/*.xcodeproj/project.pbxproj - Preserves across rebuilds
2. Version & Build Number
- Updates
CFBundleShortVersionStringinInfo.plist - Updates
CFBundleVersioninInfo.plist
3. Display Name
- Updates
CFBundleDisplayNameinInfo.plist
4. iPhone-Only
- Removes
UISupportedInterfaceOrientations~ipadfromInfo.plist - Ensures app only runs on iPhone
5. Permission Strings (Optional)
- Updates permission strings in
Info.plist - Reads from
app.json(Expo) orios.config.json(bare RN) - Only configures permissions that are provided (all optional)
Project Structure
Expo Projects:
your-project/
├── app.json ← Source of truth (version, name, buildNumber)
├── package.json
├── ios/ ← Generated by expo prebuild
│ ├── YourApp.xcodeproj/
│ │ └── project.pbxproj
│ └── YourApp/
│ └── Info.plist
└── ios.config.json ← Created automatically (Team ID, overrides)Bare React Native Projects:
your-project/
├── package.json ← Source of truth (version, name)
├── ios/
│ ├── YourApp.xcodeproj/
│ │ └── project.pbxproj
│ └── YourApp/
│ └── Info.plist
└── ios.config.json ← Created automatically (Team ID, buildNumber, overrides)Troubleshooting
"ios directory not found"
- Make sure you're in the React Native project root
- Run from the directory containing
ios/folder
"Xcode project not found"
- Make sure you've built the iOS project at least once
- For Expo: Run
npx expo prebuild --platform iosfirst
"Info.plist not found"
- The tool will search for Info.plist automatically
- If not found, Team ID configuration will still work
Team ID not persisting
- Make sure
ios.config.jsonhasteamIdset - Run the tool again after rebuilding iOS project
API
Programmatic Usage
const { configureIOS } = require('nexjax');
// Configure with Team ID
await configureIOS('/path/to/project', 'YOUR_TEAM_ID');
// Re-configure (uses config file)
await configureIOS('/path/to/project');License
MIT
Contributing
Contributions welcome! This is a simple tool designed to be lightweight and dependency-free.
