-
Notifications
You must be signed in to change notification settings - Fork 0
Module Model and Manifests
James Daley edited this page Apr 25, 2026
·
2 revisions
Every module has two contracts: a Swift type contract and a JSON manifest contract. Both must agree on identity, type, version, and entry point.
classDiagram
class ForsettiModule {
+descriptor: ModuleDescriptor
+manifest: ModuleManifest
+start(context)
+stop(context)
}
class ForsettiUIModule {
+uiContributions: UIContributions
}
class ForsettiAppModule
ForsettiModule <|-- ForsettiUIModule
ForsettiUIModule <|-- ForsettiAppModule
| Module Type | Protocol | Purpose |
|---|---|---|
service |
ForsettiModule |
Background behavior, domain services, sync, telemetry, data preparation, and non-visual feature support. |
ui |
ForsettiUIModule |
A feature UI module with toolbar items, view injections, overlays, or other host-visible UI contributions. |
app |
ForsettiAppModule |
A complete single-application module that carries the primary application UI. |
{
"schemaVersion": "1.0",
"moduleID": "com.example.product.module",
"displayName": "Example Module",
"moduleVersion": {
"major": 1,
"minor": 0,
"patch": 0
},
"moduleType": "ui",
"supportedPlatforms": ["iOS", "macOS"],
"minForsettiVersion": {
"major": 0,
"minor": 1,
"patch": 0
},
"maxForsettiVersion": null,
"capabilitiesRequested": ["toolbar_items", "view_injection"],
"iapProductID": null,
"entryPoint": "ExampleModule"
}| Field | Required | Notes |
|---|---|---|
schemaVersion |
Yes | Current supported value is 1.0. |
moduleID |
Yes | Stable unique ID. Duplicate IDs fail discovery. |
displayName |
Yes | Human-readable module name. |
moduleVersion |
Yes | Semantic version object with major, minor, and patch. |
moduleType |
Yes |
service, ui, or app. |
supportedPlatforms |
Yes | One or more of iOS and macOS. |
minForsettiVersion |
Yes | Minimum runtime version required. |
maxForsettiVersion |
No | Optional upper bound. |
capabilitiesRequested |
Yes | Capabilities requested before activation. |
iapProductID |
No | Product identifier for paid/locked modules. |
entryPoint |
Yes | Registry key used to create the module instance. |
flowchart TD
A["Choose module type"] --> B["Create Swift module class"]
B --> C["Define descriptor"]
C --> D["Create manifest JSON"]
D --> E["Keep moduleID, moduleType, version, and entryPoint aligned"]
E --> F["Register entryPoint in ModuleRegistry"]
F --> G["Add manifest to Resources/ForsettiManifests"]
G --> H["Run tests and guardrails"]
let registry = ModuleRegistry()
registry.register(entryPoint: ExampleModule.Constants.entryPoint) {
ExampleModule()
}The manifest entryPoint must match the registry key. A discovered manifest without a matching registry factory cannot activate.
flowchart LR
JSON["JSON file"] --> Shape{"Looks like manifest?"}
Shape -- no in fallback mode --> Skip["Ignore file"]
Shape -- yes --> Decode["Decode ModuleManifest"]
Decode --> Required{"Required fields present?"}
Required -- no --> Fail["validationFailed"]
Required -- yes --> Duplicate{"moduleID already loaded?"}
Duplicate -- yes --> Dup["duplicateModuleID"]
Duplicate -- no --> Index["Index by moduleID"]
Strict directory loading fails invalid JSON immediately. Fallback discovery ignores JSON files that do not contain the manifest root keys.
| Capability | Raw Value | Typical Use |
|---|---|---|
networking |
networking |
Network requests through host-provided service. |
storage |
storage |
Non-sensitive storage. |
secureStorage |
secure_storage |
Sensitive data storage. |
fileExport |
file_export |
Export files through host-approved destination. |
cryptoUtilities |
crypto_utilities |
Cryptographic helper access. |
telemetry |
telemetry |
Event tracking through host telemetry service. |
routingOverlay |
routing_overlay |
Overlay route and pointer behavior. |
uiThemeMask |
ui_theme_mask |
Reserved for framework shell; currently denied by compatibility checks. |
toolbarItems |
toolbar_items |
Toolbar item contributions. |
viewInjection |
view_injection |
Slot-based SwiftUI view injection. |