269 lines
9.8 KiB
Swift
269 lines
9.8 KiB
Swift
//
|
|
// AccountService.swift
|
|
// List
|
|
//
|
|
// Created by Igor Danich on 29.06.2020.
|
|
// Copyright © 2020 Igor Danich. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import LocalAuthentication
|
|
import Firebase
|
|
import FirebaseMessaging
|
|
import Alamofire
|
|
import Resolver
|
|
|
|
//swiftlint:disable:next identifier_name
|
|
@discardableResult func Accounts() -> Account.Service { .shared }
|
|
|
|
extension Notification.Name {
|
|
static let didChangeAccount = Notification.Name("Account.Service.didChangeAccount")
|
|
static let didUpdateLock = Notification.Name("Account.Service.didUpdateLock")
|
|
static let didUpdateAuthorization = Notification.Name("Account.Service.didUpdateAuthorization")
|
|
static let didChangeInterface = Notification.Name("Account.Interface.didUpdateTabBar")
|
|
}
|
|
|
|
extension Common.Model.Key {
|
|
fileprivate static let current = Common.Model.Key("Account.Service.current")
|
|
fileprivate static let collection = Common.Model.Key("Account.Service.collection")
|
|
fileprivate static let didLoadApp = Common.Model.Key("Account.Service.didLoadApp")
|
|
fileprivate static let password = Common.Model.Key("Account.Service.password")
|
|
fileprivate static let isPushEnabled = Common.Model.Key("Account.Service.isPushEnabled")
|
|
fileprivate static let isBiometricksEnabled = Common.Model.Key("Account.Service.isBiometricksEnabled")
|
|
fileprivate static let isAccountMigrated = Common.Model.Key("Account.Service.isAccountMigrated")
|
|
}
|
|
|
|
extension Account {
|
|
class Service: Common.Service.Provider {
|
|
|
|
static let shared = Service()
|
|
let cityId = 16
|
|
|
|
private func account(for username: String?, keyType: KeyType) -> Account.Model? {
|
|
collection.first(where: { $0.username == username && $0.keyType == keyType })
|
|
}
|
|
|
|
private var _current: String? {
|
|
get { Settings()[.current] }
|
|
set { Settings()[.current] = newValue }
|
|
}
|
|
var current: Account.Model? {
|
|
get {
|
|
let username = _current?.components(separatedBy: "@").first
|
|
let keyTypeString = _current?.components(separatedBy: "@").last ?? ""
|
|
let keyType = Account.KeyType.init(rawValue: keyTypeString) ?? Account.KeyType.active
|
|
|
|
if let current = account(for: username, keyType: keyType) { return current }
|
|
if let account = collection.first {
|
|
_current = "\(account.username)@\(account.keyType.rawValue)"
|
|
setup()
|
|
}
|
|
return account(for: username, keyType: keyType)
|
|
}
|
|
set {
|
|
// removePush()
|
|
_current = "\(newValue?.username ?? "")@\(newValue?.keyType.rawValue ?? "")"
|
|
setup()
|
|
ResolverScope.userSession.reset()
|
|
Notification.post(name: .didChangeAccount)
|
|
}
|
|
}
|
|
var exists: Bool { current != nil }
|
|
|
|
let quota = Account.Service.Resources2()
|
|
|
|
var isLocked = false {
|
|
didSet { Notification.post(name: .didUpdateLock) }
|
|
}
|
|
|
|
let contacts = Contacts()
|
|
|
|
private var didLoadApp: Bool {
|
|
get { Settings()[.didLoadApp] }
|
|
set { Settings()[.didLoadApp] = newValue }
|
|
}
|
|
|
|
private init() {
|
|
if !didLoadApp {
|
|
didLoadApp = true
|
|
password = nil
|
|
}
|
|
updatePush()
|
|
}
|
|
|
|
private func setup() {
|
|
updatePush()
|
|
quota.fetch(clear: true)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// MARK: - Edit
|
|
extension Account.Service {
|
|
|
|
private(set) var collection: [Account.Model] {
|
|
get { Settings()[.collection] ?? [] }
|
|
set { Settings()[.collection] = newValue }
|
|
}
|
|
|
|
func add(username: String, privateKey: String, password: String, keyType: Account.KeyType, completion: Completion.Network) {
|
|
|
|
guard !collection.contains(where: { $0.username == username && $0.keyType == keyType }) else {
|
|
completion(.error(L10n.Error.Accounts.exists))
|
|
return
|
|
}
|
|
guard let publicKey = privateKey.publicKey() else {
|
|
completion(.error(L10n.Error.Accounts.keyInvalid))
|
|
return
|
|
}
|
|
collection.append(.init(username: username, publicKey: publicKey, privateKey: privateKey, keyType: keyType, password: password))
|
|
try? Account.Service.History.shared.load(username: username)
|
|
current = collection.last
|
|
completion(nil)
|
|
}
|
|
|
|
func remove(username: String, keyType: Account.KeyType) {
|
|
guard let index = collection.lastIndex(where: { $0.username == username && $0.keyType == keyType }) else { return }
|
|
let value = collection[index]
|
|
let current = self.current
|
|
collection.remove(at: index)
|
|
value.clear()
|
|
if value.username == current?.username { self.current = collection.first }
|
|
updatePush()
|
|
}
|
|
|
|
func updateCredentialsIfNeeded(accounts: [Account.Model.ChangeKey], password: String) {
|
|
let collection = self.collection
|
|
for accountToChange in accounts {
|
|
guard var account = collection.first(where: {
|
|
$0.username == accountToChange.username && $0.keyType.rawValue == accountToChange.permission.permName
|
|
}) else { continue }
|
|
|
|
account.publicKey = accountToChange.publicKey
|
|
account.updatePrivateKey(privateKey: accountToChange.privateKey, password: password)
|
|
}
|
|
self.collection = collection
|
|
}
|
|
|
|
func removeAll() {
|
|
collection.forEach({
|
|
remove(username: $0.username, keyType: $0.keyType)
|
|
})
|
|
}
|
|
}
|
|
|
|
// MARK: - Authorization
|
|
extension Account.Service {
|
|
private var password: String? {
|
|
get { Keychain()[biometric: .password] }
|
|
set { Keychain()[.password, password: newValue ?? ""] = newValue }
|
|
}
|
|
var isAuthorized: Bool {
|
|
if UserDefaults.standard.bool(forKey: "isPinPwdMode") {
|
|
if let pin = Account.Service.Authorize.shared.password,
|
|
let password = getPassword(password: pin),
|
|
!password.isEmpty,
|
|
password == pin,
|
|
password.count == 4 {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
func getPassword(password: String?) -> String? {
|
|
if let password = password {
|
|
return Keychain()[.password, password: password]
|
|
} else {
|
|
return Keychain()[biometric: .password]
|
|
}
|
|
}
|
|
var isBiometricksEnabled: Bool {
|
|
get { Settings()[.isBiometricksEnabled] }
|
|
set { Settings()[.isBiometricksEnabled] = newValue }
|
|
}
|
|
var isPasswordExists: Bool { Keychain().exist(.password) }
|
|
var isFaceIdAvailable: Bool { LAContext().biometryType == .faceID }
|
|
var isBiometricksAvailable: Bool {
|
|
let context = LAContext()
|
|
return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? context.biometryType != .none : false
|
|
}
|
|
func checkBiometricks(_ completion: @escaping (Bool) -> Void) {
|
|
LAContext().evaluatePolicy(
|
|
.deviceOwnerAuthenticationWithBiometrics,
|
|
localizedReason: "Enable bimetricks"
|
|
) { (success, _) in
|
|
DispatchQueue.main.async { completion(success) }
|
|
}
|
|
}
|
|
|
|
func update(password: String, old: String? = nil) {
|
|
self.password = password
|
|
if let old = old {
|
|
collection.forEach({ $0.update(password: password, old: old) })
|
|
}
|
|
}
|
|
|
|
func removePassword() { self.password = nil }
|
|
|
|
func validate(password: String) -> Bool { password.count >= 4 }
|
|
}
|
|
|
|
// MARK: - Push Notifications
|
|
extension Account.Service {
|
|
var isPushEnabled: Bool {
|
|
get { Settings()[.isPushEnabled] }
|
|
set { Settings()[.isPushEnabled] = newValue; updatePush() }
|
|
}
|
|
// private func updatePush() { isPushEnabled ? addPush() : removePush() }
|
|
private func updatePush() {
|
|
let variables = UpdatePushNotificationDeviceTokenGraphQLRequest.Variables(
|
|
deviceToken: Messaging.messaging().fcmToken ?? "",
|
|
deviceType: "IOS",
|
|
eosAccounts: collection.map({ $0.username }),
|
|
langCode: Common.Model.Language.current.rawValue
|
|
)
|
|
AF.request(
|
|
Network.servers.current.paycashQraphql,
|
|
method: .post,
|
|
parameters: UpdatePushNotificationDeviceTokenGraphQLRequest(variables: variables),
|
|
encoder: JSONParameterEncoder.default
|
|
)
|
|
.responseString(completionHandler: { print($0) })
|
|
.responseDecodable(of: GraphQLResponse<UpdatePushNotificationDeviceTokenGraphQLRequest.ResponseData>.self) {
|
|
print($0.value?.errors)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Migration
|
|
extension Account.Service {
|
|
var isAccountMigrated: Bool {
|
|
get { Settings()[.isAccountMigrated] }
|
|
set { Settings()[.isAccountMigrated] = newValue; }
|
|
}
|
|
|
|
func migrateAccountIfNeeded(password: String) {
|
|
if !isAccountMigrated,
|
|
let data = UserDefaults.standard.value(forKey: Common.Model.Key.collection.rawValue) as? Data,
|
|
let objects = try? JSONDecoder().decode([Account.OldModel].self, from: data) {
|
|
|
|
collection = []
|
|
var accounts: [Account.Model] = []
|
|
|
|
|
|
objects.forEach { oldAccount in
|
|
let account = Account.Model(username: oldAccount.username, publicKey: oldAccount.publicKey, keyType: .active)
|
|
account.migrate(password: password)
|
|
accounts.append(account)
|
|
}
|
|
collection = accounts
|
|
isAccountMigrated = true
|
|
Notification.post(name: .didChangeAccount)
|
|
}
|
|
}
|
|
}
|