feat: App Launch Sequencing / InstanceAwareConfig - #682
RawanMatar89 wants to merge 2 commits into
Conversation
AppDelegate now awaits InstanceConfigLoader.load() before building and
presenting RouteController, instead of firing it in a detached Task
and routing off the bundled/cached catalog immediately. The system
Launch Screen stays up for the wait (ends when makeKeyAndVisible() is
called, which is now deferred until after the catalog resolves) --
no extra loading UI needed.
This matters because the bundled fallback catalog is a single
placeholder instance ("example-instance", a fake domain). Routing
immediately off it and correcting later (once the live fetch resolves
and InstanceStore.currentInstance goes back to nil for a multi-
instance catalog) would mean briefly showing a login screen for a
domain that doesn't exist, then silently swapping to a different
screen underneath the user. Waiting means RouteController's one and
only routing decision (Login vs. the instance picker vs. Main, PR-10)
is always made against the real catalog.
When INSTANCES_CATALOG_URL isn't configured, or the live fetch fails,
InstanceConfigLoader.load() already falls back to the bundled/cached
baseline near-instantly (no network call is even attempted when
unconfigured) -- so this adds no observable delay for those cases.
Standalone ConfigProtocol implementation (not a Config subclass -- Config's conformance is spread across several extension blocks, which Swift can't override via subclassing) that layers the currently selected Instance over the app-level Config. Instance-carried fields win when an instance is selected and the field isn't nil; everything else falls back to Config. Adds new typed initializers to ThemeConfig and ExperimentalFeaturesConfig so InstanceAwareConfig can build them from Instance's already-typed fields instead of round-tripping through a raw dictionary. Registers InstanceAwareConfig in AppAssembly as the ConfigProtocol implementation, wrapping the existing Config(). Keeps the config.json bridge keys (API_HOST_URL/SSO_URL/etc.) as the fallback when no instance is selected -- removing them isn't safe until PR-10's picker exists, since a fresh multi-instance install has no persisted selection at launch. Adds InstanceAwareConfigTests covering the override/fallback pattern per field category: scalar overrides, optional-field fallback, the typed theme/experimentalFeatures mapping, the always-instance-wins struct fields (uiComponents), and app-level-only passthrough fields.
|
Thanks for the pull request, @RawanMatar89! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. 🔘 Update the status of your PRYour PR is currently marked as a draft. After completing the steps above, update its status by clicking "Ready for Review", or removing "WIP" from the title, as appropriate. Where can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
What
Wires the remote instance catalog into app launch, and replaces the app-level
Configwithan instance-aware
ConfigProtocolimplementation. Stacked on Session Lifecycle #681.Why
Up to this point,
InstanceConfigLoader(built in an earlier PR) had no caller, and everyConfigProtocol-dependent code path (networking, theming, feature flags, etc.) read only thebundled app-level
Config— instances existed as data but nothing in the app actually usedthem yet.
Changes
1. Launch-time catalog fetch (
OpenEdX/AppDelegate.swift)didFinishLaunchingWithOptionsnow fires a non-blockingTaskthat callsInstanceConfigLoader.load()and hands the result toInstanceStore.updateInstancesConfig(_:).InstanceStorealready has a usable catalog synchronously (bundledfallback, auto-selected when there's exactly one instance), so launch doesn't wait on the
network.
2.
InstanceAwareConfig(Core/Core/Configuration/Config/InstanceAwareConfig.swift)ConfigProtocolimplementation — not aConfigsubclass, sinceConfig'sconformance is spread across several
extension Config { ... }blocks that Swift can'toverride via subclassing.
Instance(viaInstanceProvider) over the wrappedapp-level
Config: instance-carried fields win when an instance is selected and the fieldisn't nil, everything else falls back to
Config.firebase,facebook,microsoft,google,appleSignIn,braze,branch,URIScheme,appStoreLink,instancesCatalogURL.ThemeConfigandExperimentalFeaturesConfigsoInstanceAwareConfigcan build them straight fromInstance's already-typed fieldsinstead of round-tripping through a raw dictionary.
3. DI (
OpenEdX/DI/AppAssembly.swift)ConfigProtocolnow resolves toInstanceAwareConfig(appConfig: Config(), instanceProvider: ...)instead of a bare
Config(). No other call site changes — everything resolvesConfigProtocol.selfgenerically.4. Tests (
Core/CoreTests/InstanceAwareConfigTests.swift)fallback, the typed
theme/experimentalFeaturesmapping, the always-instance-wins structfields (
uiComponents), and app-level-only passthrough fields.Deliberately out of scope
config.jsonbridge keys (API_HOST_URL/SSO_URL/SSO_FINISHED_URL/OAUTH_CLIENT_ID)stay as the no-instance-selected fallback. Removing them isn't safe until the picker
exists — a fresh multi-instance install has no persisted selection at launch, and
InstanceAwareConfigfalling through to aConfigwith no bridge keys wouldfatalError.RequestInterceptor's own host-rewrite logic (from an earlier PR) is now partiallyredundant, since
config.baseURLpassed into it is already instance-aware viaInstanceAwareConfig. Left as-is here — flagging as a follow-up simplification rather thanbundling an untested behavior change into this PR.
.instanceDidChange: if the launch-time fetch resolves after the UIhas already built itself off the bundled catalog, nothing currently observes the
notification to re-route. No picker exists yet to route to.
How to test
Cmd+Uon theOpenEdXDev.INSTANCES_CATALOG_URLconfigured. Confirm no crash, and that
InstanceConfigLoadereither swaps in the remotecatalog or falls back to baseline (bundled/cached) on a bad/unreachable URL — check the
debug console for
InstanceConfigLoader: live fetch failed (...) — using baseline.