diff --git a/Apps/VisionOS/VisionOS.xcodeproj/project.pbxproj b/Apps/VisionOS/VisionOS.xcodeproj/project.pbxproj index c7c6eb95b..99b1273a6 100644 --- a/Apps/VisionOS/VisionOS.xcodeproj/project.pbxproj +++ b/Apps/VisionOS/VisionOS.xcodeproj/project.pbxproj @@ -13,6 +13,9 @@ 60F967472B91264B00A4E95E /* Splash in Frameworks */ = {isa = PBXBuildFile; productRef = 60F967462B91264B00A4E95E /* Splash */; }; 60F9674A2B91265B00A4E95E /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 60F967492B91265B00A4E95E /* MarkdownUI */; }; 60F9674E2B91269D00A4E95E /* Tracking in Frameworks */ = {isa = PBXBuildFile; productRef = 60F9674D2B91269D00A4E95E /* Tracking */; }; + 60F967532B91288400A4E95E /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F967502B91288400A4E95E /* AppState.swift */; }; + 60F967542B91288400A4E95E /* Payloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F967512B91288400A4E95E /* Payloads.swift */; }; + 60F967552B91288400A4E95E /* UserDefaultsCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F967522B91288400A4E95E /* UserDefaultsCodable.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -21,7 +24,10 @@ 60F967362B9125D000A4E95E /* MainScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainScreen.swift; sourceTree = ""; }; 60F967382B9125D100A4E95E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 60F9673D2B9125D100A4E95E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 60F9674B2B91267200A4E95E /* customerio-ios */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "customerio-ios"; path = ../../..; sourceTree = ""; }; + 60F9674B2B91267200A4E95E /* cio-ios-spl */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "customerio-ios"; path = ../../..; sourceTree = ""; }; + 60F967502B91288400A4E95E /* AppState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; + 60F967512B91288400A4E95E /* Payloads.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Payloads.swift; sourceTree = ""; }; + 60F967522B91288400A4E95E /* UserDefaultsCodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsCodable.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -59,6 +65,7 @@ 60F9672F2B9125D000A4E95E /* VisionOS */ = { isa = PBXGroup; children = ( + 60F9674F2B91288400A4E95E /* Storage */, 60F967342B9125D000A4E95E /* VisionOSApp.swift */, 60F967362B9125D000A4E95E /* MainScreen.swift */, 60F967382B9125D100A4E95E /* Assets.xcassets */, @@ -82,6 +89,16 @@ name = Frameworks; sourceTree = ""; }; + 60F9674F2B91288400A4E95E /* Storage */ = { + isa = PBXGroup; + children = ( + 60F967502B91288400A4E95E /* AppState.swift */, + 60F967512B91288400A4E95E /* Payloads.swift */, + 60F967522B91288400A4E95E /* UserDefaultsCodable.swift */, + ); + path = Storage; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -160,8 +177,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 60F967542B91288400A4E95E /* Payloads.swift in Sources */, 60F967372B9125D000A4E95E /* MainScreen.swift in Sources */, 60F967352B9125D000A4E95E /* VisionOSApp.swift in Sources */, + 60F967532B91288400A4E95E /* AppState.swift in Sources */, + 60F967552B91288400A4E95E /* UserDefaultsCodable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Apps/VisionOS/VisionOS/Storage/AppState.swift b/Apps/VisionOS/VisionOS/Storage/AppState.swift new file mode 100644 index 000000000..8f8508cee --- /dev/null +++ b/Apps/VisionOS/VisionOS/Storage/AppState.swift @@ -0,0 +1,23 @@ +import Foundation + +class AppState: ObservableObject { + static let shared = AppState() + + @Published var profile: Profile = .loadFromStorage() { + didSet { + UserDefaults.standard.setValue( + profile.toJson(), + forKey: Profile.storageKey() + ) + } + } + + @Published var workspaceSettings: WorkspaceSettings = .loadFromStorage() { + didSet { + UserDefaults.standard.setValue( + workspaceSettings.toJson(), + forKey: WorkspaceSettings.storageKey() + ) + } + } +} diff --git a/Apps/VisionOS/VisionOS/Storage/Payloads.swift b/Apps/VisionOS/VisionOS/Storage/Payloads.swift new file mode 100644 index 000000000..61cf4be4d --- /dev/null +++ b/Apps/VisionOS/VisionOS/Storage/Payloads.swift @@ -0,0 +1,66 @@ +import CioTracking +import Foundation + +extension Region: CaseIterable, Codable { + public static var allCases: [Region] = [.EU, .US] +} + +struct WorkspaceSettings: UserDefaultsCodable { + var siteId: String + var apiKey: String + var region: Region = .EU + + static func storageKey() -> String { + "UserDefaultsCodable" + } + + static func empty() -> Self { + WorkspaceSettings(siteId: "", apiKey: "") + } + + func isSet() -> Bool { + !siteId.isEmpty && !apiKey.isEmpty + } +} + +struct Profile: UserDefaultsCodable { + var id: String + var properties: [Property] + var loggedIn: Bool + + static func empty() -> Profile { + Profile(id: UUID().uuidString, properties: [], loggedIn: false) + } + + static func storageKey() -> String { + "Profile" + } +} + +struct Property: Codable, Identifiable, Comparable, Equatable { + static func < (lhs: Property, rhs: Property) -> Bool { + lhs.name == rhs.name + } + + let id: String + var name: String + var value: String + + init(name: String, value: String) { + self.id = UUID().uuidString + self.name = name + self.value = value + } +} + +extension [Property] { + func toDictionary() -> [String: String] { + var res: [String: String] = [:] + forEach { p in + if !p.name.isEmpty { + res[p.name] = p.value + } + } + return res + } +} diff --git a/Apps/VisionOS/VisionOS/Storage/UserDefaultsCodable.swift b/Apps/VisionOS/VisionOS/Storage/UserDefaultsCodable.swift new file mode 100644 index 000000000..e5b29bc75 --- /dev/null +++ b/Apps/VisionOS/VisionOS/Storage/UserDefaultsCodable.swift @@ -0,0 +1,34 @@ +import Foundation + +protocol UserDefaultsCodable: Codable { + static func storageKey() -> String + static func empty() -> Self +} + +extension UserDefaultsCodable { + func toJson() -> Data { + let encoder = JSONEncoder() + return try! encoder.encode(self) + } + + static func from(_ data: Data) -> Self? { + let decoder = JSONDecoder() + return try? decoder.decode(Self.self, from: data) + } + + static func loadFromStorage() -> Self { + if let data = + UserDefaults.standard.object(forKey: storageKey()) as? Data, + let storedInstance = from(data) { + return storedInstance + } + + let content = Self.empty() + UserDefaults.standard.setValue( + content.toJson(), + forKey: Self.storageKey() + ) + + return content + } +}