Skip to content

feat: App Launch Sequencing / InstanceAwareConfig - #682

Draft
RawanMatar89 wants to merge 2 commits into
openedx:developfrom
zeit-labs:infra/09-app-launch-sequencing
Draft

RawanMatar89 wants to merge 2 commits into
openedx:developfrom
zeit-labs:infra/09-app-launch-sequencing

Conversation

@RawanMatar89

Copy link
Copy Markdown
Contributor

What

Wires the remote instance catalog into app launch, and replaces the app-level Config with
an instance-aware ConfigProtocol implementation. Stacked on Session Lifecycle #681.

Why

Up to this point, InstanceConfigLoader (built in an earlier PR) had no caller, and every
ConfigProtocol-dependent code path (networking, theming, feature flags, etc.) read only the
bundled app-level Config — instances existed as data but nothing in the app actually used
them yet.

Changes

1. Launch-time catalog fetch (OpenEdX/AppDelegate.swift)

  • didFinishLaunchingWithOptions now fires a non-blocking Task that calls
    InstanceConfigLoader.load() and hands the result to InstanceStore.updateInstancesConfig(_:).
  • Non-blocking by design: InstanceStore already has a usable catalog synchronously (bundled
    fallback, auto-selected when there's exactly one instance), so launch doesn't wait on the
    network.

2. InstanceAwareConfig (Core/Core/Configuration/Config/InstanceAwareConfig.swift)

  • A standalone ConfigProtocol implementation — not a Config subclass, since Config's
    conformance is spread across several extension Config { ... } blocks that Swift can't
    override via subclassing.
  • Layers the currently selected Instance (via InstanceProvider) over the wrapped
    app-level Config: instance-carried fields win when an instance is selected and the field
    isn't nil, everything else falls back to Config.
  • Fields that always stay app-level (not per-instance in this project's model): firebase,
    facebook, microsoft, google, appleSignIn, braze, branch, URIScheme,
    appStoreLink, instancesCatalogURL.
  • New typed initializers on ThemeConfig and ExperimentalFeaturesConfig so
    InstanceAwareConfig can build them straight from Instance's already-typed fields
    instead of round-tripping through a raw dictionary.

3. DI (OpenEdX/DI/AppAssembly.swift)

  • ConfigProtocol now resolves to InstanceAwareConfig(appConfig: Config(), instanceProvider: ...)
    instead of a bare Config(). No other call site changes — everything resolves
    ConfigProtocol.self generically.

4. Tests (Core/CoreTests/InstanceAwareConfigTests.swift)

  • Covers the override/fallback pattern by 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.

Deliberately out of scope

  • config.json bridge 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
    InstanceAwareConfig falling through to a Config with no bridge keys would fatalError.
  • RequestInterceptor's own host-rewrite logic (from an earlier PR) is now partially
    redundant, since config.baseURL passed into it is already instance-aware via
    InstanceAwareConfig. Left as-is here — flagging as a follow-up simplification rather than
    bundling an untested behavior change into this PR.
  • Reactive UI on .instanceDidChange: if the launch-time fetch resolves after the UI
    has 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+U on the OpenEdXDev.
  • Manual: run on device/simulator with a valid multi-instance INSTANCES_CATALOG_URL
    configured. Confirm no crash, and that InstanceConfigLoader either swaps in the remote
    catalog or falls back to baseline (bundled/cached) on a bad/unreachable URL — check the
    debug console for InstanceConfigLoader: live fetch failed (...) — using baseline.

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.
@openedx-webhooks openedx-webhooks added open-source-contribution PR author is not from Axim or 2U core contributor PR author is a Core Contributor (who may or may not have write access to this repo). labels Sep 23, 2026
@openedx-webhooks

Copy link
Copy Markdown

Thanks for the pull request, @RawanMatar89!

This repository is currently maintained by @openedx/openedx-mobile-maintainers.

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 approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To 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:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If 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 PR

Your 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:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core contributor PR author is a Core Contributor (who may or may not have write access to this repo). open-source-contribution PR author is not from Axim or 2U

Projects

Status: Needs Triage

Development

Successfully merging this pull request may close these issues.

2 participants