86 行
2.6 KiB
Swift
86 行
2.6 KiB
Swift
|
|
import Foundation
|
||
|
|
import Security
|
||
|
|
|
||
|
|
final class LicenseStore {
|
||
|
|
private let keychainService = "com.xuqm.license"
|
||
|
|
private let defaults: UserDefaults
|
||
|
|
|
||
|
|
init() {
|
||
|
|
defaults = UserDefaults(suiteName: "xuqm_license") ?? .standard
|
||
|
|
}
|
||
|
|
|
||
|
|
var token: String? {
|
||
|
|
get { keychainGet("token") }
|
||
|
|
set { keychainSet("token", value: newValue) }
|
||
|
|
}
|
||
|
|
|
||
|
|
var deviceId: String? {
|
||
|
|
get { keychainGet("deviceId") }
|
||
|
|
set { keychainSet("deviceId", value: newValue) }
|
||
|
|
}
|
||
|
|
|
||
|
|
var status: String? {
|
||
|
|
get { defaults.string(forKey: "status") }
|
||
|
|
set { defaults.set(newValue, forKey: "status") }
|
||
|
|
}
|
||
|
|
|
||
|
|
// Stored as Double (milliseconds since epoch) to match Android's currentTimeMillis
|
||
|
|
var statusTime: Double {
|
||
|
|
get { defaults.double(forKey: "statusTime") }
|
||
|
|
set { defaults.set(newValue, forKey: "statusTime") }
|
||
|
|
}
|
||
|
|
|
||
|
|
var appKey: String? {
|
||
|
|
get { defaults.string(forKey: "appKey") }
|
||
|
|
set { defaults.set(newValue, forKey: "appKey") }
|
||
|
|
}
|
||
|
|
|
||
|
|
func clear() {
|
||
|
|
token = nil
|
||
|
|
deviceId = nil
|
||
|
|
status = nil
|
||
|
|
statusTime = 0
|
||
|
|
appKey = nil
|
||
|
|
}
|
||
|
|
|
||
|
|
private func keychainGet(_ key: String) -> String? {
|
||
|
|
let query: [CFString: Any] = [
|
||
|
|
kSecClass: kSecClassGenericPassword,
|
||
|
|
kSecAttrService: keychainService,
|
||
|
|
kSecAttrAccount: key,
|
||
|
|
kSecReturnData: true,
|
||
|
|
kSecMatchLimit: kSecMatchLimitOne,
|
||
|
|
]
|
||
|
|
var result: AnyObject?
|
||
|
|
guard SecItemCopyMatching(query as CFDictionary, &result) == errSecSuccess,
|
||
|
|
let data = result as? Data else { return nil }
|
||
|
|
return String(data: data, encoding: .utf8)
|
||
|
|
}
|
||
|
|
|
||
|
|
private func keychainSet(_ key: String, value: String?) {
|
||
|
|
guard let value else { keychainDelete(key); return }
|
||
|
|
let data = Data(value.utf8)
|
||
|
|
let query: [CFString: Any] = [
|
||
|
|
kSecClass: kSecClassGenericPassword,
|
||
|
|
kSecAttrService: keychainService,
|
||
|
|
kSecAttrAccount: key,
|
||
|
|
]
|
||
|
|
if SecItemCopyMatching(query as CFDictionary, nil) == errSecSuccess {
|
||
|
|
SecItemUpdate(query as CFDictionary, [kSecValueData: data] as CFDictionary)
|
||
|
|
} else {
|
||
|
|
var item = query
|
||
|
|
item[kSecValueData] = data
|
||
|
|
SecItemAdd(item as CFDictionary, nil)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private func keychainDelete(_ key: String) {
|
||
|
|
let query: [CFString: Any] = [
|
||
|
|
kSecClass: kSecClassGenericPassword,
|
||
|
|
kSecAttrService: keychainService,
|
||
|
|
kSecAttrAccount: key,
|
||
|
|
]
|
||
|
|
SecItemDelete(query as CFDictionary)
|
||
|
|
}
|
||
|
|
}
|