From 92ac8e8263bb995bebaaac874951db1e2e8cba3d Mon Sep 17 00:00:00 2001 From: Zaphhh Date: Sun, 6 Apr 2025 00:38:25 +0100 Subject: [PATCH] Initial Commit. --- .gitignore | 10 ++++++ .swift-format | 11 ++++++ Package.resolved | 15 ++++++++ Package.swift | 39 +++++++++++++++++++++ Sources/MetaJSON/State.swift | 68 ++++++++++++++++++++++++++++++++++++ Tests/Test.swift | 16 +++++++++ 6 files changed, 159 insertions(+) create mode 100644 .gitignore create mode 100644 .swift-format create mode 100644 Package.resolved create mode 100644 Package.swift create mode 100644 Sources/MetaJSON/State.swift create mode 100644 Tests/Test.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d75405a --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc + +/.vscode \ No newline at end of file diff --git a/.swift-format b/.swift-format new file mode 100644 index 0000000..9c8c797 --- /dev/null +++ b/.swift-format @@ -0,0 +1,11 @@ +{ + "version": 1, + "lineLength": 100, + "indentation": { + "spaces": 4 + }, + "maximumBlankLines": 1, + "respectsExistingLineBreaks": true, + "lineBreakBeforeControlFlowKeywords": true, + "lineBreakBeforeEachArgument": true +} \ No newline at end of file diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..669a8c7 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "d88be4258c389a35639a4f2bbde8f9f0c10823cb590d44070870e9cf382a9629", + "pins" : [ + { + "identity" : "meta", + "kind" : "remoteSourceControl", + "location" : "https://git.aparoksha.dev/aparoksha/meta", + "state" : { + "branch" : "main", + "revision" : "ce9c5bf7d1e8da54d64515f3d0e0fa3b05ad4355" + } + } + ], + "version" : 3 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..cf17380 --- /dev/null +++ b/Package.swift @@ -0,0 +1,39 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "meta-json", + platforms: [ + .macOS(.v10_15) + ], + products: [ + .library( + name: "MetaJSON", + targets: ["MetaJSON"] + ) + ], + dependencies: [ + .package(url: "https://git.aparoksha.dev/aparoksha/meta", branch: "main") + ], + targets: [ + .target( + name: "MetaJSON", + dependencies: [ + .product(name: "Meta", package: "meta") + ], + path: "Sources" + + ), + .executableTarget( + name: "Tests", + dependencies: ["MetaJSON"], + path: "Tests" + ), + + ], + swiftLanguageModes: [ + .v5 + ] +) diff --git a/Sources/MetaJSON/State.swift b/Sources/MetaJSON/State.swift new file mode 100644 index 0000000..58c0a8e --- /dev/null +++ b/Sources/MetaJSON/State.swift @@ -0,0 +1,68 @@ +import Foundation +import Meta + +extension State where Value: Codable { + + /// Initialize a property remembered between launches using a JSON file. + /// - Parameters: + /// - wrappedValue: The default value if no stored value exists. + /// - stateID: Unique key for the value. + /// - forceUpdates: Whether to force update all available views when modified. + public init( + wrappedValue: @autoclosure @escaping () -> Value, + _ stateID: String, + forceUpdates: Bool = false + ) { + self.init( + wrappedValue: { + guard let data = try? Data(contentsOf: URL(fileURLWithPath: JSONDatabase.path)), + let storage = try? JSONDecoder().decode([String: Data].self, from: data), + let storedValueData = storage[stateID], + let value = try? JSONDecoder().decode(Value.self, from: storedValueData) + else { + return wrappedValue() + } + return value + }, + writeValue: { value in + var storage: [String: Data] = [:] + let url = URL(fileURLWithPath: JSONDatabase.path) + + if let data = try? Data(contentsOf: url), + let decoded = try? JSONDecoder().decode([String: Data].self, from: data) + { + storage = decoded + } + + if let encodedValue = try? JSONEncoder().encode(value) { + storage[stateID] = encodedValue + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + + if let newData = try? encoder.encode(storage) { + try? FileManager.default.createDirectory( + at: url.deletingLastPathComponent(), + withIntermediateDirectories: true + ) + try? newData.write(to: url, options: .atomic) + } + } + }, + + forceUpdates: forceUpdates + ) + } + +} + +public enum JSONDatabase { + + /// The path to the JSON file. + static var path = "./database.json" + + /// Optionally allow customization of the path. + public static func setPath(_ newPath: String) { + self.path = newPath + } +} diff --git a/Tests/Test.swift b/Tests/Test.swift new file mode 100644 index 0000000..2a73e70 --- /dev/null +++ b/Tests/Test.swift @@ -0,0 +1,16 @@ +import Meta +import MetaJSON + +/// The test function. +func main() { + @State("test") + var test = 0 + @State("other") + var other = 3 + print(test) + print(other) + test = 1 + other = 0 +} + +main()