Skip to content

[Proposal]: Plugin-Based Architecture for Additional Course Block Types in Open edX Mobile Apps #481

@IvanStepanok

Description

@IvanStepanok

Type of Request

Product Proposal (larger features)

Feature Description

TL;DR

This proposal introduces a plugin-based architecture for additional course block types in the Open edX mobile applications (iOS and Android).

New and optional block types (such as PDF, SCORM, H5P, interactive content, etc.) will be implemented as separate mobile plugins/modules, which can be included only in the builds that need them. Core mobile apps remain lean and focused, without embedding client-specific or heavy block logic.

Existing baseline block types already implemented in the mobile apps remain in Core and continue to work as they do today. The plugin architecture applies only to new and additional block types.


Overview

The Open edX ecosystem is steadily expanding with new course block types and LMS extensions (XBlocks and other integrations).

Today, each new block type that needs mobile support typically requires changes to the Core iOS/Android apps and often brings heavy dependencies, even if only a subset of Open edX instances use that functionality.

This proposal defines a plugin-based architecture for additional course block types in the mobile apps, so that:

  • The Core apps stay thin, stable, and close to upstream.
  • New or optional block types are shipped as separate mobile modules/plugins.
  • Operators only pay (in size and complexity) for the blocks they actually use.

Problem

1. Adding a new block type to mobile currently requires Core changes

To support a new block type on mobile, developers often have to:

  • Modify the Core iOS/Android application.
  • Touch multiple files (enums, switches, adapters, DI modules, etc.).
  • Rebuild all white-label/mobile apps.
  • Accept higher risk of regressions and merge conflicts during upgrades.

2. Heavy and niche block types expand app size for everyone

Many additional block types (e.g., PDF viewers, SCORM players, certain interactive engines) rely on large third-party libraries.

These libraries increase the binary size of the mobile apps, even for instances that never use these block types.

3. Custom XBlocks do not scale well in current mobile architecture

The platform already has a rich ecosystem of XBlock extensions on the LMS side.

However, under the current mobile architecture:

  • Adding mobile support for each new XBlock often leads to forks or local patches of the Core apps.
  • Sharing and reusing mobile implementations across instances is difficult.

Use Cases

  • As an LMS operator, I need to enable mobile support for PDF/SCORM/H5P and other advanced block types in order to deliver a consistent course experience on iOS and Android without forking or patching the Core mobile apps.

  • As a mobile developer working for a specific Open edX instance, I need to add a new custom course block type to the mobile apps in order to support a unique content format for my client, by adding a single module/dependency instead of changing multiple files in the Core and dealing with merge conflicts.

  • As an Open edX mobile maintainer, I need to keep the Core mobile apps as close as possible to upstream in order to simplify upgrades and reduce custom code, by moving instance-specific or heavy block logic into external plugins.

  • As a course author who relies on XBlocks such as SCORM/H5P/other extensions, I need to be confident that my blocks can be rendered on mobile in order to offer a unified learning experience across web and mobile, without duplicating content.


Proposed Solution

High-Level Concept

We propose a shared architectural model for plugin-based blocks in the mobile apps.

The Core mobile application

The Core mobile application:

  • Contains:
    • Core course/block models.
    • Minimal infrastructure code.
    • A central block provider registry.
  • Does not contain rendering or business logic for new or instance-specific blocks.

Plugin modules for block types (iOS and Android)

  • Each plugin implements support for one or more specific block types (PDF, SCORM, H5P, interactive custom content, etc.).
  • Plugins register themselves in the block registry of the mobile app.
  • Operators include only the plugins they need by adding dependencies:
    • SPM on iOS.
    • Gradle on Android.

Important scope constraint

  • Existing baseline block types already implemented in the mobile apps (e.g., video, HTML, problem, discussion, etc.) remain in Core and continue to work as they do today.
  • The plugin architecture is intended for new and additional block types only, at least in the initial phase.

UX / UI

