Android Setup
The automatic way is one command. The manual way is a short snippet you paste into MainApplication. Both are documented here.
The automatic path
npx swiftpatch init
That's it. Move on to wrapping your app.
Under the hood, swiftpatch init modifies two Android files. Every change is bounded by // swiftpatch-begin / // swiftpatch-end markers, so swiftpatch unlink reverses them cleanly.
MainApplication.kt (Kotlin — RN 0.73+)
import com.swiftpatch.SwiftPatchModule
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
// ... your existing packages, dev support, etc.
// swiftpatch-begin (auto-generated, do not edit between markers)
override fun getJSBundleFile(): String? {
return SwiftPatchModule.getJSBundleFile(applicationContext)
?: super.getJSBundleFile()
}
// swiftpatch-end
}
}
For RN 0.82+ projects using ReactHost:
override val reactHost: ReactHost by lazy {
getDefaultReactHost(
context = applicationContext,
packageList = PackageList(this).packages,
// swiftpatch-begin (auto-generated, do not edit between markers)
jsBundleFilePath = SwiftPatchModule.getJSBundleFile(applicationContext),
// swiftpatch-end
)
}
The SDK's override calls super.getJSBundleFile() if no OTA bundle is available. This is critical — if Swiftpatch has nothing to serve, the default store bundle loader runs unchanged.
MainApplication.java (legacy)
import com.swiftpatch.SwiftPatchModule;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new DefaultReactNativeHost(this) {
// ... your existing overrides
// swiftpatch-begin (auto-generated, do not edit between markers)
@Override
protected String getJSBundleFile() {
String path = SwiftPatchModule.getJSBundleFile(getApplicationContext());
if (path != null) return path;
return super.getJSBundleFile();
}
// swiftpatch-end
};
}
strings.xml
<resources>
<string name="app_name">YourApp</string>
<!-- swiftpatch-begin (auto-generated, do not edit between markers) -->
<string name="swiftpatch_deployment_key">dep_xxx...</string>
<!-- swiftpatch-end -->
</resources>
Android uses the snake-case form swiftpatch_deployment_key. iOS uses the PascalCase form SwiftPatchDeploymentKey. The SDK reads from both.
Manual setup
If auto-patching can't run, use --manual:
npx swiftpatch init --manual
The CLI prints the snippets. Steps to apply by hand:
1. Paste the getJSBundleFile override
From the Kotlin / Java example above.
2. Add the deployment key to strings.xml
<string name="swiftpatch_deployment_key">dep_xxx...</string>
3. (Optional) Wire silent-push-triggered sync
If you want FCM data messages to trigger background update checks:
override fun onMessageReceived(message: RemoteMessage) {
if (message.data["swiftpatch_event"] != null) {
SilentPushReceiver.handleData(this, message.data)
return
}
// ... your existing handler
}
See Background sync.
Minimum Android version
API 24 (Android 7.0) or higher. WorkManager (used for scheduled background sync) requires 14+, which is always true on API 24. Modern React Native templates default to API 24.
ProGuard / R8
The SDK ships its own consumer ProGuard rules. No manual setup required.
If you've heavily customized your proguard-rules.pro, ensure the following packages are not obfuscated:
-keep class com.swiftpatch.** { *; }
-keepnames class com.swiftpatch.**
New Architecture
Fully supported. Set newArchEnabled=true in android/gradle.properties. See New Architecture.
Troubleshooting
- "Cannot resolve symbol SwiftPatchModule" — autolinking didn't pick up the package. Run
cd android && ./gradlew cleanand rebuild. - App loads the old bundle after update — the
getJSBundleFile()override is missing or misplaced. Runswiftpatch doctorto verify. - WorkManager crash on boot — you're on a very old minSdkVersion. Raise to 24.
For more, see Troubleshooting.
Next steps
- iOS setup — the iOS equivalents.
- Provider reference — wrap your app.
- Background sync — WorkManager + silent push.