Merge pull request #34 from Amzd/State-property-on-app-fix

Fixed stored @State properties on App
This commit is contained in:
david-swift 2024-06-11 14:34:06 +02:00 committed by GitHub
commit 1723fcdf22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 45 additions and 19 deletions

View File

@ -4,3 +4,4 @@
- [Greg Cotten](https://github.com/gregcotten) - [Greg Cotten](https://github.com/gregcotten)
- [Zev Eisenberg](https://github.com/ZevEisenberg) - [Zev Eisenberg](https://github.com/ZevEisenberg)
- [Jay Wren](https://github.com/jrwren) - [Jay Wren](https://github.com/jrwren)
- [Amzd](https://github.com/amzd)

View File

@ -37,7 +37,13 @@ public struct State<Value>: StateProtocol {
/// Get and set the value without updating the views. /// Get and set the value without updating the views.
public var rawValue: Value { public var rawValue: Value {
get { 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 { nonmutating set {
content.storage.value = newValue content.storage.value = newValue
@ -54,6 +60,8 @@ public struct State<Value>: StateProtocol {
/// The function for updating the value in the settings file. /// The function for updating the value in the settings file.
private var writeValue: (() -> Void)? private var writeValue: (() -> Void)?
/// The function for reading the value in the settings file.
var readValue: (() -> Value)?
/// The value with an erased type. /// The value with an erased type.
public var value: Any { public var value: Any {
@ -97,6 +105,8 @@ public struct State<Value>: StateProtocol {
var internalStorage: Storage? var internalStorage: Storage?
/// The initial value. /// The initial value.
private var initialValue: 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. /// Initialize the content.
/// - Parameter storage: The storage. /// - Parameter storage: The storage.
@ -159,15 +169,18 @@ public struct State<Value>: StateProtocol {
/// Get the settings directory path. /// Get the settings directory path.
/// - Returns: The path. /// - Returns: The path.
private func dirPath() -> URL { private func dirPath() -> URL? {
Self.userDataDir() guard let folder = content.storage.folder ?? GTUIApp.appID else {
.appendingPathComponent(content.storage.folder ?? GTUIApp.appID, isDirectory: true) return nil
}
return Self.userDataDir()
.appendingPathComponent(folder, isDirectory: true)
} }
/// Get the settings file path. /// Get the settings file path.
/// - Returns: The path. /// - Returns: The path.
private func filePath() -> URL { private func filePath() -> URL? {
dirPath().appendingPathComponent("\(content.storage.key ?? "temporary").json") dirPath()?.appendingPathComponent("\(content.storage.key ?? "temporary").json")
} }
} }
@ -187,32 +200,41 @@ extension State where Value: Codable {
self.forceUpdates = forceUpdates self.forceUpdates = forceUpdates
content.storage.key = key content.storage.key = key
content.storage.folder = folder content.storage.folder = folder
checkFile() let value = self
readValue() self.readValue = {
value.checkFile()
return value.readActualValue() ?? wrappedValue
}
self.writeValue = writeCodableValue self.writeValue = writeCodableValue
} }
/// Check whether the settings file exists, and, if not, create it. /// Check whether the settings file exists, and, if not, create it.
private func checkFile() { private func checkFile() {
let fileManager = FileManager.default 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( try? fileManager.createDirectory(
at: .init(fileURLWithPath: dirPath().path), at: .init(fileURLWithPath: dirPath.path),
withIntermediateDirectories: true, withIntermediateDirectories: true,
attributes: nil attributes: nil
) )
} }
if !fileManager.fileExists(atPath: filePath().path) { if !fileManager.fileExists(atPath: filePath.path) {
fileManager.createFile(atPath: filePath().path, contents: .init(), attributes: nil) fileManager.createFile(atPath: filePath.path, contents: .init(), attributes: nil)
} }
} }
/// Update the local value with the value from the file. /// Update the local value with the value from the file.
private func readValue() { private func readActualValue() -> Value? {
let data = try? Data(contentsOf: filePath()) if let filePath = filePath(),
if let data, let value = try? JSONDecoder().decode(Value.self, from: data) { let data = try? Data(contentsOf: filePath),
rawValue = value let value = try? JSONDecoder().decode(Value.self, from: data) {
return value
} }
return nil
} }
/// Update the value on the file with the local value. /// Update the value on the file with the local value.
@ -220,7 +242,10 @@ extension State where Value: Codable {
let encoder = JSONEncoder() let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted encoder.outputFormatting = .prettyPrinted
let data = try? encoder.encode(rawValue) let data = try? encoder.encode(rawValue)
try? data?.write(to: filePath()) guard let filePath = filePath() else {
return
}
try? data?.write(to: filePath)
} }
} }

View File

@ -25,6 +25,7 @@ public protocol App {
/// The app's application ID. /// The app's application ID.
var id: String { get } var id: String { get }
/// The app's windows. /// The app's windows.
@SceneBuilder var scene: Scene { get } @SceneBuilder var scene: Scene { get }
// swiftlint:disable implicitly_unwrapped_optional // swiftlint:disable implicitly_unwrapped_optional

View File

@ -13,8 +13,7 @@ public class GTUIApp {
/// The handlers which are called when a state changes. /// The handlers which are called when a state changes.
static var updateHandlers: [(Bool) -> Void] = [] static var updateHandlers: [(Bool) -> Void] = []
/// The app's id for the file name for storing the data. /// The app's id for the file name for storing the data.
static var appID = "temporary" static var appID: String?
/// The pointer to the application. /// The pointer to the application.
public var pointer: UnsafeMutablePointer<GtkApplication>? public var pointer: UnsafeMutablePointer<GtkApplication>?
/// Fields for additional information. /// Fields for additional information.