I would also advise in using the main App
's init
method for this one, as it seems safe to use (any objections?).
What I usually do, that might be useful to share, is to have a couple of utility types, combined with the Builder
pattern.
/// An abstraction for a predefined set of functionality,
/// aimed to be ran once, at app startup.
protocol StartupProcess {
func run()
}
/// A convenience type used for running StartupProcesses.
/// Uses the Builder pattern for some coding eye candy.
final class StartupProcessService {
init() { }
/// Executes the passed-in StartupProcess by running it's "run()" method.
/// - Parameter process: A StartupProcess instance, to be initiated.
/// - Returns: Returns "self", as a means to chain invocations of StartupProcess instances.
@discardableResult
func execute(process: any StartupProcess) -> StartupProcessService {
process.run()
return self
}
}
and then we have some processes
struct CrashlyticsProcess: StartupProcess {
func run() {
// Do stuff, like SDK initialization, etc.
}
}
struct FirebaseProcess: StartupProcess {
func run() {
// Do stuff, like SDK initialization, etc.
}
}
struct AppearanceCustomizationProcess: StartupProcess {
func run() {
// Do stuff, like SDK initialization, etc.
}
}
and finally, running them
@main
struct TheApp: App {
init() {
initiateStartupProcesses()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
private extension TheApp {
func initiateStartupProcesses() {
StartupProcessService()
.execute(process: ExampleProcess())
.execute(process: FirebaseProcess())
.execute(process: AppearanceCustomizationProcess)
}
}
Seems quite nice and super clean.