Tusker/Tusker/LocalData.swift

158 lines
5.2 KiB
Swift

//
// LocalData.swift
// Tusker
//
// Created by Shadowfacts on 8/18/18.
// Copyright © 2018 Shadowfacts. All rights reserved.
//
import Foundation
import Combine
class LocalData: ObservableObject {
static let shared = LocalData()
let defaults: UserDefaults
private init() {
if ProcessInfo.processInfo.environment.keys.contains("UI_TESTING") {
defaults = UserDefaults(suiteName: "\(Bundle.main.bundleIdentifier!).uitesting")!
if ProcessInfo.processInfo.environment.keys.contains("UI_TESTING_LOGIN") {
accounts = [
UserAccountInfo(
id: UUID().uuidString,
instanceURL: URL(string: "http://localhost:8080")!,
clientID: "client_id",
clientSecret: "client_secret",
username: "admin",
accessToken: "access_token")
]
}
} else {
defaults = UserDefaults()
}
}
private let accountsKey = "accounts"
var accounts: [UserAccountInfo] {
get {
if let array = defaults.array(forKey: accountsKey) as? [[String: String]] {
return array.compactMap { (info) in
guard let id = info["id"],
let instanceURL = info["instanceURL"],
let url = URL(string: instanceURL),
let clientId = info["clientID"],
let secret = info["clientSecret"],
let accessToken = info["accessToken"] else {
return nil
}
return UserAccountInfo(id: id, instanceURL: url, clientID: clientId, clientSecret: secret, username: info["username"], accessToken: accessToken)
}
} else {
return []
}
}
set {
objectWillChange.send()
let array = newValue.map { (info) -> [String: String] in
var res = [
"id": info.id,
"instanceURL": info.instanceURL.absoluteString,
"clientID": info.clientID,
"clientSecret": info.clientSecret,
"accessToken": info.accessToken
]
if let username = info.username {
res["username"] = username
}
return res
}
defaults.set(array, forKey: accountsKey)
}
}
private let mostRecentAccountKey = "mostRecentAccount"
private var mostRecentAccount: String? {
get {
return defaults.string(forKey: mostRecentAccountKey)
}
set {
objectWillChange.send()
defaults.set(newValue, forKey: mostRecentAccountKey)
}
}
var onboardingComplete: Bool {
return !accounts.isEmpty
}
func addAccount(instanceURL url: URL, clientID: String, clientSecret secret: String, username: String?, accessToken: String) -> UserAccountInfo {
var accounts = self.accounts
if let index = accounts.firstIndex(where: { $0.instanceURL == url && $0.username == username }) {
accounts.remove(at: index)
}
let id = UUID().uuidString
let info = UserAccountInfo(id: id, instanceURL: url, clientID: clientID, clientSecret: secret, username: username, accessToken: accessToken)
accounts.append(info)
self.accounts = accounts
return info
}
func setUsername(for info: UserAccountInfo, username: String) {
var info = info
info.username = username
removeAccount(info)
accounts.append(info)
}
func removeAccount(_ info: UserAccountInfo) {
accounts.removeAll(where: { $0.id == info.id })
}
func getAccount(id: String) -> UserAccountInfo? {
return accounts.first(where: { $0.id == id })
}
func getMostRecentAccount() -> UserAccountInfo? {
guard onboardingComplete else { return nil }
let mostRecent: UserAccountInfo?
if let id = mostRecentAccount {
mostRecent = accounts.first { $0.id == id }
} else {
mostRecent = nil
}
return mostRecent ?? accounts.first!
}
func setMostRecentAccount(_ account: UserAccountInfo?) {
mostRecentAccount = account?.id
}
}
extension LocalData {
struct UserAccountInfo: Equatable, Hashable {
let id: String
let instanceURL: URL
let clientID: String
let clientSecret: String
fileprivate(set) var username: String!
let accessToken: String
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func ==(lhs: UserAccountInfo, rhs: UserAccountInfo) -> Bool {
return lhs.id == rhs.id
}
}
}
extension Notification.Name {
static let userLoggedOut = Notification.Name("Tusker.userLoggedOut")
static let addAccount = Notification.Name("Tusker.addAccount")
static let activateAccount = Notification.Name("Tusker.activateAccount")
}