-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Forsetti is a native Apple modular runtime framework for iOS and macOS applications. It gives host apps a consistent way to discover, validate, unlock, activate, and render feature modules while keeping architecture boundaries strict and enforceable. Last updated: February 27, 2026
If you are evaluating Forsetti, start here. If you are implementing with Forsetti, this README is the canonical high-level reference.
- What Forsetti Is
- The Problem It Solves
- Design Principles
- Integration Contract (What You Can and Cannot Do)
- Package Structure
- Core Runtime Concepts
- Why Dependency Rules Are Strict
- Import Rules and Why They Exist
- Quick Start
- Module Authoring Workflow
- Manifest Contract
- Entitlements and Paid Modules
- Capability Governance
- OOP and Modularity Rules
- Architecture Guardrails (Lint, Tests, CI)
- Recommended Consumer-Repo Guardrails
- Troubleshooting
- FAQ
- Additional Documentation
- License
Forsetti is a framework for modular app composition. It is built around these capabilities:
- Module discovery from bundled manifests.
- Compatibility validation before activation.
- Entitlement-aware module locking/unlocking.
- Flexible module activation with clear deployment patterns (see below).
- Structured UI contributions (toolbar, theme tokens, overlays, view injection).
- Native host integration with Swift and SwiftUI.
Forsetti currently targets:
- iOS
- macOS
In many apps, feature code grows into tightly coupled systems where:
- UI and domain logic bleed into each other.
- Purchases/entitlements are bolted on late.
- Feature toggles and module activation become ad-hoc.
- “Temporary” dependencies become permanent architecture debt.
Forsetti addresses this by enforcing module contracts and runtime policy up front. The goal is controlled extensibility without losing architectural integrity.
Forsetti is opinionated by design. These principles are intentional constraints, not suggestions.
- Native-first: Swift, SwiftPM/Xcode, Apple frameworks.
- Contract-first: modules integrate through explicit protocols and manifests.
- Boundary-first: dependency direction is intentional and enforced.
- Policy-first: compatibility, capabilities, and entitlements are runtime gates.
- Host-agnostic modules: features should be plug-in style, not host-wired.
Forsetti supports four deployment patterns. Choose the one that matches your use case.
The app is a single ForsettiUIModule that includes the complete application UI.
The framework loads silently in the background.
End users see only the module's UI and have no awareness of the framework.
Framework controls (Home, Settings) are hidden in production.
Framework errors go silently to the framework error log.
Updates are delivered by swapping the module with a new version.
This is the expected pattern for the vast majority of apps built on Forsetti.
The application is composed of multiple modules: exactly one UI module plus one or more service modules. The UI module carries the application's UI. All service modules run simultaneously alongside the UI module. The framework enforces one active UI module at a time for this pattern. The framework still runs silently; end users see only the UI module's interface.
Use this pattern when your application requires background services (data sync, analytics, etc.) that are cleanly separated from the UI.
A developer loads multiple different single-module apps on one framework instance for testing. Each module represents a separate application and may have its own UI. Only one module is active at a time since each represents a different application. Framework controls (Home, Settings, module switcher) remain visible so the developer can switch between apps.
This is a development and QA pattern, not intended for production deployment.
Multiple separate applications are hosted on one framework for end-user access. Framework controls may remain visible to allow users to navigate between applications. A dedicated UI module for the dashboard itself is recommended.
Use this pattern for portal or launcher-style apps where end users explicitly switch between multiple applications.
Forsetti is meant to be consumed as a sealed framework.
Allowed:
- Use Forsetti public package products and public APIs.
- Build app-owned modules in your own targets.
- Compose host runtime/services through public extension points.
- Request upstream enhancements if an extension point is missing.
Not allowed:
- Modifying Forsetti internals for app-specific behavior.
- Copying Forsetti source files into your app and patching them.
- Backdoor coupling from app targets into Forsetti internals.
Decision rule:
- If a solution requires changing Forsetti internals in your app repo, the solution is out of policy.
Forsetti ships as multiple products/targets with clear responsibilities:
-
ForsettiCore- Runtime contracts, models, compatibility checks, activation orchestration.
- Platform-agnostic logic only.
-
ForsettiPlatform- Native platform service adapters and entitlement implementations.
-
ForsettiModulesExample- Example modules + manifests for reference and testing.
-
ForsettiHostTemplate- SwiftUI host controller/views for module discovery and activation UI.
Forsetti flow at runtime:
- Build a
ModuleRegistrywith entry-point factories. - Boot
ForsettiRuntimewith services, entitlement provider, and policy. - Load manifests from a bundle subdirectory.
- Validate each manifest for compatibility/capability/version constraints.
- Activate eligible modules.
- Reflect UI contributions through
UISurfaceManager. - React to entitlement changes and reconcile active modules.
Core contracts:
-
ForsettiModuleandForsettiUIModule -
ModuleManifest,ModuleDescriptor ModuleRegistryManifestLoaderCompatibilityCheckerCapabilityPolicyActivationStoreForsettiEntitlementProvider-
ForsettiServiceProviding/ForsettiServiceContainer
A rule like “X must not import Y” means:
- X is not allowed to compile against Y.
- X must remain independent of Y’s behavior and release cycle.
- Any required behavior must be expressed via contracts and dependency injection instead.
This protects:
- Stability: lower layers are insulated from upper-layer churn.
- Testability: domain/runtime can be tested without UI/store frameworks.
- Portability: core logic stays reusable across hosts.
- Build health: fewer transitive dependencies and fewer cycles.
- Team velocity: clear ownership boundaries reduce merge conflicts.
Without these rules, architectures drift into hidden coupling and regress quickly.
These are enforced in this repo via lint/tests. They are intentionally strict.
-
ForsettiPlatform,ForsettiModulesExample,ForsettiHostTemplate -
SwiftUI,UIKit,AppKit,StoreKit
Why:
- Core is the architecture foundation.
- If Core depends on UI/platform/commerce frameworks, every consumer inherits that coupling.
- Core must remain pure runtime/domain to stay stable and reusable.
-
ForsettiModulesExample,ForsettiHostTemplate -
SwiftUI,UIKit,AppKit
Why:
- Platform layer should implement service adapters, not host presentation concerns.
- Prevents “adapter layer” from drifting into app UI orchestration.
-
ForsettiPlatform,ForsettiHostTemplate -
SwiftUI,UIKit,AppKit,StoreKit
Why:
- Example modules should demonstrate module contracts, not internal host/platform coupling.
- Keeps sample modules portable and pedagogical.
ForsettiModulesExample
Why:
- Host must remain generic and work with any valid module set.
- Prevents accidental hardcoding to sample implementations.
import ForsettiCore
import ForsettiPlatform
import ForsettiModulesExample
import ForsettiHostTemplate
let registry = ModuleRegistry()
ExampleModuleRegistry.registerAll(into: registry)
let entitlementProvider = ForsettiEntitlementProviderFactory.makeDefault(
macOSUnlockedProductIDs: ["com.forsetti.iap.example-ui"]
)
let controller = ForsettiHostTemplateBootstrap.makeController(
manifestsBundle: ExampleModuleResources.bundle,
moduleRegistry: registry,
entitlementProvider: entitlementProvider
)
let rootView = ForsettiHostRootView(controller: controller)What this does:
- Registers module factories.
- Uses default entitlement provider strategy by platform.
- Builds runtime and host controller.
- Renders host UI that can discover/activate modules.
In consumer apps, create your own module target and follow this sequence.
- Define module class conforming to
ForsettiModuleorForsettiUIModule. - Implement
descriptorandmanifestwith alignedmoduleIDandentryPoint. - Implement lifecycle (
start/stop) as idempotent, bounded operations. - Register module factory in your bootstrap.
- Include manifest JSON in bundle resources.
- Run architecture/lint/test guardrails before merge.
Guidance:
- Prefer protocol-based service lookup through
ForsettiContext.services. - Keep module responsibilities narrow.
- Avoid direct knowledge of host internals.
A manifest is the runtime contract for discoverability and eligibility.
Required fields:
schemaVersionmoduleIDdisplayNamemoduleVersionmoduleTypesupportedPlatformsminForsettiVersioncapabilitiesRequestedentryPoint
Optional fields:
maxForsettiVersioniapProductID
If key metadata is wrong (missing entry point, invalid platform, denied capability), activation fails by design.
Forsetti entitlement model:
- If
iapProductIDisnil, module is considered unlocked. - If
iapProductIDis set, entitlement provider determines lock/unlock. - Entitlement changes trigger active-module reconciliation.
Default provider behavior:
- iOS: StoreKit 2 backed entitlement provider.
- macOS: static allowlist provider (stub-friendly default).
Why this matters:
- Monetization state is not a UI-only concern; it is an activation policy concern.
- Enforcement at runtime layer prevents “UI says locked but runtime still active” class of bugs.
Capabilities are explicit permission requests from modules. Examples include storage, telemetry, routing overlay, toolbar items, and view injection.
Use capability policy to enforce least privilege:
-
AllowAllCapabilityPolicyfor permissive scenarios. -
FixedCapabilityPolicyfor allowlisted scenarios.
Why enforce:
- Prevent modules from silently expanding scope.
- Make capability expansion a reviewable architecture decision.
Forsetti intentionally favors classic OOP discipline with modern Swift patterns.
Required approach:
- Protocol-first boundaries.
- Constructor dependency injection.
- Narrow public APIs.
- Strong encapsulation with
private/internaldefaults. -
finalclasses where inheritance is not a deliberate extension point.
Why:
- Reduces accidental override behavior.
- Makes coupling explicit.
- Improves deterministic behavior under modular composition.
This repository includes hard guardrails:
- Architecture test target for layering and class finality checks.
- Strict
SwiftLintpolicy with custom layer import rules. - CI workflow that blocks regressions on push/PR.
Run locally:
./Scripts/verify-forsetti-guardrails.shThis executes:
swift test --parallel --enable-code-coverageswiftlint lint --strict --config .swiftlint.yml
If your app consumes Forsetti, replicate guardrails in your own repository:
- Add architecture policy tests for your app targets.
- Add strict lint import/dependency rules.
- Add one local verification script that runs all checks.
- Block merges on CI unless guardrails pass.
Suggested files in consumer repo:
Tests/ArchitectureTests/ForsettiArchitecturePolicyTests.swift.swiftlint.ymlScripts/verify-forsetti-guardrails.sh.github/workflows/forsetti-guardrails.yml
moduleNotDiscovered:
- Manifest missing from bundle resources.
- Wrong manifests subdirectory at runtime boot.
- Manifest validation failure.
entryPointNotRegistered:
- Manifest entry point has no matching registry factory.
moduleLocked:
- Entitlement provider does not currently unlock module/product.
incompatible:
- Platform mismatch.
- Forsetti version range mismatch.
- Denied capability.
- Schema mismatch.
notUIModule:
- Manifest says
moduleType = uibut factory returns non-ForsettiUIModule.
Because unmanaged extensibility creates long-term coupling debt. Forsetti optimizes for controlled modular growth, not unconstrained short-term flexibility.
You can technically do almost anything in code. Architecturally, bypassing these rules is equivalent to taking dependency debt that will compound. The framework is designed to make the correct path the easiest path.
Because host composition must remain stable and reviewable. Forsetti supports UI contributions through structured contracts instead of arbitrary host mutation.
Because lock/unlock state affects activation validity, not just visuals. Runtime-level entitlement enforcement prevents policy drift and edge-case inconsistencies.
-
guide.md- concise integration rules and policies.
-
wiki.md- extended integration playbook with more implementation examples.
-
forsetti-instructions.json- architecture source material and phase context.
Forsetti is proprietary software.
See full licensing terms in:
license.md
External/commercial use requires a separate written paid license from James Daley.
This repo includes an Xcode project template for faster setup:
- Install script:
Scripts/install-forsetti-xcode-template.sh - Uninstall script:
Scripts/uninstall-forsetti-xcode-template.sh
After installation, create a new project in Xcode under:
File > New > ProjectMultiplatformForsetti App
Forsetti is opinionated on purpose. The rules are not there to reduce flexibility; they are there to preserve long-term flexibility by preventing architecture erosion.