From a learner’s and instructor’s perspective

  • Existing blocks continue to behave as they do now.
  • Additional block types appear in the course outline as regular units, but their rendering is delegated to plugins.
  • Depending on the block type, a plugin may:
    • Render inline content within the lesson view (e.g., PDF viewer embedded in the unit).
    • Navigate to a dedicated screen or player (e.g., SCORM runtime view).
    • Fall back to a clear “not supported on mobile” message if the required plugin is not present.

From a mobile developer/operator perspective

  • Enabling a new block type becomes a build-time decision:
    • Add an SPM package (iOS) or Gradle dependency (Android).
    • Optionally toggle a feature flag or white-label configuration.
  • No direct changes to Core logic are required to add or remove block types, as long as the contracts and registry are stable.

iOS: Plugin Architecture for Additional Block Types

On iOS, we propose the following structure.

Extensible block type representation

BlockType is extended with a case such as:

case custom(String)

This allows it to represent arbitrary string-based types received from the backend.

For existing baseline block types (video, html, problem, discussion, etc.), BlockType continues to behave as it does today.

Block provider registry

The Core app defines a central CourseBlockRegistry that:

  • Stores a mapping { String blockType → CourseBlockProvider }.
  • Exposes thread-safe methods for registering and retrieving providers.

Provider contract

A protocol CourseBlockProvider is introduced, for example:

  • identifier: String — the string block type that the provider supports (aligned with the LMS block_type value).
  • makeView(context: CourseBlockProviderContext) -> AnyView — constructs the SwiftUI view for the given block.

CourseBlockProviderContext includes:

  • The course block model (CourseBlock).
  • Positional information (index, current index).
  • Container size and layout hints.
  • Connectivity information.
  • Streaming quality and video-related helpers (when relevant).
  • Interfaces for offline loading, analytics, logging, and other shared services.

Integration with Core UI

The lesson screen (e.g., CourseUnitView) is updated to:

  1. Attempt to obtain a custom block view from CourseBlockRegistry based on the block type.
  2. If a provider is found, render the plugin’s SwiftUI view.
  3. If not, fall back to the existing logic (e.g., LessonType + switch), preserving full backward compatibility for baseline blocks.

Plugins as SPM packages

Each new additional block type is implemented as a separate Swift Package that:

  • Imports a shared foundation module (see Implementation plan) that defines BlockType, CourseBlockProvider, CourseBlockProviderContext, and required service protocols.
  • Implements CourseBlockProvider for one or more block types.
  • Registers itself with CourseBlockRegistry during initialization (for example, via a bootstrap function invoked from the app).

This way, the Core iOS app depends only on stable contracts and the registry; block-specific UI and logic live fully inside separate SPM modules.


Android: Plugin Architecture for Additional Block Types

On Android, we propose a conceptually similar architecture.

Foundation library

A dedicated foundation library provides:

  • CourseUnitBlockExtensionsRegistry — the central registry for block extensions.
  • CourseUnitBlockExtension — the extension point interface (block handler).
  • CourseUnitBlockContext — the runtime context for rendering a block (block data, services, etc.).
  • BlockTypePlugin and BlockTypePluginLoader — interfaces and loading mechanism for plugins, e.g., using ServiceLoader.

Integration with the Open edX Android app

The course unit container (e.g., CourseUnitContainerAdapter) is refactored to:

  • Use CourseUnitBlockExtensionsRegistry to resolve which extension should render a given block type.
  • Keep the existing behavior for baseline block types registered by default (either in Core or in a dedicated registration module).

Baseline blocks remain supported as they are today, while additional block types are handled via the extension registry.

Plugins as Gradle dependencies

Each new additional block type is implemented in a separate Android Library module that:

  • Implements BlockTypePlugin, which registers one or more CourseUnitBlockExtension instances in the registry at runtime.
  • May use ServiceLoader to allow automatic discovery by the application.

Enabling a new block type in a particular mobile build becomes:

  • Adding a single line in build.gradle, such as:
implementation "org.example:my-block-plugin:x.y.z"

The plugin is then discovered and registered automatically, without code changes in the Core app.


Implementation Plan

