Closed SrZorro closed 1 year ago
@SrZorro - excellent write up!
So there's a few issues in here:
metadata
field is required in the response (as you noticed). The fix is to add metadata: {}
to the update response, since it is required for determining manifest type in expo-constants
(JS) and expo-manifests
(native code). I will put up a PR for this. I'm honestly shocked expo-manifests
is working without it so an investigation into why it works without it will be the first step.expo-updates
itself shouldn't specify what fields are in the extra
field, but it should specify where and how extra
fields work. I'll put up a separate PR for this.expoConfig
indicating which config fields they depend on. This is a much more difficult problem to address since there's so many libraries, but starting with expo-linking
seems good. I'll put up a PR for this as well.This example repo is designed to be a barebones implementation of the expo-updates protocol specification: https://docs.expo.dev/technical-specs/expo-updates-0/. But that doesn't mean that it shouldn't be easy to fork it and add additional expo libraries to it, so I think that is the end goal.
@SrZorro - I implemented the first two items I proposed. The last one is a bit tough to do thoroughly since so many other libraries require config.
What I wish This repository guide about how to make an update, in a nutshell the steps are:
- Make modifications to app
expo export --experimental-bundle
- Copy
dist
toexpo-update-server/updates/<runtime version>
IMHO I think that the expo config should be as easy to update, as the exported bundle
expo export --experimental-bundle
could also export the expo config in a file calledapp.config.json
next tometadata.json
in thedist
directoryAnd then the example server in
expo-updates-server
implement the needed logic to add it toextra.expoClient
& add an emptymetadata
key (or whatever should have)
This is an interesting idea. I'm not sure I'm the right person to assess whether this should be part of the expo export
command though versus just having it be an extra command. Maybe @byCedric or @EvanBacon can comment. Something like adding a flag: expo export --expo-config
.
The same thing can be done by just using getConfig
from @expo/config
. If we think about how EAS does it, it just reads the config and uploads it to the server. So the equivalent for this repo is to just have another step in the yarn expo-publish
script that exports the result of getConfig
into the dist directory as well, and then have the server read it and serve it.
Alright, I think I'll update this repo to embed the config in the manifest as a demonstration.
Starting problem
Assets like images fail to load or the app crashes after an update using the example server (
./expo-updates-server
) when using methodLinking.createURL(path, namedParameters)
ofexpo-linking
adb logcat
shows the following error when assets fail to load:ReactNativeJS: Error: Cannot make a deep link into a standalone app with no custom scheme defined
And when it crashes:
Cause
expo-linking
requires the field(app.json|app.config.json).expo.scheme
to be set, but following the code from that error in expo-linking backtracking to line 120, calls the methodcollectManifestSchemes
that makes multiple gets toConstants.expoConfig
(lines 68, 69, 73, 76), someway this method fails to return the manifests and returns an empty array, causing the throw errorMaking another standalone build, commenting the call to
Linking.createURL
and withconsole.log("Constants.expoConfig::", Constants.expoConfig);
inApp.tsx
If we launch the app, without a new OTA update,
console.log
returns the content fromapp.json|app.config.json
(expo config going forward) that is bundled with the appIf we launch the app, WITH a new OTA update,
console.log
returns an object with the exact response from the manifest endpoint aka the Manifest Response Body, butConstants.expoConfig
it is expected to have expo config as stated in constants documentationReal problem
Documentation or a fix is missing for how we should add the expo config to the metadata response, as the current implementation anything that expects that
Constants.expoConfig
to be expo config, will receive a Manifest Response body instead, in my case breakingexpo-linking
and showing the version of the app asundefined
when gettingConstants.expoConfig?.version
EAS Updates implementation
As stated from this pull request to eas-cli, referencing the pull request
/expo/universe/pull/7893
the EAS Updates server (private repo I guess? Returns 404) will support storing and serving theextra.expoClient
field in the eas update manifestSo when using
eas publish
, it's adding to the metadata that will upload to EAS Updates the expo config in theextra.expoClient
fieldBut this special
extra
key (expoClient
) is not documented in the Manifest Response Body as it only says:extra: { [key: string]: any };
about theextra
fieldHacks / Workarounds
These are two ways I got
Constants.expoConfig
to have the expected fields from expo config after an OTA UpdateAppend expo config to metadata response
Appending to
./expo-updates-server/pages/api/manifest.ts:47
manifest, the expo config,Constants.expoConfig
will have a union of expo config & Manifest Response BodyThis was the first hack I got working to make the app get an update and not break
expo-linking
&Constants.expoConfig?.version
EAS Updates current implementation?
I did all my investigations without checking a real world response from EAS Updates, but making a puzzle with all the source code available from expo & eas I think I understand how the EAS Update service works to get the expo config in an OTA update and have it work
Checking the getter for
Constants.expoConfig
It calls
getManifest
that if all the checks are OK, just returnsrawManifest
rawManifest
in a nutshell will be: if we got an OTA update, it will be Manifest Response Body, if not, the bundled expo config (fromExponentConstants.manifest
)This is where all my problems come from, this
rawManifest
that can be Manifest Response Body or expo config, but by default in an OTA Update will be Manifest Response Body if we don't make the specific "dance" to get the expo config from the Manifest Response BodyThe dance
Checking the code from the EAS Update implementation, is when I found out that
extra.expoClient
IS expo config, so I could finish the puzzle from theexpoConfig
getterSo, to make the metadata endpoint return the expo config, and get
Constants.expoConfig
to return it, I had to:Change the
./expo-updates-server/pages/api/manifest.ts:47
manifest object type with the following modifications:Constants.expoConfig
getter will check ifrawManifest
has ametadata
key (metadata._randomKey
in the modified type to ensure we havemetadata
). If we havemetadata
, it will returnrawManifest.extra?.expoClient ?? null
Because we set
expoClient
in the type example, we will finally getConstants.expoConfig
with the expected value of expo config from the response of Manifest Response Body. Hurray 🎉What I wish
This repository guide about how to make an update, in a nutshell the steps are:
expo export --experimental-bundle
dist
toexpo-update-server/updates/<runtime version>
IMHO I think that the expo config should be as easy to update, as the exported bundle
expo export --experimental-bundle
could also export the expo config in a file calledapp.config.json
next tometadata.json
in thedist
directoryAnd then the example server in
expo-updates-server
implement the needed logic to add it toextra.expoClient
& add an emptymetadata
key (or whatever should have)