From 15cafb85f47588c1580edc38aaf9dbed0dd6adfc Mon Sep 17 00:00:00 2001 From: david-swift Date: Tue, 11 Jun 2024 14:29:46 +0200 Subject: [PATCH] Move to approach without any breaking changes Read the file's value as soon as the state value is accessed for the first time --- Sources/Adwaita/Model/Data Flow/State.swift | 59 +++++++++++++------ .../Model/User Interface/App/App.swift | 9 +-- .../Model/User Interface/App/GTUIApp.swift | 2 +- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Sources/Adwaita/Model/Data Flow/State.swift b/Sources/Adwaita/Model/Data Flow/State.swift index c47f92b..842b9b2 100644 --- a/Sources/Adwaita/Model/Data Flow/State.swift +++ b/Sources/Adwaita/Model/Data Flow/State.swift @@ -37,7 +37,13 @@ public struct State: StateProtocol { /// Get and set the value without updating the views. public var rawValue: Value { get { - content.storage.value as! Value + if let readValue, !content.fetched { + let newValue = readValue() + content.storage.value = newValue + content.fetched = true + return newValue + } + return content.storage.value as! Value } nonmutating set { content.storage.value = newValue @@ -54,6 +60,8 @@ public struct State: StateProtocol { /// The function for updating the value in the settings file. private var writeValue: (() -> Void)? + /// The function for reading the value in the settings file. + var readValue: (() -> Value)? /// The value with an erased type. public var value: Any { @@ -97,6 +105,8 @@ public struct State: StateProtocol { var internalStorage: Storage? /// The initial value. private var initialValue: Value + /// Whether the value has already been fetched from the file stored on the system. + var fetched = false /// Initialize the content. /// - Parameter storage: The storage. @@ -159,15 +169,18 @@ public struct State: StateProtocol { /// Get the settings directory path. /// - Returns: The path. - private func dirPath() -> URL { - Self.userDataDir() - .appendingPathComponent(content.storage.folder ?? GTUIApp.appID, isDirectory: true) + private func dirPath() -> URL? { + guard let folder = content.storage.folder ?? GTUIApp.appID else { + return nil + } + return Self.userDataDir() + .appendingPathComponent(folder, isDirectory: true) } /// Get the settings file path. /// - Returns: The path. - private func filePath() -> URL { - dirPath().appendingPathComponent("\(content.storage.key ?? "temporary").json") + private func filePath() -> URL? { + dirPath()?.appendingPathComponent("\(content.storage.key ?? "temporary").json") } } @@ -187,32 +200,41 @@ extension State where Value: Codable { self.forceUpdates = forceUpdates content.storage.key = key content.storage.folder = folder - checkFile() - readValue() + let value = self + self.readValue = { + value.checkFile() + return value.readActualValue() ?? wrappedValue + } self.writeValue = writeCodableValue } /// Check whether the settings file exists, and, if not, create it. private func checkFile() { let fileManager = FileManager.default - if !fileManager.fileExists(atPath: dirPath().path) { + guard let dirPath = dirPath(), let filePath = filePath() else { + print("Stored state value accessed before initializing app id.") + return + } + if !fileManager.fileExists(atPath: dirPath.path) { try? fileManager.createDirectory( - at: .init(fileURLWithPath: dirPath().path), + at: .init(fileURLWithPath: dirPath.path), withIntermediateDirectories: true, attributes: nil ) } - if !fileManager.fileExists(atPath: filePath().path) { - fileManager.createFile(atPath: filePath().path, contents: .init(), attributes: nil) + if !fileManager.fileExists(atPath: filePath.path) { + fileManager.createFile(atPath: filePath.path, contents: .init(), attributes: nil) } } /// Update the local value with the value from the file. - private func readValue() { - let data = try? Data(contentsOf: filePath()) - if let data, let value = try? JSONDecoder().decode(Value.self, from: data) { - rawValue = value + private func readActualValue() -> Value? { + if let filePath = filePath(), + let data = try? Data(contentsOf: filePath), + let value = try? JSONDecoder().decode(Value.self, from: data) { + return value } + return nil } /// Update the value on the file with the local value. @@ -220,7 +242,10 @@ extension State where Value: Codable { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let data = try? encoder.encode(rawValue) - try? data?.write(to: filePath()) + guard let filePath = filePath() else { + return + } + try? data?.write(to: filePath) } } diff --git a/Sources/Adwaita/Model/User Interface/App/App.swift b/Sources/Adwaita/Model/User Interface/App/App.swift index 7528846..208d503 100644 --- a/Sources/Adwaita/Model/User Interface/App/App.swift +++ b/Sources/Adwaita/Model/User Interface/App/App.swift @@ -24,7 +24,7 @@ public protocol App { /// The app's application ID. - static var id: String { get } + var id: String { get } /// The app's windows. @SceneBuilder var scene: Scene { get } @@ -40,12 +40,8 @@ public protocol App { extension App { - @available(*, deprecated, message: "The 'id' property is removed. Please use the new static id instead.") - var id: String { Self.id } - /// The application's entry point. public static func main() { - GTUIApp.appID = Self.id let app = setupApp() app.run() } @@ -55,7 +51,7 @@ extension App { /// To run the app, call the ``GTUIApp/run(automaticSetup:manualSetup:)`` function. public static func setupApp() -> GTUIApp { var appInstance = self.init() - appInstance.app = GTUIApp(Self.id) { appInstance } + appInstance.app = GTUIApp(appInstance.id) { appInstance } GTUIApp.updateHandlers.append { force in var removeIndices: [Int] = [] for (index, window) in appInstance.app.sceneStorage.enumerated() { @@ -69,6 +65,7 @@ extension App { appInstance.app.sceneStorage.remove(at: index) } } + GTUIApp.appID = appInstance.id return appInstance.app } diff --git a/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift b/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift index 5ae3c10..1007d1b 100644 --- a/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift +++ b/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift @@ -13,7 +13,7 @@ public class GTUIApp { /// The handlers which are called when a state changes. static var updateHandlers: [(Bool) -> Void] = [] /// The app's id for the file name for storing the data. - static var appID: String! + static var appID: String? /// The pointer to the application. public var pointer: UnsafeMutablePointer? /// Fields for additional information.