// // SharedConstants.swift // LockdowniOS // // Created by Johnny Lin on 8/8/19. // Copyright © 2019 Confirmed Inc. All rights reserved. // // These are constants and functions shared by the main app and the extensions import Foundation import CocoaLumberjackSwift import KeychainAccess let isTestFlight = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" var appInstallDate: Date? { if let documentsFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last { if let installDate = try! FileManager.default.attributesOfItem(atPath: documentsFolder.path)[.creationDate] as? Date { return installDate } } return nil } @discardableResult func appHasJustBeenUpgradedOrIsNewInstall() -> Bool { let currentVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String let versionOfLastRun = UserDefaults.standard.object(forKey: kVersionOfLastRun) as? String DDLogInfo("APP UPGRADED CHECK: LAST RUN \(versionOfLastRun ?? "n/a") | CURRENT \(currentVersion ?? "n/a")") UserDefaults.standard.set(currentVersion, forKey: kVersionOfLastRun) if (versionOfLastRun == nil || versionOfLastRun != currentVersion) { // either first time this check has occurred, or app was updated since last run DDLogInfo("APP UPGRADED CHECK: TRUE - LAST RUN \(versionOfLastRun ?? "n/a") | CURRENT \(currentVersion ?? "n/a")") return true } else { // nothing changed DDLogInfo("APP UPGRADED CHECK: FALSE") return false } } let reachability = Availability() let keychain = Keychain(service: "com.confirmed.tunnels").synchronizable(true) // MARK: - VPN Credentials let kVPNCredentialsKeyBase64 = "VPNCredentialsKeyBase64" let kVPNCredentialsId = "VPNCredentialsId" let kICloudContainer = "iCloud.com.confirmed.lockdown" let kOpenFirewallTunnelRecord = "OpenFirewallTunnelRemotely" let kCloseFirewallTunnelRecord = "CloseFirewallTunnelRemotely" let kRestartFirewallTunnelRecord = "RestartFirewallTunnelRemotely" struct VPNCredentials { var id: String = "" var keyBase64: String = "" } func setVPNCredentials(id: String, keyBase64: String) throws { DDLogInfo("Setting VPN Credentials: \(id), base64: \(keyBase64)") if (id == "") { throw "ID was blank" } if (keyBase64 == "") { throw "Key was blank" } do { try keychain.set(id, key: kVPNCredentialsId) try keychain.set(keyBase64, key: kVPNCredentialsKeyBase64) } catch { throw "Unable to set VPN credentials on keychain" } } func getVPNCredentials() -> VPNCredentials? { DDLogInfo("Getting stored VPN credentials") var id: String? = nil do { id = try keychain.get(kVPNCredentialsId) if id == nil { DDLogError("No stored credential id") return nil } } catch { DDLogError("Error getting stored VPN credentials id: \(error)") return nil } var keyBase64: String? = nil do { keyBase64 = try keychain.get(kVPNCredentialsKeyBase64) if keyBase64 == nil { DDLogError("No stored credential keyBase64") return nil } } catch { DDLogError("Error getting stored VPN credentials keyBase64: \(error)") return nil } DDLogInfo("Returning stored VPN credentials: \(id!) \(keyBase64!)") return VPNCredentials(id: id!, keyBase64: keyBase64!) } // MARK: - API Credentials let kAPICredentialsEmail = "APICredentialsEmail" let kAPICredentialsPassword = "APICredentialsPassword" struct APICredentials { var email: String = "" var password: String = "" } func setAPICredentials(email: String, password: String) throws { DDLogInfo("Setting API Credentials with email: \(email)") if (email == "") { throw "Email was blank" } if (password == "") { throw "Password was blank" } do { try keychain.set(email, key: kAPICredentialsEmail) try keychain.set(password, key: kAPICredentialsPassword) } catch { throw "Unable to set API credentials on keychain" } } func clearAPICredentials() { try? keychain.remove(kAPICredentialsEmail) try? keychain.remove(kAPICredentialsPassword) } func getAPICredentials() -> APICredentials? { print("Getting stored API credentials") var email: String? = nil do { email = try keychain.get(kAPICredentialsEmail) if email == nil { print("No stored API credential email") return nil } } catch { print("Error getting stored API credentials email: \(error)") return nil } var password: String? = nil do { password = try keychain.get(kAPICredentialsPassword) if password == nil { print("No stored API credential password") return nil } } catch { print("Error getting stored API credentials password: \(error)") return nil } print("Returning stored API credentials with email: \(email!)") return APICredentials(email: email!, password: password!) } func getAPICredentialsConfirmed() -> Bool { return defaults.bool(forKey: kAPICredentialsConfirmed) } func setAPICredentialsConfirmed(confirmed: Bool) { defaults.set(confirmed, forKey: kAPICredentialsConfirmed) } // MARK: - Extensions extension String: Error { // Error makes it easy to throw errors as one-liners func base64Encoded() -> String? { if let data = self.data(using: .utf8) { return data.base64EncodedString() } return nil } func base64Decoded() -> String? { if let data = Data(base64Encoded: self) { return String(data: data, encoding: .utf8) } return nil } } extension UIColor { static let confirmedBlue = UIColor(named: "Confirmed Blue") ?? UIColor.tunnelsBlue static let tunnelsBlue = UIColor(red: 0/255.0, green: 173/255.0, blue: 231/255.0, alpha: 1.0) static let tunnelsWarning = UIColor(red: 231/255.0, green: 76/255.0, blue: 68/255.0, alpha: 1.0) static let tunnelsDarkBlue = UIColor(red: 0/255.0, green: 117/255.0, blue: 157/255.0, alpha: 1.0) static let tunnelsLightBlue = UIColor(red: 223/255.0, green: 243/255.0, blue: 251/255.0, alpha: 1.0) static let paywallOrange = UIColor(red: 255/255, green: 171/255, blue: 0/255, alpha: 1) static let paywallNew = UIColor(red: 0.225, green: 0.219, blue: 0.6, alpha: 1.0) static let borderGray = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1) static let borderBlue = UIColor(red: 0, green: 0.678, blue: 0.906, alpha: 1) static let smallGrey = UIColor(red: 0.8, green: 0.8, blue: 0.8, alpha: 1) static var purplePaywall = UIColor(red: 134/255.0, green: 26/255.0, blue: 201/255.0, alpha: 1) static var purplePaywall2 = UIColor(red: 103/255.0, green: 26/255.0, blue: 201/255.0, alpha: 1) static var extraLightGray = UIColor(red: 242/255.0, green: 244/255.0, blue: 245/255.0, alpha: 1) static var gradientPink1 = UIColor(red: 0.788, green: 0.102, blue: 0.788, alpha: 1) static var gradientPink2 = UIColor(red: 0.405, green: 0.103, blue: 0.789, alpha: 1) static let tunnelsBlueTest = UIColor(red: 0/255.0, green: 173/255.0, blue: 231/255.0, alpha: 0.0) static let lockdownRed = UIColor(red: 214.0/255.0, green: 87.0/255.0, blue: 75.0/255.0, alpha: 1.0) static let panelSecondaryBackground = UIColor(named: "Panel Secondary Background") static let tableCellBackground = UIColor(named: "tableCellBackground") static let tableCellSelectedBackground = UIColor(named: "tableCellSelectedBackground") static let disabledGray = UIColor(red: 0.3, green: 0.3, blue: 0.3, alpha: 1) } extension UnicodeScalar { var hexNibble:UInt8 { let value = self.value if 48 <= value && value <= 57 { return UInt8(value - 48) } else if 65 <= value && value <= 70 { return UInt8(value - 55) } else if 97 <= value && value <= 102 { return UInt8(value - 87) } fatalError("\(self) not a legal hex nibble") } } extension Data { init(hex: String) { let scalars = hex.unicodeScalars var bytes = Array(repeating: 0, count: (scalars.count + 1) >> 1) for (index, scalar) in scalars.enumerated() { var nibble = scalar.hexNibble if index & 1 == 0 { nibble <<= 4 } bytes[index >> 1] |= nibble } self = Data(bytes: bytes) } } extension UIDevice { var modelName: String { var systemInfo = utsname() uname(&systemInfo) let machineMirror = Mirror(reflecting: systemInfo.machine) let identifier = machineMirror.children.reduce("") { identifier, element in guard let value = element.value as? Int8, value != 0 else { return identifier } return identifier + String(UnicodeScalar(UInt8(value))) } return identifier } } enum AppConfiguration { case Debug case TestFlight case AppStore } struct Config { private static let isTestFlight = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" static var isDebug: Bool { #if DEBUG return true #else return false #endif } static var appConfiguration: AppConfiguration { if isDebug { return .Debug } else if isTestFlight { return .TestFlight } else { return .AppStore } } } // MARK: - Fonts let fontRegular12 = UIFont(name: "Montserrat-Regular", size: 12)! let fontRegular14 = UIFont(name: "Montserrat-Regular", size: 14)! let fontRegular15 = UIFont(name: "Montserrat-Regular", size: 15)! let fontRegular17 = UIFont(name: "Montserrat-Regular", size: 17)! let fontMedium14 = UIFont(name: "Montserrat-Medium", size: 14)! let fontMedium11 = UIFont(name: "Montserrat-Medium", size: 11)! let fontMedium13 = UIFont(name: "Montserrat-Medium", size: 13)! let fontMedium15 = UIFont(name: "Montserrat-Medium", size: 15)! let fontMedium16 = UIFont(name: "Montserrat-Medium", size: 16)! let fontMedium17 = UIFont(name: "Montserrat-Medium", size: 17)! let fontMedium18 = UIFont(name: "Montserrat-Medium", size: 18)! let fontSemiBold13 = UIFont(name: "Montserrat-SemiBold", size: 13)! let fontSemiBold15 = UIFont(name: "Montserrat-SemiBold", size: 15)! let fontSemiBold15_5 = UIFont(name: "Montserrat-SemiBold", size: 15.5)! let fontSemiBold17 = UIFont(name: "Montserrat-SemiBold", size: 17)! let fontSemiBold22 = UIFont(name: "Montserrat-SemiBold", size: 22)! let fontBold11 = UIFont(name: "Montserrat-Bold", size: 11)! let fontBold13 = UIFont(name: "Montserrat-Bold", size: 13)! let fontBold15 = UIFont(name: "Montserrat-Bold", size: 15)! let fontBold17 = UIFont(name: "Montserrat-Bold", size: 17)! let fontBold18 = UIFont(name: "Montserrat-Bold", size: 18)! let fontBold22 = UIFont(name: "Montserrat-Bold", size: 22)! let fontBold24 = UIFont(name: "Montserrat-Bold", size: 24)! let fontBold26 = UIFont(name: "Montserrat-Bold", size: 26)! let fontBold28 = UIFont(name: "Montserrat-Bold", size: 28)! let fontBold34 = UIFont(name: "Montserrat-Bold", size: 34)!