-
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
Current Version: 0.1.0
If you are evaluating Forsetti, start here. If you are implementing with Forsetti, this README is the canonical high-level reference.
- What Forsetti Is
- Who This Is For
- 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
- Xcode Template (Optional)
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
Forsetti is a modularity-first, object-oriented framework for building iOS and macOS applications in Xcode using Apple-native tools, libraries, and frameworks only. This includes Swift, SwiftUI, Metal, and other Apple-native technologies used in the correct module or architectural layer.
Forsetti is designed for:
- Teams building monetized iOS and macOS apps that need clean module boundaries, entitlement-gated features, and scalable architecture.
- Solo developers shipping modular applications who want runtime governance without building custom infrastructure.
- Organizations managing multi-app portfolios that want a shared modular runtime across products.
Forsetti is proprietary software. Evaluation for internal assessment is permitted. Production or commercial use requires a written license from James Daley (see section 21 for details).
If you are evaluating Forsetti, start with the Quick Start (section 10) and the Xcode template (section 22).
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 ForsettiAppModule 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.
In the multi-module single-application pattern, the application is composed of exactly one dedicated UI module plus one or more service modules. The dedicated UI module carries the application's user interface. That UI module may use SwiftUI and other Apple-native UI frameworks and tools. Service modules remain separate from the UI module.
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:
-
ForsettiModule,ForsettiAppModule, andForsettiUIModule -
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.
Important clarification: Layer and target import restrictions in Forsetti are architectural boundary rules. They are not global bans on Apple-native frameworks in app-owned modules. A consumer application may build app-owned Forsetti-compatible modules in its own targets and may use Apple-native frameworks that are appropriate to each module's responsibility, provided those frameworks are used in the correct module or architectural layer.
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.
For development and testing, use makeDebug to bypass StoreKit:
// Unlock everything (all modules treated as purchased):
let debugProvider = ForsettiEntitlementProviderFactory.makeDebug()
// Unlock specific modules/products only:
let selectiveProvider = ForsettiEntitlementProviderFactory.makeDebug(
unlockedProductIDs: ["com.yourapp.iap.premium"]
)Pass the debug provider to ForsettiHostTemplateBootstrap.makeController(entitlementProvider:).
Use StaticEntitlementProvider.setUnlockedProducts(_:) to change unlock state at runtime during tests.
In consumer apps, create your own module target and follow this sequence.
- Define module class conforming to
ForsettiAppModule(single-module apps),ForsettiUIModule(multi-module UI), orForsettiModule(service/feature). - 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.
- For multi-module single applications, keep the application UI in a dedicated UI module. That UI module may use SwiftUI and other appropriate Apple-native frameworks.
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 = uiormoduleType = appbut factory returns a type that does not conform toForsettiUIModuleorForsettiAppModule.
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.
Metal is an allowed Apple-native technology in Forsetti-based applications when it is used in the correct app-owned module or architectural layer. For example, a rendering-oriented module may use Metal where that module's responsibility justifies it. Metal is not prohibited by Forsetti; it is governed by modular boundaries and correct placement.
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.
See wiki.md section 18 for detailed guidance on cold-start overhead, 50+ module counts, and offline entitlement behavior.
In short: keep module start() fast, keep manifest files small, and rely on StoreKit's local receipt cache for offline entitlement resilience.
-
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.
-
AI_IMPLEMENTATION_GUIDE.md- canonical interpretation guide for AI agents and implementers.
-
MODULE_BOUNDARY_RULES.md- concise boundary rules that separate internal target guardrails from consumer module implementation.
Forsetti is proprietary software owned by James Daley.
- Evaluation: You may access this repository to evaluate Forsetti for your team's needs. Evaluation does not grant production or distribution rights.
- Commercial use: Requires a separate written license. Contact James Daley for terms and pricing.
- Personal/non-commercial projects: Contact James Daley to discuss availability of a personal-use license.
Full terms: license.md
This repo includes an Xcode project template for faster setup.
./Scripts/install-forsetti-xcode-template.sh- Copy the folder
XcodeTemplates/Project Templates/Forsetti/to:~/Library/Developer/Xcode/Templates/Project Templates/Forsetti/ - Restart Xcode.
After installation, create a new project: File > New > Project > Multiplatform > Forsetti App.
The template includes educational comments in ForsettiBootstrap.swift explaining each setup step.
To uninstall: Scripts/uninstall-forsetti-xcode-template.sh
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.