105 lines
3.2 KiB
Swift

//
// App.swift
// Meta
//
// Created by david-swift on 01.07.24.
//
/// A structure conforming to `App` is the entry point of your app.
///
/// ```swift
/// @main
/// struct Test: App {
///
/// let id = "io.github.AparokshaUI.TestApp"
/// var app: BackendApp!
///
/// var scene: Scene {
/// WindowScene()
/// }
///
/// }
/// ```
///
public protocol App {
/// The app storage type.
associatedtype Storage: AppStorage
/// The app's application ID.
var id: String { get }
/// The app's scene.
@SceneBuilder var scene: Scene { get }
// swiftlint:disable implicitly_unwrapped_optional
/// The app storage.
var app: Storage! { get set }
// swiftlint:enable implicitly_unwrapped_optional
/// An app has to have an `init()` initializer.
init()
}
extension App {
/// The application's entry point.
public static func main() {
let app = setupApp()
app.app.run {
for element in app.scene where element as? Storage.SceneElementType != nil {
element.setupInitialContainers(app: app.app)
}
}
}
/// Initialize and get the app with the app storage.
/// - Returns: The app instance.
///
/// To run the app, call the ``AppStorage/run(setup:)`` function.
public static func setupApp() -> Self {
var appInstance = self.init()
appInstance.app = Storage(id: appInstance.id)
appInstance.app.storage.app = { appInstance }
StateManager.addUpdateHandler { force in
var updateProperties = force
for property in appInstance.getState() {
if let oldID = appInstance.app.storage.stateStorage[property.key]?.id {
StateManager.changeID(old: oldID, new: property.value.id)
appInstance.app.storage.stateStorage[property.key]?.id = property.value.id
}
if StateManager.getUpdateState(id: property.value.id) {
updateProperties = true
}
}
var removeIndices: [Int] = []
for (index, element) in appInstance.app.storage.sceneStorage.enumerated() {
if element.destroy {
removeIndices.insert(index, at: 0)
} else if let scene = appInstance.scene.first(
where: { $0.id == element.id }
) as? Storage.SceneElementType as? SceneElement {
scene.update(element, app: appInstance.app, updateProperties: updateProperties)
}
}
for index in removeIndices {
appInstance.app.storage.sceneStorage.remove(at: index)
}
}
StateManager.appID = appInstance.id
let state = appInstance.getState()
appInstance.app.storage.stateStorage = state
return appInstance
}
func getState() -> [String: StateProtocol] {
var state: [String: StateProtocol] = [:]
for property in Mirror(reflecting: self).children {
if let label = property.label, let value = property.value as? StateProtocol {
state[label] = value
}
}
return state
}
}