211 lines
6.3 KiB
Swift
211 lines
6.3 KiB
Swift
//
|
|
// Session.swift
|
|
// Cyberlock
|
|
//
|
|
// Created by Jura on 8/29/19.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
typealias EmitterSessionRecordChangeType = (SessionRecord?)
|
|
typealias EmitterVPNChangeType = (VPNService?)
|
|
typealias EmitterVPNConnectionStateChangeType = (VPNConnectionState, VPNService?)
|
|
typealias EmitterLocationChangeType = (GeoLocation?)
|
|
|
|
enum SessionError: Error {
|
|
case setupRequire
|
|
}
|
|
|
|
final class Session {
|
|
|
|
typealias AuthenticationCompletionClosure = (_ result: Result<SessionRecord, ErrorResponse>) -> Void
|
|
|
|
private let router = Environment.router
|
|
private let key: String
|
|
private var cancellationToken: CancellationToken?
|
|
|
|
let locationEmitter = Emitter<EmitterLocationChangeType>()
|
|
let sessionEmitter = Emitter<EmitterSessionRecordChangeType>()
|
|
let vpnEmitter = Emitter<EmitterVPNChangeType>()
|
|
let vpnConnectionStateEmitter = Emitter<EmitterVPNConnectionStateChangeType>()
|
|
|
|
private(set) var currentRecord: SessionRecord? {
|
|
didSet {
|
|
let record = self.currentRecord
|
|
DispatchQueue.main.async { [weak self] in
|
|
guard let self = self else { return }
|
|
self.sessionEmitter.invoke(record)
|
|
}
|
|
}
|
|
}
|
|
|
|
var location: GeoLocation? {
|
|
didSet {
|
|
DispatchQueue.main.async { [weak self] in
|
|
guard let self = self else { return }
|
|
self.locationEmitter.invoke(self.location)
|
|
}
|
|
}
|
|
}
|
|
|
|
var currentVPN: VPNService? {
|
|
didSet {
|
|
|
|
if let vpn = self.currentVPN {
|
|
|
|
// Observe state changing!
|
|
vpn.stateEmitter.addReaction { [weak self, weak vpn] (state, service) -> ShouldContinueReceiveNotifications in
|
|
|
|
guard let current = vpn else {
|
|
return false
|
|
}
|
|
|
|
guard current.id == service.id else { return true }
|
|
|
|
DispatchQueue.main.async {
|
|
guard let self = self else { return }
|
|
self.vpnConnectionStateEmitter.invoke((state, vpn))
|
|
}
|
|
|
|
return self.isExist
|
|
}
|
|
}
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
|
|
guard let self = self else { return }
|
|
self.vpnConnectionStateEmitter.invoke((self.currentVPN?.state ?? .disconnected, self.currentVPN))
|
|
|
|
self.vpnEmitter.invoke(self.currentVPN)
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
var token: TokenRecord? {
|
|
return self.currentRecord?.token
|
|
}
|
|
|
|
var bestLocation: ServerRecord? {
|
|
didSet {
|
|
self.saveBestLocation()
|
|
}
|
|
}
|
|
|
|
// MARK: - Static
|
|
|
|
private static var instance: Session?
|
|
|
|
@discardableResult
|
|
static func shared(with key: String? = nil) -> Session {
|
|
|
|
if let session = self.instance { return session }
|
|
|
|
let instance = Session(with: key ?? "")
|
|
self.instance = instance
|
|
return instance
|
|
}
|
|
|
|
// MARK: - Init
|
|
|
|
private init(with key: String) {
|
|
self.key = key
|
|
self.restoreBestLocation()
|
|
}
|
|
|
|
// MARK: - Methods
|
|
|
|
func login(using username: String, password: String, then completion: @escaping AuthenticationCompletionClosure) {
|
|
|
|
guard !self.cancellationToken.isExist else { return }
|
|
|
|
let service = SessionService.login(username: username, password: password, key: self.key)
|
|
self.cancellationToken = self.router.requestDecodable(Either<SessionRecord, ErrorResponse>.self, service: service) { [weak self] result in
|
|
|
|
self?.cancellationToken = nil
|
|
|
|
result
|
|
.onSuccess { (either, _) in
|
|
switch either {
|
|
case let .firstType(record):
|
|
completion(.success(record))
|
|
case let .secondType(error):
|
|
completion(.failure(error))
|
|
}
|
|
}.onFail { error in
|
|
|
|
if error is DecodingError {
|
|
completion(.failure(ErrorResponse(code: ErrorResponse.Constants.decoding, reason: "\(error)")))
|
|
} else {
|
|
completion(.failure(ErrorResponse(code: ErrorResponse.Constants.undefined, reason: "\(error)")))
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func refresh() {
|
|
|
|
}
|
|
|
|
@discardableResult
|
|
private func restoreBestLocation() -> Bool {
|
|
let keys = KeychainSwift()
|
|
|
|
guard let data = keys.getData(PrivadoConstants.bestLocationKey)
|
|
, let record = try? JSONDecoder().decode(ServerRecord.self, from: data) else {
|
|
return false
|
|
}
|
|
|
|
self.bestLocation = record
|
|
return true
|
|
}
|
|
|
|
@discardableResult
|
|
private func saveBestLocation() -> Bool {
|
|
let keys = KeychainSwift()
|
|
if let record = self.bestLocation {
|
|
guard let encoded = try? JSONEncoder().encode(record) else { return false }
|
|
keys.set(encoded, forKey: PrivadoConstants.bestLocationKey)
|
|
} else {
|
|
keys.set(Data(), forKey: PrivadoConstants.bestLocationKey)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
@discardableResult
|
|
func save(_ sessionRecord: SessionRecord?) -> Bool {
|
|
let keys = KeychainSwift()
|
|
if let record = sessionRecord {
|
|
guard let encoded = try? JSONEncoder().encode(record) else { return false }
|
|
keys.set(encoded, forKey: PrivadoConstants.sessionKey)
|
|
} else {
|
|
keys.set(Data(), forKey: PrivadoConstants.sessionKey)
|
|
}
|
|
|
|
self.currentRecord = sessionRecord
|
|
|
|
return true
|
|
}
|
|
|
|
@discardableResult
|
|
func restore() -> Bool {
|
|
|
|
let keys = KeychainSwift()
|
|
|
|
guard let data = keys.getData(PrivadoConstants.sessionKey)
|
|
, let record = try? JSONDecoder().decode(SessionRecord.self, from: data) else {
|
|
return false
|
|
}
|
|
|
|
guard record.isValid else {
|
|
return false
|
|
}
|
|
|
|
self.currentRecord = record
|
|
return true
|
|
}
|
|
|
|
}
|