| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- 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
- /// - <doc:Logging>
- /// 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
- }
- }
|