Troubleshooting
Common issues and fixes for the SwiftPatch SDK.
Update Not Applying
Symptoms: Update shows as available but doesn't apply after restart.
- Verify you're running a release build, not debug
- Check that the deployment key is correct
- Confirm the AppDelegate/MainApplication loads the SwiftPatch bundle
- Make sure the target binary version matches your app version
Update Download Fails
Symptoms: Download starts but fails or times out.
- Check network connectivity
- Verify the device can reach your SwiftPatch API server
- Check for proxy or firewall issues
- Enable debug mode for detailed logs:
export default withSwiftPatch(App, {
debug: true,
});
App Crashes After Update
Symptoms: App crashes on launch after an OTA update, or enters a restart loop.
1. Missing --hermes flag (most common)
If your app uses Hermes (React Native's JavaScript engine that runs precompiled bytecode, enabled by default since React Native 0.70+), you must deploy with --hermes. A plain JS bundle crashes immediately because the Hermes VM expects compiled bytecode.
# Wrong -- crashes Hermes apps
swiftpatch deploy -p ios
# Correct
swiftpatch deploy -p ios --hermes
This is the number one cause of post-update crashes. Always use --hermes if Hermes is enabled.
2. Crash detection and auto-rollback
SwiftPatch rolls back after 2 crashes within 10 seconds (both configurable). If auto-rollback isn't working:
- Make sure
withSwiftPatchwraps your entire app - The SDK tracks crashes within
crashDetectionWindowMs(default: 10s) - After
maxCrashesBeforeRollbackcrashes (default: 2), the bundle reverts - After 3+ consecutive auto-rollbacks, the SDK falls back to the original app store bundle
- For apps with heavy initialization, increase the thresholds:
export default withSwiftPatch(App, {
crashDetectionWindowMs: 15000, // 15s window
maxCrashesBeforeRollback: 3,
});
3. Emergency: Disable the release
If users are affected, disable the release via the API:
curl -s -X POST 'https://api.swiftpatch.io/v1/cli/ci/update-release' \
-H 'x-ci-token: YOUR_CI_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"projectId":"YOUR_APP_ID","hash":"BUNDLE_HASH","appVersion":"VERSION","isDisabled":true}'
Update Not Found (Version Mismatch)
Symptoms: SDK logs No matching release found. Update was deployed but users don't receive it.
The --app-version in swiftpatch deploy must exactly match the native binary version.
- iOS:
MARKETING_VERSIONin Xcode /CFBundleShortVersionStringin Info.plist - Android:
versionNameinbuild.gradle
If your iOS app reports 42.0, deploy with --app-version 42.0 -- not 42.0.0. The API does an exact string match.
Check your native binary version:
# iOS
grep 'MARKETING_VERSION' ios/YourApp.xcodeproj/project.pbxproj | head -1
# Android
grep 'versionName' android/app/build.gradle
Hash Mismatch Error
Symptoms: Error: "Bundle hash mismatch"
- The bundle may have been corrupted during download
- If using signing, ensure the SDK public key matches the private key used to sign
- The SDK rejects mismatched bundles automatically
Bundle Signing Verification Fails
Symptoms: Update rejected with a signature verification error.
- Verify the public key matches the private key used in
deploy --private-key - Ensure the key pair was generated correctly:
swiftpatch generate-key-pair -o ./keys
- Check that
.swiftpatchsignedJWT file is included in the bundle ZIP
Slot State Issues
The SDK uses three slots:
- DEFAULT_SLOT -- Original bundle from the app binary
- STABLE_SLOT -- Verified OTA bundle
- NEW_SLOT -- Newly installed bundle, pending verification
Check the current slot:
const { currentBundle } = useSwiftPatch();
console.log('Slot:', currentBundle?.slot);
Native Config Not Found
Symptoms: SDK initializes but doesn't pick up credentials from Info.plist or strings.xml.
Step 1: Verify with getNativeConfig()
import { getNativeConfig } from '@swiftpatch/react-native';
const config = await getNativeConfig();
console.log('App ID:', config.appId);
Step 2: Check the Details
- Verify key spelling:
SwiftPatchAppId(exact match, case-sensitive) - iOS: The key must be in
Info.plist, not an.xcconfigfile - Android: The key must be in
res/values/strings.xml
Error Codes
| Code | Description | Solution |
|---|---|---|
NETWORK_ERROR | Cannot reach the update server | Check internet and serverUrl |
DOWNLOAD_ERROR | Download failed | Check disk space and network; may retry automatically |
VERIFICATION_ERROR | Bundle integrity check failed | Check for corruption or mismatched signing key |
PATCH_ERROR | Differential patch failed | SDK falls back to full download on next attempt |
INSTALL_ERROR | Installation failed | Check disk space; try clearPendingUpdate() |
CONFIG_ERROR | Invalid configuration | Check deploymentKey and native config |
STABILIZE_ERROR | Bundle stabilization failed | Check slot metadata |
ENVIRONMENT_ERROR | Environment switch failed | Use valid EnvironmentMode (PROD or STAGE) |
UNKNOWN_ERROR | Unexpected error | Enable debug: true and check native logs |
Using getErrorDescription()
import { getErrorDescription, SwiftPatchErrorCode } from '@swiftpatch/react-native';
const { description, troubleshooting } = getErrorDescription(
SwiftPatchErrorCode.NETWORK_ERROR
);
console.log(description);
console.log(troubleshooting);
Debug Logging
Step 1: Enable Verbose Logs
export default withSwiftPatch(App, {
debug: true,
});
Step 2: View Native Logs
# iOS
xcrun simctl spawn booted log show --predicate 'subsystem == "com.swiftpatch"' --last 5m
# Android
adb logcat -s SwiftPatch
Getting Help
- Check the FAQ
- Search GitHub Issues
- Email support: support@swiftpatch.io