Scope focus: this proposal covers additional/new block types. Moving existing baseline blocks from Core to plugins is explicitly out of scope for the initial phase.

1. Finalise contracts (iOS + Android)

Define and stabilise:

  • Registry interfaces (CourseBlockRegistry, CourseUnitBlockExtensionsRegistry).
  • Provider contracts (CourseBlockProvider, CourseUnitBlockExtension / BlockTypePlugin).
  • The minimal set of services and data included in the block context (connectivity, offline storage, analytics, logging, etc.).

Deliverable: a short technical design document and initial interfaces in the foundation modules.

2. Create or refine foundation layers

Introduce or refine a shared foundation module for each platform (e.g., OEXFoundation for iOS, a core foundation library for Android) that contains:

  • Block type definitions (including custom(String) on iOS).
  • Provider and context contracts.
  • Key service interfaces required by block plugins.

Update the Core mobile apps so that they depend on these contracts rather than on block-specific implementations.

3. Integrate the plugin architecture into Core UI (iOS + Android)

iOS:

  • Update the course unit view to use CourseBlockRegistry first and fall back to existing logic for baseline blocks.

Android:

  • Ensure CourseUnitBlockExtensionsRegistry is used by the course unit container to resolve block rendering, while keeping existing handling intact for baseline block types.

4. Pilot plugins

Select 1–2 additional block types as pilot candidates (for example, PDF and/or SCORM).

Implement them as separate modules:

  • iOS: Swift Packages implementing CourseBlockProvider and associated UI.
  • Android: Android Libraries implementing BlockTypePlugin and CourseUnitBlockExtension.

Validate:

  • Correct rendering in mobile apps when the plugin is present.
  • Clear and safe behavior when a course contains that block type but the plugin is not included in a specific mobile build (e.g., “not supported on mobile” message).

5. Documentation and contributor guidelines

Create a concise developer guide explaining:

  • How to implement a new mobile block plugin.
  • How to map LMS-side block_type values to mobile plugins.
  • Recommendations on dependency size, performance, and offline behaviour.

Resources and Funding

Raccoon Gang is currently looking for funding for this proposal.


Long-Term Ownership and Maintenance Plans

Core architecture (registry, contracts, foundation)

  • Owned by the Open edX mobile maintainers (e.g., Axim Collaborative mobile team and contributors).
  • Changes follow the normal Open edX review and release processes.

Individual block plugins

  • Each plugin has a clearly identified owner (organization or team) listed in its README/documentation.

Possible split:

  • “Official” plugins for widely used formats (e.g., PDF, SCORM) maintained by core teams or key partners.
  • Community plugins maintained on a best-effort basis.

Compatibility and Versioning

  • Foundation/contract modules are versioned and document supported mobile app versions.
  • Plugins specify the foundation versions they support and can deprecate older contracts over time.

Contact Person

Ivan Stepanok
📧 [email protected]


Open Questions

  1. Pilot plugins and priorities

    • Which block types should be prioritized as pilot plugins?
      • PDF?
      • SCORM?
      • H5P?
      • Other high-value formats requested by the community?
  2. Minimal context surface

    • What is the minimal set of services and data that must be included in CourseBlockProviderContext / CourseUnitBlockContext (e.g., connectivity, offline storage, analytics, logging, navigation hooks)?
    • What should remain optional or plugin-specific?
  3. Long-term strategy for baseline blocks

    • Should existing baseline blocks (video, HTML, problem, discussion, etc.) remain in Core indefinitely?
    • Or should we consider migrating them to the same plugin mechanism over time, once the architecture proves stable?
  4. Publication and catalog of plugins

    • Do we want a centralized catalog of “official” mobile block plugins (similar to how XBlocks are documented today)?
    • If yes, where should it live?
      • Open edX documentation?
      • A dedicated catalog page?
      • A GitHub organization?

Link to Product Proposal

https://openedx.atlassian.net/wiki/x/KgA7PgE

Status

New

Proposed By

Ivan Stepanok (Raccoon Gang)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions