EAS Update 在生产构建中未下载,尽管通道匹配且已成功发布
发布: (2026年2月9日 GMT+8 10:26)
4 分钟阅读
原文: Dev.to
Source: Dev.to
请提供您希望翻译的文章正文(包括任何代码块、列表或其他 Markdown 内容),我将按照要求保留原始格式并将文本翻译成简体中文。谢谢!
问题概述
-
我在 bare React Native 项目(非 Managed 工作流)中使用 EAS Update。更新已成功发布并出现在 Expo 仪表板上,但通过 TestFlight 安装的实体设备从未收到更改。
-
更新在仪表板中列在正确的分支/通道下。
-
我已经多次重启应用。
项目配置
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 * 1_000_000 + minor * 1_000 + 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
<dict>
<key>EXUpdatesEnabled</key>
<true/>
<key>EXUpdatesCheckOnLaunch</key>
<string>ALWAYS</string>
<key>EXUpdatesLaunchWaitMs</key>
<integer>0</integer>
<key>EXUpdatesRequestHeaders</key>
<dict>
<key>expo-channel-name</key>
<string>production</string>
</dict>
<key>EXUpdatesURL</key>
<string>https://u.expo.dev/8475b304-e536-458b-aa6a-6aea6e3e6939</string>
<key>EXUpdatesRuntimeVersion</key>
<string>1.0.5</string>
</dict>
我已经验证的内容
- 更新出现在 Expo Dashboard 的 production 渠道下。
- 应用已在设备上 强制退出并重新启动 多次。
目标
找出为何在 TestFlight 安装的设备上 OTA 更新未被应用,尽管仪表盘显示更新已发布。希望能获得关于缺失的配置、缓存或所需原生更改的任何见解。
如果您需要查看其他配置文件或特定日志,请告诉我,我会立即更新帖子。