English | 简体中文
An example demonstrating how to initialize and cache an application configuration using MonoTask with execution merging, retry logic, and TTL caching. It also shows how consumers can access specific configuration values or compose usage strings without re-fetching the configuration.
- Platforms:
- iOS 13.0+
- macOS 10.15+
- tvOS 13.0+
- watchOS 6.0+
- Swift: 5.5+
- Dependencies:
- Monstra framework (local development version)
git clone https://github.com/yangchenlarkin/Monstra.git
cd Monstra/Examples/MonoTask/ModuleInitialization# From the ModuleInitialization directory
xed Package.swiftOr manually in Xcode:
- Open Xcode
- Go to
File → Open... - Navigate to the
ModuleInitializationfolder - Select
Package.swift(not the root Monstra project) - Click Open
This opens the example as a standalone Swift package.
Minimal configuration model (see Sources/.../AppConfiguration.swift).
struct AppConfiguration: Codable, Hashable {
let config1: String
let config2: String
}Simulates fetching the configuration with small artificial latency and simple logging.
enum AppConfigurationAPI {
static func getAppConfiguration() async throws -> AppConfiguration { /* ... */ }
}Wraps MonoTask<AppConfiguration> to initialize and cache the configuration once (TTL uses .infinity). Includes retry with fixed intervals.
- Initialization:
initializeModule(completion:)— starts the fetch (forceUpdate=false). Consumers can call at app launch.
- Accessors (all leverage
task.asyncExecute()so they do not re-fetch once cached):getConfiguration(): returns the whole configuration asResult<AppConfiguration, Error>getConfig1(): returnsconfig1stringgetConfig2(): returnsconfig2stringuseConfig1(str:): returns a composed string usingconfig1useConfig2(str:): returns a composed string usingconfig2
main.swift initializes the module and then demonstrates multiple concurrent consumers reading/using the configuration.
let manager = AppConfigurationManager()
print("[ModuleInitialization] start initializeModule()")
manager.initializeModule { result in /* log success/failure */ }
Task { print(await manager.getConfig1()) }
Task { print(await manager.getConfig2()) }
Task { print(await manager.useConfig1(str: "main.swift")) }
Task { print(await manager.useConfig2(str: "main.swift")) }- Execution Merging: concurrent calls to accessors share one execution when not cached
- Retry Logic: fixed-interval retry is configured (2 attempts)
- Infinite TTL: configuration is cached for the app lifetime (unless you adapt strategy)
- Single Source of Truth: accessor functions do not duplicate fetches once initialized
From a sample run of this example:
[ModuleInitialization] start initializeModule()
[Mock api] start fetch configuration
[Mock api] did fetch configuration
[ModuleInitialization] initialized: config1=value-1, config2=value-2
success("value-1")
success("main.swift is using value-1")
success("value-2")
success("main.swift is using value-2")
What this demonstrates:
- Initialization triggers a single configuration fetch
- Subsequent accessor calls use the cached configuration
- Result values show both raw config access and composed usage strings
- AppConfigurationManager: Data/initialization layer wrapping
MonoTaskand the API - Domain Layer: Define a protocol if needed for DI/testing (e.g.,
AppConfigurationProvider) - Presentation Layer: Call
initializeModule()at startup; use accessors where needed
Package.swift— SPM manifest (Monstra dependency)Sources/ModuleInitialization/AppConfiguration.swift— configuration modelSources/ModuleInitialization/AppConfigurationAPI.swift— mocked data sourceSources/ModuleInitialization/AppConfigurationManager.swift— manager wrappingMonoTaskSources/ModuleInitialization/main.swift— initialization & accessors demo
MonoTask<AppConfiguration>withresultExpireDuration = .infinity(cache for app lifetime)- Retry:
.count(count: 2, intervalProxy: .fixed(timeInterval: 0.2)) - Accessors share the same cached result via
asyncExecute()
