165 lines
4.6 KiB
Swift
165 lines
4.6 KiB
Swift
//
|
|
// Session.swift
|
|
// PrivadoVPN
|
|
//
|
|
// Created by Juraldinio on 12/16/20.
|
|
// Copyright © 2020 Privado LLC. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
final class Session {
|
|
|
|
enum SessionError: Error {
|
|
case absent
|
|
// case setupRequire
|
|
}
|
|
|
|
typealias EmitterSessionRecordChangeType = (SessionRecord?)
|
|
typealias EmitterVPNChangeType = (VPNService?)
|
|
typealias EmitterVPNConnectionStateChangeType = (VPNConnectionState, VPNService?)
|
|
|
|
// MARK: - Properties
|
|
|
|
static private var session: Session?
|
|
|
|
/// Change VPN emitter.
|
|
let vpnEmitter = Emitter<EmitterVPNChangeType>()
|
|
/// Change VPN state emitter.
|
|
let vpnConnectionStateEmitter = Emitter<EmitterVPNConnectionStateChangeType>()
|
|
/// Change session record emitter.
|
|
let sessionEmitter = Emitter<EmitterSessionRecordChangeType>()
|
|
|
|
private let keychain: KeychainSwift
|
|
|
|
/// Current session record.
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Current VPN Service.
|
|
private(set) var currentVPN: VPNService?
|
|
|
|
/// Cancellation token.
|
|
private var cancellationToken: CancellationToken?
|
|
|
|
// MARK: - Static
|
|
|
|
@discardableResult
|
|
static func initialize(with key: String, keychain: KeychainSwift) -> Session {
|
|
if let session = Self.session { return session }
|
|
|
|
let session = Session(with: key, keychain: keychain)
|
|
Self.session = session
|
|
return session
|
|
}
|
|
|
|
@discardableResult
|
|
static func instance() throws -> Session {
|
|
guard let session = self.session else { throw SessionError.absent }
|
|
return session
|
|
}
|
|
|
|
// MARK: - Init
|
|
|
|
private init(with key: String, keychain: KeychainSwift) {
|
|
self.keychain = keychain
|
|
}
|
|
|
|
// MARK: - Interface
|
|
|
|
@discardableResult
|
|
func save(_ sessionRecord: SessionRecord?) -> Bool {
|
|
|
|
if let record = sessionRecord {
|
|
guard let encoded = try? JSONEncoder().encode(record) else { return false }
|
|
CometLogger.shared.event(name: PrivadoConstants.Event.source, attributes: ["Record": "Save"], secured: nil)
|
|
self.keychain.set(encoded, forKey: PrivadoConstants.sessionKey)
|
|
} else {
|
|
CometLogger.shared.event(name: PrivadoConstants.Event.source, attributes: ["Record": "Clear"], secured: nil)
|
|
self.keychain.set(Data(), forKey: PrivadoConstants.sessionKey)
|
|
}
|
|
|
|
self.currentRecord = sessionRecord
|
|
|
|
return true
|
|
}
|
|
|
|
@discardableResult
|
|
func restore() -> Bool {
|
|
|
|
guard let data = self.keychain.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
|
|
}
|
|
|
|
func switchVPNService(_ sevice: VPNService) {
|
|
|
|
self.currentVPN = sevice
|
|
|
|
// Observe state changing!
|
|
let vpn = sevice
|
|
sevice.stateEmitter.addReaction { [weak self, weak vpn] (state, service) -> ShouldContinueReceiveNotifications in
|
|
|
|
guard let self = self
|
|
, let current = vpn else {
|
|
return false
|
|
}
|
|
|
|
guard current.id == service.id else { return true }
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
guard let self = self else { return }
|
|
self.vpnConnectionStateEmitter.invoke((state, current))
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
|
|
guard let self = self else { return }
|
|
|
|
self.vpnEmitter.invoke(self.currentVPN)
|
|
self.vpnConnectionStateEmitter.invoke((self.currentVPN?.state ?? .disconnected, self.currentVPN))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
func current(location: GeoLocation?) {
|
|
guard let location = location else { return }
|
|
Session.shared().location = location
|
|
}
|
|
|
|
///////////
|
|
|
|
|
|
let locationEmitter = Emitter<EmitterLocationChangeType>()
|
|
|
|
var location: GeoLocation? {
|
|
didSet {
|
|
DispatchQueue.main.async { [weak self] in
|
|
guard let self = self else { return }
|
|
self.locationEmitter.invoke(self.location)
|
|
}
|
|
}
|
|
}
|
|
*/
|