Bootstrap Guide
Comprehensive guide to safely and efficiently initializing your dependency injection container at app startup. WeaveDI provides powerful bootstrap patterns supporting Swift 5/6 concurrency, test isolation, conditional initialization, and production-ready configuration patterns.
Overview
Core Goals
- 🎧 Centralized Setup: Initialize all dependencies in one place at app startup
- 🔒 Type Safety: Compile-time dependency verification
- ⚡ Performance: Optimized container initialization
- 🧪 Testing: Isolated test environments
Key Features
- 🔄 Concurrency Support: Full async/await and Swift 6 strict concurrency
- 🎯 Atomic Operations: Thread-safe container replacement
- 🔍 Environment Awareness: Different setups for dev/staging/production
- 🧬 Test Isolation: Clean slate for each test
Swift Version Compatibility
Feature | Swift 5.8+ | Swift 5.9+ | Swift 6.0+ |
---|---|---|---|
Basic Bootstrap | ✅ | ✅ | ✅ |
Async Bootstrap | ✅ | ✅ | ✅ |
Mixed Bootstrap | ✅ | ✅ | ✅ |
Actor Isolation | ⚠️ | ✅ | ✅ |
Strict Sendable | ❌ | ⚠️ | ✅ |
When to Use Bootstrap
Required Scenarios
- 🚀 App Launch: Always bootstrap at app startup
- 🧪 Unit Tests: Bootstrap before each test suite
- 🔄 Integration Tests: Bootstrap with test-specific configuration
- 🛠️ Environment Changes: Re-bootstrap when switching environments
Application Entry Points
SwiftUI App (Recommended)
@main
struct MyApp: App {
init() {
Task {
await bootstrapDependencies()
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
UIKit App
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Task {
await bootstrapDependencies()
}
return true
}
}
Synchronous Bootstrap
import WeaveDI
await WeaveDI.Container.bootstrap { container in
container.register(Logger.self) { ConsoleLogger() }
container.register(Networking.self) { DefaultNetworking() }
container.register(UserRepository.self) { UserRepositoryImpl() }
}
// After bootstrap, use WeaveDI.Container.shared.resolve(...) anywhere
let logger = WeaveDI.Container.shared.resolve(Logger.self)
Asynchronous Bootstrap
Use bootstrapAsync
when async initialization is needed (e.g., remote config, database connection).
let ok = await WeaveDI.Container.bootstrapAsync { container in
// Example: Load remote configuration
let config = try await RemoteConfig.load()
container.register(AppConfig.self) { config }
// Example: Initialize async resources
let db = try await Database.open()
container.register(Database.self) { db }
}
guard ok else { /* Handle failure (splash/alert/retry) */ return }
Note:
bootstrapAsync
can be configured tofatalError
in DEBUG builds and returnfalse
in RELEASE builds on failure. Current implementation provides Bool return with internal logging.
Mixed Bootstrap (sync + async)
Useful when you want core dependencies immediately and supplementary dependencies asynchronously.
@MainActor
await WeaveDI.Container.bootstrapMixed(
sync: { container in
container.register(Logger.self) { ConsoleLogger() }
container.register(Networking.self) { DefaultNetworking() }
},
async: { container in
// Async extended dependencies
let analytics = await AnalyticsClient.make()
container.register(AnalyticsClient.self) { analytics }
}
)
Bootstrap in Background Task
When you want to minimize app launch delay, perform async bootstrap in background.
WeaveDI.Container.bootstrapInTask { container in
let featureFlags = try await FeatureFlags.fetch()
container.register(FeatureFlags.self) { featureFlags }
}
Conditional Bootstrap
Use when you want to skip if already initialized.
let didInit = await WeaveDI.Container.bootstrapIfNeeded { container in
container.register(Logger.self) { ConsoleLogger() }
}
if !didInit {
// Already initialized
}
Async version is also available:
let didInit = await WeaveDI.Container.bootstrapAsyncIfNeeded { container in
let remote = try await RemoteConfig.load()
container.register(RemoteConfig.self) { remote }
}
Ensure Bootstrapped (Assert)
Use to enforce that bootstrap occurred before DI access.
WeaveDI.Container.ensureBootstrapped() // Precondition failure if not bootstrapped
Testing Guide
Use reset API when you want a clean container for each test.
@MainActor
override func setUp() async throws {
try await super.setUp()
await WeaveDI.Container.resetForTesting() // Only allowed in DEBUG builds
// Test-specific registration
WeaveDI.Container.shared.register(MockService.self) { MockService() }
}
Best Practices
- Single bootstrap: Call only once at app entry point (or test setUp)
- Handle failures: Prepare user experience path when async bootstrap fails
- Use mixed pattern: Register essential dependencies synchronously, supplementary asynchronously
- Ensure access: Use
ensureBootstrapped()
in development to catch mistakes early - Test isolation: Call
resetForTesting()
before each test