import Foundation /// Backing storage for `logger`. /// /// ## Safety /// This is only ever mutated once, almost immediately after the app is launched and /// well before we do any concurrency shenanigans. Subsequent reads are always safe /// since `Logger` is `Sendable`. nonisolated(unsafe) private var _logger: Logger? /// The global logger. @_spi(Backends) public var logger: Logger { guard let _logger else { let logger = Logger(label: "TestLogger") logger.trace("logger used before initialization") _logger = logger return logger } return _logger } /// An application. @MainActor public protocol App { /// The backend used to render the app. associatedtype Backend: BaseAppBackend /// The type of scene representing the content of the app. associatedtype Body: Scene /// Metadata loaded at app start up. /// /// By default SwiftCrossUI attempts to load metadata inserted by Swift /// Bundler if present. Used by backends' default ``App/backend`` /// implementations if not `nil`. static var metadata: AppMetadata? { get } /// The application's backend. var backend: Backend { get } /// The content of the app. @SceneBuilder var body: Body { get } /// Creates an instance of the app. /// /// This initializer is run before anything else, so you can perform early /// setup tasks in here, such as opening a database or preparing a /// dependency injection library. init() } /// Force refresh the entire scene graph. Used by hot reloading. If you need to do /// this in your own code then something has gone very wrong... @MainActor public var _forceRefresh: () -> Void = {} /// Metadata embedded by Swift Bundler, if present. Loaded at app start up. /// /// This will contain the app's metadata, if present, by the time ``App/init()`` /// gets called. @MainActor private var swiftBundlerAppMetadata: AppMetadata? /// An error encountered when parsing Swift Bundler metadata. private enum SwiftBundlerMetadataError: LocalizedError { case noExecutableURL case failedToReadExecutable case emptyMetadata case jsonNotDictionary(String) case missingAppIdentifier case missingAppVersion case badMetadataPointer var errorDescription: String? { switch self { case .noExecutableURL: "no executable URL" case .failedToReadExecutable: "executable failed to read itself (to extract metadata)" case .emptyMetadata: "metadata found but was empty" case .jsonNotDictionary: "root metadata JSON value wasn't an object" case .missingAppIdentifier: "missing 'appIdentifier' (of type String)" case .missingAppVersion: "missing 'appVersion' (of type String)" case .badMetadataPointer: """ bad metadata pointer returned by injected metadata function; \ this causes segfaults on some systems so metadata parsing has \ been skipped. update to a version of Swift Bundler newer than \ commit 7b9c6a45fa5d0266985d45a3d12bc8d9fd729b84 to restore \ metadata parsing """ } } } extension App { /// Metadata loaded at app start up. /// /// This will contain the app's metadata, if present, by the time /// ``App/init()`` gets called. public static var metadata: AppMetadata? { swiftBundlerAppMetadata } /// The default log handler for apps which don't specify a custom one. /// /// This simply outputs logs to standard error. /// /// # See Also /// - /// Runs the application. public static func main() { Backend.earlySetup() extractMetadataAndInitializeLogging() let app = Self() let backend = app.backend let _app = _App(app, backend: backend) _forceRefresh = { backend.runInMainThread { _app.refreshSceneGraph() } } _app.run() } private static func extractMetadataAndInitializeLogging() { _logger = Logger(label: "SwiftCrossUI") } } // MARK: - Metadata extraction extension App { private static func extractSwiftBundlerMetadata() throws -> AppMetadata? { return nil } }