Overview
The Xcode Sandbox provides a cloud-based Xcode build environment connected to an iOS simulator. You can sync your local source code, trigger builds, and see results in real-time with hot reload support.
Creating a Sandbox Client
From an iOS Instance
When using an iOS instance with Xcode sandbox enabled: import Limrun from '@limrun/api' ;
import { createXCodeSandboxClient } from '@limrun/api' ;
const client = new Limrun ({
apiKey: process . env . LIM_API_KEY ,
});
const instance = await client . iosInstances . create ({
wait: true ,
spec: {
sandbox: {
xcode: {
enabled: true ,
},
},
},
});
const sandboxClient = await createXCodeSandboxClient ({
apiUrl: instance . status . sandbox . xcode . url ! ,
token: process . env . LIM_API_KEY ,
});
When using an iOS instance, the simulator is already configured.
Standalone Sandbox (Advanced)
When using a standalone sandbox, you need to configure the simulator: const sandboxClient = await createXCodeSandboxClient ({
apiUrl: 'https://sandbox.example.com' ,
token: 'sandbox-token' ,
simulator: {
apiUrl: 'https://simulator.example.com' ,
token: 'simulator-token' , // Optional, defaults to sandbox token
},
});
Syncing Source Code
Sync your local iOS project to the sandbox:
One-Time Sync
const result = await sandboxClient . sync ( './my-ios-app' );
console . log ( 'Source code synced' );
Watch Mode (Hot Reload)
Automatically re-sync when files change:
const result = await sandboxClient . sync ( './my-ios-app' , {
watch: true ,
});
console . log ( 'Watching for changes...' );
// Stop watching when done
if ( result . stopWatching ) {
result . stopWatching ();
}
Sync Options
const result = await sandboxClient . sync ( './my-ios-app' , {
// Enable watch mode
watch: true ,
// Custom cache key for delta basis
cacheKey: 'my-app-development' ,
// Custom cache directory
basisCacheDir: './.limsync-cache' ,
// Maximum patch size in bytes
maxPatchBytes: 10 * 1024 * 1024 , // 10MB
// Custom file filter
filter : ( relativePath ) => {
// Exclude test files
if ( relativePath . includes ( 'Tests/' )) return false ;
// Include everything else
return true ;
},
// Custom logging
log : ( level , msg ) => {
console . log ( `[ ${ level } ] ${ msg } ` );
},
});
Default Exclusions
The following are automatically excluded:
Build outputs: build/, .build/, DerivedData/
Index files: Index.noindex/, ModuleCache.noindex/, .index-build/
Dependencies: .swiftpm/, Pods/, Carthage/Build/
Version control: .git/
System files: .DS_Store
Debug symbols: .dSYM/
User data: xcuserdata/
Sync cache: .limsync-cache/
Building with Xcode
Trigger an xcodebuild command and stream output:
Basic Build
const build = sandboxClient . xcodebuild ();
// Stream output line by line
build . stdout . on ( 'data' , ( line ) => {
console . log ( '[build]' , line );
});
build . stderr . on ( 'data' , ( line ) => {
console . error ( '[error]' , line );
});
// Wait for completion
const { exitCode } = await build ;
if ( exitCode === 0 ) {
console . log ( 'Build succeeded!' );
} else {
console . error ( 'Build failed with code:' , exitCode );
}
Build Configuration
const build = sandboxClient . xcodebuild ({
workspace: 'MyApp.xcworkspace' ,
scheme: 'MyApp' ,
});
await build ;
const build = sandboxClient . xcodebuild ({
project: 'MyApp.xcodeproj' ,
scheme: 'MyApp' ,
});
await build ;
Complete Development Workflow
Setup
Create instance and sandbox client: import Limrun from '@limrun/api' ;
import { createXCodeSandboxClient } from '@limrun/api' ;
const client = new Limrun ({
apiKey: process . env . LIM_API_KEY ,
});
const instance = await client . iosInstances . create ({
wait: true ,
spec: {
sandbox: { xcode: { enabled: true } },
},
});
const sandbox = await createXCodeSandboxClient ({
apiUrl: instance . status . sandbox . xcode . url ! ,
token: process . env . LIM_API_KEY ,
});
Sync Code with Watch Mode
const syncResult = await sandbox . sync ( './MyiOSApp' , {
watch: true ,
});
console . log ( 'Source code synced, watching for changes...' );
Initial Build
const build = sandbox . xcodebuild ({
workspace: 'MyApp.xcworkspace' ,
scheme: 'MyApp' ,
});
build . stdout . on ( 'data' , ( line ) => console . log ( line ));
build . stderr . on ( 'data' , ( line ) => console . error ( line ));
const { exitCode } = await build ;
console . log ( 'Initial build finished:' , exitCode === 0 ? 'success' : 'failed' );
Develop with Hot Reload
// Make changes to your source code locally
// The sandbox automatically syncs changes and rebuilds
console . log ( 'Watching for file changes...' );
console . log ( 'Edit your source files and see builds triggered automatically' );
// Keep the process running
await new Promise (() => {});
Clean Up
// Stop watching
if ( syncResult . stopWatching ) {
syncResult . stopWatching ();
}
// Delete instance
await client . iosInstances . delete ( instance . metadata . id );
Advanced: Custom File Filtering
Implement complex filtering logic:
const result = await sandbox . sync ( './MyApp' , {
filter : ( relativePath ) => {
// Exclude specific directories
if ( relativePath . startsWith ( 'Pods/' )) return false ;
if ( relativePath . startsWith ( 'build/' )) return false ;
// Include only Swift and Objective-C files in src/
if ( relativePath . startsWith ( 'src/' )) {
return relativePath . endsWith ( '.swift' ) ||
relativePath . endsWith ( '.m' ) ||
relativePath . endsWith ( '.h' );
}
// Include all other files
return true ;
},
});
Advanced: Multiple Builds
Trigger multiple build configurations:
// Build for simulator
const simBuild = sandbox . xcodebuild ({
workspace: 'MyApp.xcworkspace' ,
scheme: 'MyApp-Simulator' ,
});
simBuild . stdout . on ( 'data' , ( line ) => {
console . log ( '[simulator]' , line );
});
await simBuild ;
// Build for device (if supported)
const deviceBuild = sandbox . xcodebuild ({
workspace: 'MyApp.xcworkspace' ,
scheme: 'MyApp-Device' ,
});
deviceBuild . stdout . on ( 'data' , ( line ) => {
console . log ( '[device]' , line );
});
await deviceBuild ;
Logging
Control sandbox client verbosity:
const sandbox = await createXCodeSandboxClient ({
apiUrl: sandboxUrl ,
token: apiKey ,
logLevel: 'debug' , // 'none' | 'error' | 'warn' | 'info' | 'debug'
});
Log levels:
none: No logging
error: Only errors
warn: Warnings and errors
info: General info, warnings, and errors (default)
debug: All messages including sync details
Error Handling
Sync Errors
Build Errors
Watch Mode Cleanup
try {
await sandbox . sync ( './MyApp' );
} catch ( err ) {
console . error ( 'Failed to sync:' , err . message );
}
Delta Sync Optimization
The sync operation uses delta compression to minimize upload size:
First sync : Full upload of all files
Subsequent syncs : Only changed files uploaded
Basis caching : Previous state cached locally for delta calculation
Patch mode : If delta is small enough, sends patch instead of full files
// First sync - uploads all files
await sandbox . sync ( './MyApp' );
// Change a single file
// Second sync - only uploads the changed file
await sandbox . sync ( './MyApp' );
Use watch mode during development to automatically sync changes and trigger rebuilds.
The sandbox client automatically handles file filtering to exclude build artifacts and dependencies.
Ensure your local project structure matches what Xcode expects. The sandbox runs xcodebuild in the synced directory.