diff --git a/apps/ios/ProtonVPN/Scenes/Profiles/ViewControllers/CreateProfileViewController.swift b/apps/ios/ProtonVPN/Scenes/Profiles/ViewControllers/CreateProfileViewController.swift index 92f86ff9a..6a76ab92a 100644 --- a/apps/ios/ProtonVPN/Scenes/Profiles/ViewControllers/CreateProfileViewController.swift +++ b/apps/ios/ProtonVPN/Scenes/Profiles/ViewControllers/CreateProfileViewController.swift @@ -101,19 +101,19 @@ class CreateProfileViewController: UITableViewController { return } - viewModel.saveProfile { success in + viewModel.saveProfile { [weak self] success in guard success else { return } if viewModel.editingExistingProfile { - self.profilesViewControllerDelegate?.showProfileEditedSuccessMessage() + self?.profilesViewControllerDelegate?.showProfileEditedSuccessMessage() } else { - self.profilesViewControllerDelegate?.showProfileCreatedSuccessMessage() + self?.profilesViewControllerDelegate?.showProfileCreatedSuccessMessage() } - self.navigationController?.popViewController(animated: true) - self.profilesViewControllerDelegate?.reloadProfiles() + self?.navigationController?.popViewController(animated: true) + self?.profilesViewControllerDelegate?.reloadProfiles() } } diff --git a/apps/ios/ProtonVPN/Scenes/Settings/ViewModels/SettingsAccountViewModel.swift b/apps/ios/ProtonVPN/Scenes/Settings/ViewModels/SettingsAccountViewModel.swift index 7a9290bf6..e2e8d8891 100644 --- a/apps/ios/ProtonVPN/Scenes/Settings/ViewModels/SettingsAccountViewModel.swift +++ b/apps/ios/ProtonVPN/Scenes/Settings/ViewModels/SettingsAccountViewModel.swift @@ -42,7 +42,7 @@ final class SettingsAccountViewModel { AuthKeychainHandleFactory & NavigationServiceFactory - private var factory: Factory + private let factory: Factory private lazy var alertService: AlertService = factory.makeCoreAlertService() private lazy var appSessionManager: AppSessionManager = factory.makeAppSessionManager() diff --git a/apps/ios/ProtonVPN/Scenes/Settings/ViewModels/SettingsViewModel.swift b/apps/ios/ProtonVPN/Scenes/Settings/ViewModels/SettingsViewModel.swift index 4d511395e..8424a0b18 100644 --- a/apps/ios/ProtonVPN/Scenes/Settings/ViewModels/SettingsViewModel.swift +++ b/apps/ios/ProtonVPN/Scenes/Settings/ViewModels/SettingsViewModel.swift @@ -58,7 +58,6 @@ final class SettingsViewModel { AuthKeychainHandleFactory & NetworkingFactory - private let factory: Factory private lazy var propertiesManager: PropertiesManagerProtocol = factory.makePropertiesManager() diff --git a/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileManager.swift b/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileManager.swift index ba1ecc3f7..b824ceb6a 100644 --- a/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileManager.swift +++ b/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileManager.swift @@ -45,7 +45,7 @@ public protocol ProfileManagerFactory { func makeProfileManager() -> ProfileManager } -public class ProfileManager { +public final class ProfileManager { @Dependency(\.authKeychain) var authKeychain public let contentChanged = Notification.Name("ProfileManagerContentChanged") diff --git a/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileStorage.swift b/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileStorage.swift index acb29ee79..aa4ce9691 100644 --- a/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileStorage.swift +++ b/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileStorage.swift @@ -30,8 +30,15 @@ public protocol ProfileStorageFactory { // TODO: This would be a good use case for non-user defaults storage // - Profiles can get pretty big because they encode servers (big especially when they are SC servers) // - Once we move to something like Core Data for servers, we could model a relationship between profiles and servers? -public class ProfileStorage { - private static let storageVersion = 1 +public final class ProfileStorage { + enum StorageVersion: Int, CaseIterable { + case v1 = 1 + case v2 = 2 + + static let current: StorageVersion = .v2 + } + + private static let storageVersion = StorageVersion.v2 private static let versionKey = "profileCacheVersion" public static let contentChanged = Notification.Name("ProfileStorageContentChanged") @@ -53,34 +60,46 @@ public class ProfileStorage { func fetch() -> [Profile] { @Dependency(\.defaultsProvider) var provider - guard let storageKey = storageKey() else { - return [] - } - - let version = provider.getDefaults().integer(forKey: Self.versionKey) - var profiles = [Profile]() - if version == Self.storageVersion { - profiles = fetchFromMemory(storageKey: storageKey) - } - - if systemProfilesPresent(in: profiles) { - profiles = removeSystemProfiles(in: profiles) - store(profiles) + + var profiles: [Profile] = [] + + for versionCheck in StorageVersion.allCases { + guard let storageKey = storageKey(for: versionCheck) else { + return [] + } + + let version = provider.getDefaults().integer(forKey: Self.versionKey) + if version == versionCheck.rawValue { + profiles.append(contentsOf: fetchFromMemory(storageKey: storageKey)) + } + + if systemProfilesPresent(in: profiles) { + profiles = removeSystemProfiles(in: profiles) + store(profiles) + } } return profiles } - func store(_ profiles: [Profile]) { - guard let storageKey = storageKey() else { return } + func store(_ profiles: [Profile], storageVersion: StorageVersion = .current) { + guard let storageKey = storageKey(for: storageVersion) else { + log.error("Unable to store profiles without a storage key.", category: .persistence) + return + } storeInMemory(profiles, storageKey: storageKey) DispatchQueue.main.async { NotificationCenter.default.post(name: Self.contentChanged, object: profiles) } } // MARK: - Private functions - private func storageKey() -> String? { - guard let username = authKeychain.username else { return nil } - return "profiles_" + username + + private func storageKey(for version: StorageVersion) -> String? { + switch version { + case .v1: + return authKeychain.username.map { "profiles_" + $0 } + case .v2: + return authKeychain.userId.map { "profiles_" + $0 } + } } private func fetchFromMemory(storageKey: String) -> [Profile] { @@ -109,7 +128,7 @@ public class ProfileStorage { private func storeInMemory(_ profiles: [Profile], storageKey: String) { @Dependency(\.defaultsProvider) var provider - provider.getDefaults().set(Self.storageVersion, forKey: Self.versionKey) + provider.getDefaults().set(Self.storageVersion.rawValue, forKey: Self.versionKey) let archivedData = try? encoder.encode(profiles) provider.getDefaults().set(archivedData, forKey: storageKey) } diff --git a/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileUtility.swift b/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileUtility.swift index 88630ccbc..2ca479313 100644 --- a/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileUtility.swift +++ b/libraries/LegacyCommon/Sources/LegacyCommon/Management/Profile/ProfileUtility.swift @@ -29,7 +29,7 @@ enum ProfileUtilityOperationOutcome { case nameInUse } -public class ProfileUtility { +public final class ProfileUtility { public static func index(for serverType: ServerType) -> Int { switch serverType { diff --git a/libraries/NEHelper/Sources/VPNShared/Authentication/AuthCredentials.swift b/libraries/NEHelper/Sources/VPNShared/Authentication/AuthCredentials.swift index d8491145e..3c43c286f 100644 --- a/libraries/NEHelper/Sources/VPNShared/Authentication/AuthCredentials.swift +++ b/libraries/NEHelper/Sources/VPNShared/Authentication/AuthCredentials.swift @@ -23,7 +23,7 @@ import Foundation import Ergonomics -public class AuthCredentials: NSObject, NSSecureCoding, Codable { +public final class AuthCredentials: NSObject, NSSecureCoding, Codable { static let VERSION: Int = 0 // Current build version. public static var supportsSecureCoding: Bool = true diff --git a/libraries/NEHelper/Sources/VPNShared/Authentication/AuthKeychain.swift b/libraries/NEHelper/Sources/VPNShared/Authentication/AuthKeychain.swift index 641d46aad..5f258dd6a 100644 --- a/libraries/NEHelper/Sources/VPNShared/Authentication/AuthKeychain.swift +++ b/libraries/NEHelper/Sources/VPNShared/Authentication/AuthKeychain.swift @@ -71,7 +71,7 @@ extension DependencyValues { } } -public class AuthKeychain { +public final class AuthKeychain { public static let clearNotification = Notification.Name("AuthKeychain.clear") private struct StorageKey {