Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
// Gradle script for detached apps.
void runBefore(String dependentTaskName, Task task) {
Task dependentTask = tasks.findByPath(dependentTaskName);
if (dependentTask != null) {
dependentTask.dependsOn task
afterEvaluate {
def expoRoot = file("../../")
def inputExcludes = ["android/**", "ios/**"]
task exponentPrebuildStep(type: Exec) {
workingDir expoRoot
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine "cmd", "/c", ".\\android\\detach-scripts\\run-exp.bat"
} else {
commandLine "./android/detach-scripts/", "prepare-detached-build", "--platform", "android", expoRoot
runBefore("preBuild", exponentPrebuildStep)
// Based on
// Grab all build types and product flavors
def buildTypes = android.buildTypes.collect { type -> }
def productFlavors = android.productFlavors.collect { flavor -> }
// When no product flavors defined, use empty
if (!productFlavors) productFlavors.add("")
productFlavors.each { productFlavorName ->
buildTypes.each { buildTypeName ->
// Create variant and target names
def flavorNameCapitalized = "${productFlavorName.capitalize()}"
def buildNameCapitalized = "${buildTypeName.capitalize()}"
def targetName = "${flavorNameCapitalized}${buildNameCapitalized}"
def targetPath = productFlavorName ?
"${productFlavorName}/${buildTypeName}" :
def assetsDir = file("$buildDir/intermediates/assets/${targetPath}")
// Bundle task name for variant
def bundleExpoAssetsTaskName = "bundle${targetName}ExpoAssets"
def currentBundleTask = tasks.create(
name: bundleExpoAssetsTaskName,
type: Exec) {
description = "Expo bundle assets for ${targetName}."
// Create dirs if they are not there (e.g. the "clean" task just ran)
doFirst {
// Set up inputs and outputs so gradle can cache the result
inputs.files fileTree(dir: expoRoot, excludes: inputExcludes)
outputs.dir assetsDir
// Set up the call to exp
workingDir expoRoot
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine("cmd", "/c", ".\\android\\detach-scripts\\run-exp.bat", "bundle-assets", expoRoot, "--platform", "android", "--dest", assetsDir)
} else {
commandLine("./android/detach-scripts/", "bundle-assets", expoRoot, "--platform", "android", "--dest", assetsDir)
enabled targetName.toLowerCase().contains("release") || targetName.toLowerCase().contains("prod")
runBefore("process${targetName}Resources", currentBundleTask)

Manual standalone app update to Expo SDK 27

Make sure to update exp!

Add assetBundlePatterns to your manifest with paths to your assets folder or just the **/* wildcard:

"assetBundlePatterns": [


In xcode, go to your project settings, build phases and create a new run script phase called Bundle Expo Assets with this as the script:

set -eo pipefail

if [ "$CONFIGURATION" == "Debug" ]; then
  echo "Skipping asset bundling in debug mode."
  exit 0

pushd ${SRCROOT}/..
value=$(cat ~/.expo/PATH)
PATH="$PATH:$value" exp bundle-assets --platform ios --dest "$dest"

Make sure your manifest contains the following keys:

  • ios.publishBundlePath: "ios/<your_app_slug>/Supporting/shell-app.bundle"
  • ios.publishManifestPath: "ios/<your_app_slug>/Supporting/shell-app-manifest.json"


Copy the following files:

  • run-exp.bat and to the android/detach-scripts folder, you may also delete the other files in this directory.
  • expo.gradle to the android/app folder.

You may need to grant execute permissions to on unix systems (chmod +x android/detach-scripts/

In android/app/build.gradle remove the exponentPrebuildStep method and add apply from: 'expo.gradle'.

-task exponentPrebuildStep(type: Exec) {
-  workingDir '../'
-  if (System.getProperty('').toLowerCase().contains('windows')) {
-    commandLine 'cmd', '/c', '.\\detach-scripts\\prepare-detached-build.bat'
-  } else {
-    commandLine './detach-scripts/'
-  }
-preBuild.dependsOn exponentPrebuildStep
+apply from: 'expo.gradle'

Add the following keys to your manifest:

  • android.publishBundlePath: "android/app/src/main/assets/shell-app.bundle"
  • android.publishManifestPath: "android/app/src/main/assets/shell-app-manifest.json"

Make sure to publish a new version first and then production builds will work offline!

exp %*
value=$(cat ~/.expo/PATH)
PATH="$PATH:$value" exp "$@"

This comment has been minimized.

Copy link

commented May 26, 2018

I think this is a bit unclear. What exactly is "my manifest"?


This comment has been minimized.

Copy link

commented May 29, 2018

@iuliuvisovan I think they mean app.json in the root of your project


This comment has been minimized.

Copy link

commented Jul 24, 2018

Elsewhere, there are indications that the manifest is not the same thing as app.json. So I'm with @iuliuvisovan on this -- I don't see what or where the manifest is. I posted a question to the forum:


This comment has been minimized.

Copy link

commented May 14, 2019

For iOS, also remember to create the file references of shell-app.bundle and shell-app-manifest.json in Xcode otherwise it won't package these files in the binary and offline support won't work. I got stuck over this for a couple of days :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.