-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathAppState.swift
More file actions
106 lines (93 loc) · 3.52 KB
/
AppState.swift
File metadata and controls
106 lines (93 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import CryptoKit
import Foundation
import JSONRPC
import Logger
import Status
public extension JSONValue {
subscript(key: String) -> JSONValue? {
if case .hash(let dict) = self {
return dict[key]
}
return nil
}
var stringValue: String? {
if case .string(let value) = self {
return value
}
return nil
}
static func convertToJSONValue<T: Codable>(_ object: T) -> JSONValue? {
do {
let data = try JSONEncoder().encode(object)
let jsonValue = try JSONDecoder().decode(JSONValue.self, from: data)
return jsonValue
} catch {
Logger.client.info("Error converting to JSONValue: \(error)")
return nil
}
}
}
public class AppState {
public static let shared = AppState()
private var cache: [String: [String: JSONValue]] = [:]
private let cacheFileName = "appstate.json"
private let queue = DispatchQueue(label: "com.github.AppStateCacheQueue")
private var loadStatus: [String: Bool] = [:]
private init() {
cache[""] = [:] // initialize a default cache if no user exists
initCacheForUserIfNeeded()
}
func toHash(contents: String, _ length: Int = 16) -> String {
let data = Data(contents.utf8)
let hashData = SHA256.hash(data: data)
let hashValue = hashData.compactMap { String(format: "%02x", $0 ) }.joined()
let index = hashValue.index(hashValue.startIndex, offsetBy: length)
return String(hashValue[..<index])
}
public func update<T: Codable>(key: String, value: T) {
queue.async {
let userName = Status.currentUser() ?? ""
self.initCacheForUserIfNeeded(userName)
self.cache[userName]![key] = JSONValue.convertToJSONValue(value)
self.saveCacheForUser(userName)
}
}
public func get(key: String) -> JSONValue? {
return queue.sync {
let userName = Status.currentUser() ?? ""
initCacheForUserIfNeeded(userName)
return (self.cache[userName] ?? [:])[key]
}
}
private func configFilePath(userName: String) -> URL {
return ConfigPathUtils.configFilePath(userName: userName, fileName: cacheFileName)
}
private func saveCacheForUser(_ userName: String? = nil) {
if let user = userName ?? Status.currentUser(), !user.isEmpty { // save cache for non-empty user
let cacheFilePath = configFilePath(userName: user)
do {
let data = try JSONEncoder().encode(self.cache[user] ?? [:])
try data.write(to: cacheFilePath)
} catch {
Logger.client.info("Failed to save AppState cache: \(error)")
}
}
}
private func initCacheForUserIfNeeded(_ userName: String? = nil) {
if let user = userName ?? Status.currentUser(), !user.isEmpty,
loadStatus[user] != true { // load cache for non-empty user
self.loadStatus[user] = true
self.cache[user] = [:]
let cacheFilePath = configFilePath(userName: user)
guard FileManager.default.fileExists(atPath: cacheFilePath.path) else {
return
}
do {
let data = try Data(contentsOf: cacheFilePath)
self.cache[user] = try JSONDecoder().decode([String: JSONValue].self, from: data)
} catch {
Logger.client.info("Failed to load AppState cache: \(error)")
}
}
}
}