Complete guide for building, installing, and deploying separate Service Agent and Employee Agent apps from a single codebase
- Overview
- App Types
- Quick Start
- Installation
- Building & Running
- Project Structure
- How It Works
- Benefits
- Troubleshooting
- FAQ
- Implementation Details
This repository supports building two separate apps from a single codebase:
- Service Agent - Customer-facing, anonymous auth, no Mobile SDK
- Employee Agent - Internal workforce, OAuth auth, includes Mobile SDK
Both apps share >98% of code (JavaScript/TypeScript and core native code), with only configuration files differing.
- Purpose: Customer-facing, public service app
- Authentication: Anonymous (URL-based configuration)
- Display Name: "Service Agent"
- Mobile SDK: NOT included
- Purpose: Internal workforce app
- Authentication: OAuth via Salesforce Mobile SDK
- Display Name: "Employee Agent"
- Mobile SDK: Included (SalesforceSDKCore 13.1.1 from CocoaPods)
iOS:
- Xcode 15.0 or later
- CocoaPods
- XcodeGen (
brew install xcodegen) - Node.js >= 18
Android:
- Android Studio or Android SDK
- Gradle
- Node.js >= 18
# Service Agent only (fastest)
node installios.js service
node installandroid.js service
npm run ios:service
npm run android:service
# Employee Agent only
node installios.js employee
node installandroid.js employee
npm run ios:employee
npm run android:employee
# Both apps (backward compatible)
node installios.js all
node installandroid.js allChoose which app to install:
# Service Agent Only (no Mobile SDK)
node installios.js service
node installandroid.js service
# Employee Agent Only (with Mobile SDK)
node installios.js employee
node installandroid.js employee
# Both Apps (backward compatible default)
node installios.js all
node installandroid.js all
# No argument = 'all' (backward compatible)
node installios.js # Same as 'all'
node installandroid.js # Same as 'all'| Target | Mobile SDK | CocoaPods/Gradle |
|---|---|---|
| service | ❌ Not included | Service Agent only |
| employee | ✅ From Maven/CocoaPods | Employee Agent only |
| all | ✅ From Maven/CocoaPods | Both targets |
What each script does:
- Installs npm dependencies (always)
- [employee/all only] Builds react-native-force (Mobile SDK React Native bridge)
- Configures Node.js path for Xcode (always)
- Generates Xcode project with xcodegen (always)
- Installs CocoaPods (only for selected target)
- Installs npm dependencies (always)
- Applies React Native Gradle plugin patches (always)
- [employee/all only] Builds react-native-force (Mobile SDK React Native bridge)
Employee Agent uses Salesforce Mobile SDK from published artifacts:
- iOS: CocoaPods specs (
SalesforceSDKCoreviaReactNativeAgentforce/WithMobileSDK) - Android: Maven Central (
com.salesforce.mobilesdk:SalesforceReact:13.1.1)
Service Agent has no Mobile SDK dependency.
# Service Agent
npm run ios:service
npm run android:service
# Employee Agent
npm run ios:employee
npm run android:employee# Service Agent Release
npm run build:ios:service
npm run build:android:service
# Employee Agent Release
npm run build:ios:employee
npm run build:android:employeeiOS (Xcode):
- Open
ios/ReactAgentforce.xcworkspace(NOT .xcodeproj) - Select scheme: ServiceAgent or EmployeeAgent
- Select target device
- Build and run (Cmd+R)
Android (Gradle):
cd android
# List all available build variants
./gradlew tasks | grep assemble
# Build specific variants
./gradlew assembleServiceAgentDebug
./gradlew assembleServiceAgentRelease
./gradlew assembleEmployeeAgentDebug
./gradlew assembleEmployeeAgentReleaseAndroid APKs:
android/app/build/outputs/apk/
├── serviceAgent/
│ ├── debug/app-serviceAgent-debug.apk
│ └── release/app-serviceAgent-release.apk
└── employeeAgent/
├── debug/app-employeeAgent-debug.apk
└── release/app-employeeAgent-release.apk
iOS IPAs:
- Open Xcode
- Window → Organizer
- Select archive
- Distribute App → Export
ios/
├── project.yml # XcodeGen configuration
├── Podfile # Default: loads Podfile.employee (run pod install via installios.js)
├── Podfile.service # Service Agent only (subset of deps)
├── Podfile.employee # Employee Agent / both apps (full deps)
├── Podfile.common.rb # Shared pod list and hooks
├── Podfile.service.lock # Lock for Service Agent
├── Podfile.employee.lock # Lock for Employee Agent / both
├── Shared/ # Shared code (AppDelegate, main.m)
│ ├── AppDelegate.{h,m}
│ └── main.m
├── ServiceAgent/ # Service Agent specific files
│ ├── Info.plist # Display name: "Service Agent"
│ ├── ServiceAgent.entitlements
│ ├── LaunchScreen.storyboard
│ └── PrivacyInfo.xcprivacy
└── EmployeeAgent/ # Employee Agent specific files
├── Info.plist # Display name: "Employee Agent"
├── EmployeeAgent.entitlements
├── LaunchScreen.storyboard
└── PrivacyInfo.xcprivacy
android/app/src/
├── main/ # Shared code
│ ├── java/.../app/
│ │ ├── MainActivity.java
│ │ └── MainApplication.java
│ ├── res/
│ └── AndroidManifest.xml
├── serviceAgent/ # Service Agent overrides
│ └── res/values/
│ └── strings.xml # app_name="Service Agent"
└── employeeAgent/ # Employee Agent overrides
└── res/values/
└── strings.xml # app_name="Employee Agent"
All JavaScript/TypeScript code is 100% shared between both apps. No changes needed.
Single Project, Two Targets (XcodeGen + CocoaPods):
This project uses a single Xcode project with two targets approach, which provides:
- Platform consistency: Matches Android's product flavor architecture
- Maximum code reuse: >98% shared code, minimal duplication
- Selective installation: Install service-only or employee with Mobile SDK
- React Native CLI compatibility: Works seamlessly with standard RN tooling
Alternative Considered: Separate projects (one for each app) with independent project.yml/Podfile per app was evaluated but rejected because it would:
- Double disk space and build time (2x Pods directories)
- Break React Native CLI conventions (expects single ios/ directory)
- Create platform inconsistency (Android uses flavors, not separate projects)
- Duplicate most configuration for minimal benefit
Architecture Details:
project.ymldefines two targets (ServiceAgent, EmployeeAgent)xcodegen generatecreates.xcodeprojwith both targets- Two Podfiles:
Podfile.service(Service Agent only, subset) andPodfile.employee(both targets, full deps).installios.jscopies the right one toPodfileand uses the matching lock (Podfile.service.lockorPodfile.employee.lock). "all" uses the employee Podfile so both apps share one dependency set. - Build order fix:
Podfile.common.rbadds explicit Pods target dependencies at the Xcode project level, ensuring Pods build before app targets (fixes "React/RCTBridgeDelegate.h file not found" errors) - ServiceAgent uses
ReactNativeAgentforce/Core(no Mobile SDK) - EmployeeAgent uses
ReactNativeAgentforce/WithMobileSDK(includes Mobile SDK)
Mobile SDK Dependencies:
# EmployeeAgent target
pod 'SalesforceReact', :path => '../node_modules/react-native-force'
pod 'ReactNativeAgentforce/WithMobileSDK', :path => '../AgentforceSDK-ReactNative-Bridge/ios'
# WithMobileSDK subspec brings in SalesforceSDKCore from published specsBuild Order Solution:
The Podfile.common.rb includes an add_pods_target_dependency function that automatically adds explicit dependencies from each app target (ServiceAgent, EmployeeAgent) to its corresponding Pods target (Pods-ServiceAgent, Pods-EmployeeAgent). This ensures CocoaPods builds first, making React Native headers available during app compilation.
Product Flavors + Gradle:
build.gradledefines two flavors:serviceAgent,employeeAgent- Creates 4 build variants (service/employee × debug/release)
- ServiceAgent flavor: NO Mobile SDK dependency
- EmployeeAgent flavor: Mobile SDK from Maven Central
Mobile SDK Dependency:
// build.gradle - Employee Agent only
employeeAgentImplementation("com.salesforce.mobilesdk:SalesforceReact:13.1.1")The bridge automatically detects Mobile SDK availability:
Android (AgentforcePackage.kt):
private fun isMobileSdkAvailable(): Boolean {
return try {
Class.forName("com.salesforce.androidsdk.app.SalesforceSDKManager")
true
} catch (e: ClassNotFoundException) {
false
}
}iOS (Subspecs):
Coresubspec: Service Agent only, no Mobile SDKWithMobileSDKsubspec: IncludesSalesforceSDKCoredependency
Employee Agent requires SDK initialization at app startup:
Android (Conditional in Shared MainApplication):
// android/app/src/main/java/.../MainApplication.java
public void onCreate() {
super.onCreate();
// Initialize Mobile SDK for Employee Agent flavor only (uses reflection)
if ("employeeAgent".equals(BuildConfig.FLAVOR)) {
Class<?> sdkManagerClass = Class.forName("com.salesforce.androidsdk.app.SalesforceSDKManager");
Method initMethod = sdkManagerClass.getMethod("initNative", Context.class, Class.class);
initMethod.invoke(null, this, MainActivity.class);
}
// ... rest of initialization
}iOS (Flavor-Specific AppDelegate):
// ios/EmployeeAgent/AppDelegate.m (overrides Shared/AppDelegate.m)
#import <SalesforceReact/SalesforceReactSDKManager.h>
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[SalesforceReactSDKManager initializeSDK];
// ... rest of initialization
}Configuration Files:
- Android:
android/app/src/employeeAgent/res/values/bootconfig.xml - iOS:
ios/EmployeeAgent/bootconfig.plist
These files contain OAuth client configuration for Mobile SDK authentication.
- Both installable side-by-side
- Independent release cycles
- Clear separation of concerns
-
98% shared code
- Single codebase maintenance
- Consistent features across apps
- Install only what you need (service/employee/all)
- Service Agent: No Mobile SDK required
- Employee Agent: Full Mobile SDK from published artifacts
- Service Agent devs don't need to understand Mobile SDK
- Employee Agent devs get full Mobile SDK integration
- Flexible workflow for different development needs
Cause: The app target is not using the CocoaPods-generated xcconfig, or Pods are not building before the app target. This typically happens when:
- You run
xcodegen generateafterpod install– XcodeGen overwrites the project and removes thebaseConfigurationReferenceto the Pods xcconfig that CocoaPods added. - You open the
.xcodeprojinstead of the.xcworkspace– Always openios/ReactAgentforce.xcworkspaceso the Pods project and settings are in scope. - You're building the wrong target – If you ran
node installios.js service, only the ServiceAgent target has Pods linked. Building EmployeeAgent will fail with missing React headers until you runnode installios.js employeeornode installios.js all. - Pods target dependency not set – The app target needs an explicit dependency on its Pods target to ensure correct build order.
Solution:
-
Use the correct install order and do not re-run XcodeGen after Pods are installed:
node installios.js service # or employee, or allThis runs
xcodegen generatethenpod install. CocoaPods then:- Wires each app target to its
Pods-<Target>.(debug|release).xcconfig - Runs the
post_installhook which adds explicit Pods target dependencies
If you need to change
project.yml, run the full install again so that XcodeGen runs first and then CocoaPods re-applies the xcconfig references and dependencies. - Wires each app target to its
-
Always open the workspace:
open ios/ReactAgentforce.xcworkspace
In Xcode, select the ServiceAgent or EmployeeAgent scheme (matching what you installed) and build.
-
If you already ran XcodeGen after pod install, re-run the installer so CocoaPods can re-attach the xcconfig and dependencies:
node installios.js service # or employee / all -
Verify Pods target dependencies: After running
installios.js, check the output for:✅ Added ServiceAgent -> Pods-ServiceAgent target dependency ✅ Added EmployeeAgent -> Pods-EmployeeAgent target dependencyThis confirms the build order is properly configured.
-
UIKit / CoreGraphics / Foundation “module not found” – Often fixed by the Podfile
post_installsettings (CLANG_ENABLE_MODULES,CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES). If you still see this, ensure you are building with the workspace and the correct scheme.
Cause: The app target doesn't have an explicit dependency on its Pods target.
Solution: This is now handled automatically by installios.js. The Podfile.common.rb post_install hook adds explicit dependencies from each app target to its Pods target. If you're still seeing issues:
# Clean and reinstall
rm -rf ios/Pods ios/Podfile.lock ios/ReactAgentforce.xcodeproj ios/ReactAgentforce.xcworkspace
node installios.js service # or employee / all❌ Invalid target: xyz
Solution: Use service, employee, or all:
node installios.js service # ✅ CorrectCause: Ran node installios.js service but trying to build Employee Agent
Solution: Install Employee Agent dependencies:
node installios.js employee
node installandroid.js employeeCause: CocoaPods or Gradle cache corruption
Solution: Clean and reinstall:
cd ios && pod deintegrate && pod install
cd android && ./gradlew cleanSolution:
cd ios
xcodegen generate
pod installCause: Ran node installios.js service but Podfile expects Employee target
Solution: This is expected - Service Agent install skips Employee pods:
npm run ios:service # Build Service Agent onlyCause: Corrupted build cache
Solution: Clean and reinstall:
rm -rf android/build android/app/build
node installandroid.js employeeThese warnings are cosmetic and don't affect functionality.
A: Yes! Just run the install script again with the new target:
node installios.js service # Install Service Agent only
# ... do Service Agent work ...
node installios.js employee # Add Employee Agent + Mobile SDKA: No, all JavaScript/TypeScript code is 100% shared between apps. This only affects native dependencies.
A: Yes! Both apps can be installed side-by-side on the same device.
A: Depends on your pipeline:
- Separate pipelines → Use specific targets (
serviceoremployee) - Monolithic pipeline → Use
all
Why Single Project with Two Targets?
This project uses a single Xcode project with two targets approach (not separate projects). This decision was made after careful analysis:
Advantages:
- ✅ Platform consistency: Matches Android's product flavor architecture
- ✅ Maximum code reuse: >98% shared code between targets
- ✅ Single build graph: Pods installed once, shared between targets
- ✅ React Native CLI compatibility: Standard RN tooling expects single ios/ directory
- ✅ Faster builds: Single Pods directory, no duplication
- ✅ Easier maintenance: One project.yml, one set of build settings
Alternative Considered (Rejected): Separate projects (ServiceAgent-ios/, EmployeeAgent-ios/) with independent configurations were evaluated but would introduce:
- ❌ 2x disk space (separate Pods directories)
- ❌ 2x pod install time
- ❌ Platform inconsistency (Android uses flavors, not separate gradle projects)
- ❌ React Native CLI incompatibility (expects single ios/ directory)
- ❌ Configuration duplication (most settings identical)
The Current Issues (Now Fixed):
Build order problems requiring scheme patching workaround→ Fixed with proper Pods target dependenciesPodfile switching complexity→ Improved with clear logging and validationTwo lock files to maintain separately→ Improved with explicit lock file managementLimited validation and unclear error messages→ Fixed with pre-flight checks and verbose logging
- Multi-target iOS setup (XcodeGen + CocoaPods)
- Product flavors Android setup (Gradle)
- Selective installation (service/employee/all arguments)
- Conditional dependency resolution (flavor-specific dependencies)
- Published artifacts integration (Maven Central & CocoaPods specs)
- Build order fix (Explicit Pods target dependencies via post_install hook)
- Environment validation (Pre-flight checks for required tools)
- Verbose logging (Clear progress indicators and step-by-step feedback)
ios/project.yml- XcodeGen configurationios/ServiceAgent/- Service Agent specific filesios/EmployeeAgent/- Employee Agent specific filesAppDelegate.m- Overrides Shared/AppDelegate.m with Mobile SDK initializationbootconfig.plist- OAuth configuration
ios/Shared/- Shared iOS code (AppDelegate.m used by Service Agent)android/app/src/serviceAgent/- Service Agent flavor (uses shared MainApplication)android/app/src/employeeAgent/- Employee Agent flavorres/values/bootconfig.xml- OAuth configuration (uses shared MainApplication with conditional init)
scripts/generate-app-config.js- App mode configuration generatorscripts/build-react-native-force.js- Builds react-native-force bridge
package.json- Added run scripts, react-native-force dependency, build:force scriptinstallios.js- Added selective installation logic + react-native-force buildinstallandroid.js- Added selective installation logic + react-native-force buildios/Podfile.service- Service Agent only (subset of deps)ios/Podfile.employee- Both targets, full deps (used for employee and "all")ios/Podfile.common.rb- Shared pod list and post_install hooks- Locks:
Podfile.service.lockandPodfile.employee.lock(install script copies the active one toPodfile.lockand back) android/settings.gradle- Simplified project structureandroid/app/build.gradle- Product flavors + conditional depssrc/config/AppConfig.ts- Dynamic app mode configuration.gitignore- Added generated config file
iOS:
- XcodeGen: Makes project structure version-controllable (YAML)
- Two Podfiles + locks: Install script selects Podfile.service or Podfile.employee and the matching lock
- Published specs: Uses SalesforceReact from npm + CocoaPods specs
Android:
- Product flavors: Standard Android approach for app variants
- Flavor-specific dependencies:
employeeAgentImplementationonly for Employee Agent
Both:
- Backward compatible: No argument =
all(same as before) - Idempotent scripts: Can run multiple times safely
- Published artifacts: Uses Maven/CocoaPods for Mobile SDK
| Component | Shared % |
|---|---|
| JavaScript/TypeScript | 100% |
| iOS native (AppDelegate, main) | 100% |
| Android native (MainActivity, Application) | 100% |
| iOS configuration (Info.plist, entitlements) | 0% (app-specific) |
| Android configuration (strings.xml) | 0% (app-specific) |
| Overall | >98% |
| Aspect | Service Agent | Employee Agent |
|---|---|---|
| Mobile SDK | ❌ Not included | ✅ From Maven/CocoaPods |
| Authentication | Anonymous | OAuth |
| Use case | Customer-facing | Internal workforce |
node installios.js service
node installandroid.js servicenode installios.js employee
node installandroid.js employeenode installios.js all
node installandroid.js all
# Default, backward compatibleservice-pipeline:
script:
- node installios.js service
- npm run build:ios:service
employee-pipeline:
script:
- node installios.js employee
- npm run build:ios:employeefull-pipeline:
script:
- node installios.js all
- npm run build:ios:service
- npm run build:ios:employee- Main README:
../README.md- Project overview - Bridge Documentation:
../AgentforceSDK-ReactNative-Bridge/README.md - Contributing:
../CONTRIBUTING.md
This guide covered:
- ✅ Installing Service Agent and Employee Agent separately
- ✅ Building and running both apps
- ✅ Understanding the multi-app architecture
- ✅ Troubleshooting common issues
- ✅ Optimizing for time and disk space
Choose the right install target and save time! 🚀
Last Updated: March 3, 2026