EAS Update not downloading on production build despite matching channels and successful publish
22:59 04 Feb 2026

I'm having trouble with EAS Update. The update is successfully published and visible on the Expo Dashboard, but the physical device (installed via TestFlight) is not receiving the changes.

Note on Project Environment: This is a Bare React Native project (not Managed Workflow). I am only using EAS Update for OTA updates.

What I've checked:

  • The update is listed under the correct branch/channel in the dashboard.

  • I've restarted the app multiple times.

My Configuration:

app.config.ts

import {ExpoConfig, ConfigContext} from 'expo/config';
import * as dotenv from 'dotenv';
import path from 'path';
import pkg from './package.json';

const APP_VARIANT = process.env.APP_VARIANT || 'prod';

dotenv.config({path: path.resolve(__dirname, `.env.${APP_VARIANT}`)});

interface CustomExpoConfig extends ExpoConfig {
  'react-native-google-mobile-ads'?: {
    android_app_id?: string;
    ios_app_id?: string;
  };
}

const convertVersionToNumber = (version: string) => {
  const [major, minor, patch] = version.split('.').map(Number);
  return major * 1000000 + minor * 1000 + patch + 2;
};

export default ({config}: ConfigContext): CustomExpoConfig => ({
  ...config,
  name: 'GoalWith',
  slug: 'goalwith',
  version: pkg.version,
  runtimeVersion: pkg.version,

  ios: {
    ...config.ios,
    bundleIdentifier: 'com.goalwith.goalwith',
    buildNumber: convertVersionToNumber(pkg.version).toString(),
    googleServicesFile: './ios/GoogleService-Info.plist',
  },

  android: {
    package: 'com.goalwith',
    versionCode: convertVersionToNumber(pkg.version),
  },

  extra: {
    env: process.env.ENV,
    apiUrl: process.env.API_URL,
    kakaoAppKey: process.env.KAKAO_APP_KEY,
    googleWebClientId: process.env.GOOGLE_WEB_CLIENT_ID,
    admobIdAndroid: process.env.ADMOB_ID_ANDROID,
    admobIdIos: process.env.ADMOB_ID_IOS,
    eas: {
      projectId: '8475b304-e536-458b-aa6a-6aea6e3e6939',
    },
  },

  'react-native-google-mobile-ads': {
    android_app_id: process.env.ADMOB_ID_ANDROID,
    ios_app_id: process.env.ADMOB_ID_IOS,
  },

  updates: {
    url: 'https://u.expo.dev/8475b304-e536-458b-aa6a-6aea6e3e6939',
    requestHeaders: {
      'expo-channel-name': 'production',
    },
  },
});
eas.json

{
  "build": {
    "development": {
      "channel": "dev",
      "env": {
        "APP_VARIANT": "dev"
      }
    },
    "production": {
      "channel": "production",
      "env": {
        "APP_VARIANT": "prod"
      }
    }
  }
}

AppDelegate.swift

import UIKit
import Expo
import React
import React_RCTAppDelegate
import ReactAppDependencyProvider
import GoogleSignIn
import RNBootSplash
import KakaoSDKCommon
import KakaoSDKAuth
import FirebaseCore
import EXUpdates

@main
class AppDelegate: ExpoAppDelegate {
  var window: UIWindow?

  var reactNativeDelegate: ReactNativeDelegate?
  var reactNativeFactory: RCTReactNativeFactory?

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    AppController.initializeWithoutStarting()
    FirebaseApp.configure()

    let delegate = ReactNativeDelegate()
    let factory = ExpoReactNativeFactory(delegate: delegate)
    delegate.dependencyProvider = RCTAppDependencyProvider()

    reactNativeDelegate = delegate
    reactNativeFactory = factory
    bindReactNativeFactory(factory)

    self.window = UIWindow(frame: UIScreen.main.bounds)

    factory.startReactNative(
      withModuleName: "main",
      in: self.window,
      launchOptions: launchOptions
    )

    if let rootView = self.window?.rootViewController?.view {
        RNBootSplash.initWithStoryboard("BootSplash", rootView: rootView)
    }

    GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
      if error != nil || user == nil {
        // Show the app's signed-out state.
      } else {
        // Show the app's signed-in state.
      }
    }
      if let kakaoAppKey = Bundle.main.object(forInfoDictionaryKey: "KAKAO_APP_KEY") as? String {
             KakaoSDK.initSDK(appKey: kakaoAppKey)
        } else {
             print("Warning: KAKAO_APP_KEY not found in Info.plist")
        }
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
  override func application(
        _ app: UIApplication,
        open url: URL,
        options: [UIApplication.OpenURLOptionsKey : Any] = [:]
    ) -> Bool {
        var handled = false



        handled = GIDSignIn.sharedInstance.handle(url)
        if handled { return true }


        if (AuthApi.isKakaoTalkLoginUrl(url)) {
            handled = AuthController.handleOpenUrl(url: url)
            if handled { return true }
        }
        return super.application(app, open: url, options: options)
    }
}

class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
  override func sourceURL(for bridge: RCTBridge) -> URL? {
    // needed to return the correct URL for expo-dev-client.
    bridge.bundleURL ?? bundleURL()
  }

  override func bundleURL() -> URL? {
#if DEBUG
    RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
#else
   if let updatesUrl = AppController.sharedInstance.launchAssetUrl() {
      return updatesUrl
    }
    
    return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
  }
}

Expo.plist




  
    EXUpdatesEnabled
    
    EXUpdatesCheckOnLaunch
    ALWAYS
    EXUpdatesLaunchWaitMs
    0
    EXUpdatesRequestHeaders
    
      expo-channel-name
      production
    
    EXUpdatesURL
    https://u.expo.dev/8475b304-e536-458b-aa6a-6aea6e3e6939
    EXUpdatesRuntimeVersion
    1.0.5
  

Here is my expo dashboard screenshot. And I confirm that I install same version 1.0.5

If you need to see any other configuration files or specific logs, please let me know and I will update the post immediately.

ios react-native eas eas-update