mirror of
https://github.com/twofas/2fas-ios.git
synced 2024-11-22 02:10:19 +01:00
TF-250 Main Repository
This commit is contained in:
parent
d96e63b385
commit
4d46d53e90
@ -19,7 +19,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum PINType: CaseIterable {
|
||||
public enum PINType: CaseIterable, Codable {
|
||||
case digits4
|
||||
case digits6
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ public final class LocalKeyEncryption: CommonLocalKeyEncryption {
|
||||
alphabet: Keys.LocalKeyEncryption.alphabet
|
||||
)
|
||||
|
||||
public init() {}
|
||||
|
||||
public func encrypt(_ str: String) -> String { encryption.encipher(str) }
|
||||
public func decrypt(_ str: String) -> String { encryption.decipher(str) }
|
||||
}
|
||||
|
@ -719,7 +719,7 @@
|
||||
C2A4C8372A11141D00AFD67C /* TokensNextTokenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2A4C8362A11141D00AFD67C /* TokensNextTokenView.swift */; };
|
||||
C2A4C8392A1153A200AFD67C /* TokensTokenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2A4C8382A1153A200AFD67C /* TokensTokenView.swift */; };
|
||||
C2A4C83B2A11736200AFD67C /* TokensCircleProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2A4C83A2A11736200AFD67C /* TokensCircleProgress.swift */; };
|
||||
C2A4D3152BC097F80001587C /* MainRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2A4D3142BC097F80001587C /* MainRepositoryImpl.swift */; };
|
||||
C2A4D3152BC097F80001587C /* MainRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2A4D3142BC097F80001587C /* MainRepository.swift */; };
|
||||
C2A4D32C2BC0B0C30001587C /* ServiceIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = C21AF6A526A237EA00BDAA37 /* ServiceIcon.swift */; };
|
||||
C2A4D32D2BC0B1AD0001587C /* TokenHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26D14642263A45100599101 /* TokenHandler.swift */; };
|
||||
C2A4D32E2BC0B1B20001587C /* Token.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2D672F625DB2BD3004BD697 /* Token.swift */; };
|
||||
@ -871,6 +871,8 @@
|
||||
C2B811D5284D1CFE009FD99D /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = C2B811D4284D1CFE009FD99D /* FirebaseCrashlytics */; };
|
||||
C2B811D9284D1D2C009FD99D /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = C2B811D8284D1D2C009FD99D /* FirebaseRemoteConfig */; };
|
||||
C2B811DB284D1D62009FD99D /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = C2B811DA284D1D62009FD99D /* FirebaseMessaging */; };
|
||||
C2B86D222BC3058A00AAAC63 /* UserDefaultsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2B86D212BC3058A00AAAC63 /* UserDefaultsRepository.swift */; };
|
||||
C2B86D242BC3070F00AAAC63 /* AppPIN.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2B86D232BC3070F00AAAC63 /* AppPIN.swift */; };
|
||||
C2B90EEE281EB3CF004B8AEC /* MainRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2B90EED281EB3CF004B8AEC /* MainRepositoryImpl.swift */; };
|
||||
C2B90EF0281EB3F8004B8AEC /* MainRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2B90EEF281EB3F8004B8AEC /* MainRepository.swift */; };
|
||||
C2B90EF1281EC622004B8AEC /* NetworkStack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C241D445278B9E0E00D7C604 /* NetworkStack.framework */; platformFilter = ios; };
|
||||
@ -2501,7 +2503,7 @@
|
||||
C2A4C8362A11141D00AFD67C /* TokensNextTokenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokensNextTokenView.swift; sourceTree = "<group>"; };
|
||||
C2A4C8382A1153A200AFD67C /* TokensTokenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokensTokenView.swift; sourceTree = "<group>"; };
|
||||
C2A4C83A2A11736200AFD67C /* TokensCircleProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokensCircleProgress.swift; sourceTree = "<group>"; };
|
||||
C2A4D3142BC097F80001587C /* MainRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainRepositoryImpl.swift; sourceTree = "<group>"; };
|
||||
C2A4D3142BC097F80001587C /* MainRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainRepository.swift; sourceTree = "<group>"; };
|
||||
C2A4D3372BC0B3D60001587C /* CloudState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudState.swift; sourceTree = "<group>"; };
|
||||
C2A4D3482BC0B6B00001587C /* Base32Watch.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Base32Watch.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C2A4D34D2BC0B9620001587C /* Base32Watch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Base32Watch.h; sourceTree = "<group>"; };
|
||||
@ -2621,6 +2623,8 @@
|
||||
C2B5BF582946616900E5092D /* TokensNavigationFlowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokensNavigationFlowController.swift; sourceTree = "<group>"; };
|
||||
C2B6D78D260BB024005B599E /* ColorPickerButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerButton.swift; sourceTree = "<group>"; };
|
||||
C2B71D1C23DDEEEE007C5896 /* MainContainerPaging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainContainerPaging.swift; sourceTree = "<group>"; };
|
||||
C2B86D212BC3058A00AAAC63 /* UserDefaultsRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsRepository.swift; sourceTree = "<group>"; };
|
||||
C2B86D232BC3070F00AAAC63 /* AppPIN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPIN.swift; sourceTree = "<group>"; };
|
||||
C2B90EED281EB3CF004B8AEC /* MainRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainRepositoryImpl.swift; sourceTree = "<group>"; };
|
||||
C2B90EEF281EB3F8004B8AEC /* MainRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainRepository.swift; sourceTree = "<group>"; };
|
||||
C2B90EFD281EDE80004B8AEC /* NotificationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPresenter.swift; sourceTree = "<group>"; };
|
||||
@ -5625,7 +5629,9 @@
|
||||
C274C9D52BAB8ABC008E7212 /* Preview Content */,
|
||||
C2A1B3B32BB6071C00D6B923 /* PINKeyboard.swift */,
|
||||
C2A1B3B72BB608C900D6B923 /* PINKeyboardLayout.swift */,
|
||||
C2A4D3142BC097F80001587C /* MainRepositoryImpl.swift */,
|
||||
C2A4D3142BC097F80001587C /* MainRepository.swift */,
|
||||
C2B86D212BC3058A00AAAC63 /* UserDefaultsRepository.swift */,
|
||||
C2B86D232BC3070F00AAAC63 /* AppPIN.swift */,
|
||||
);
|
||||
path = "TwoFASWatch Watch App";
|
||||
sourceTree = "<group>";
|
||||
@ -10009,7 +10015,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C2A4D3302BC0B1B80001587C /* Crypto.swift in Sources */,
|
||||
C2A4D3152BC097F80001587C /* MainRepositoryImpl.swift in Sources */,
|
||||
C2A4D3152BC097F80001587C /* MainRepository.swift in Sources */,
|
||||
C2A4D32E2BC0B1B20001587C /* Token.swift in Sources */,
|
||||
C2D2AFFA2BB8C93C00B91435 /* TokensListPresenter.swift in Sources */,
|
||||
C2A1B3B82BB608C900D6B923 /* PINKeyboardLayout.swift in Sources */,
|
||||
@ -10020,8 +10026,10 @@
|
||||
C2A4D32C2BC0B0C30001587C /* ServiceIcon.swift in Sources */,
|
||||
C2A4D3312BC0B2540001587C /* TokenGenerator.swift in Sources */,
|
||||
C2A4D32D2BC0B1AD0001587C /* TokenHandler.swift in Sources */,
|
||||
C2B86D222BC3058A00AAAC63 /* UserDefaultsRepository.swift in Sources */,
|
||||
C2A4D3322BC0B2B30001587C /* String+.swift in Sources */,
|
||||
C2D2AFF82BB8C8EE00B91435 /* TokensList.swift in Sources */,
|
||||
C2B86D242BC3070F00AAAC63 /* AppPIN.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
26
TwoFAS/TwoFASWatch Watch App/AppPIN.swift
Normal file
26
TwoFAS/TwoFASWatch Watch App/AppPIN.swift
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// This file is part of the 2FAS iOS app (https://github.com/twofas/2fas-ios)
|
||||
// Copyright © 2024 Two Factor Authentication Service, Inc.
|
||||
// Contributed by Zbigniew Cisiński. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CommonWatch
|
||||
|
||||
struct AppPIN: Codable {
|
||||
let type: PINType
|
||||
let value: String
|
||||
}
|
@ -28,18 +28,60 @@ import ContentWatch
|
||||
// register for background push!
|
||||
//optional func didReceiveRemoteNotification(_ userInfo: [AnyHashable : Any]) async -> WKBackgroundFetchResult
|
||||
// save Storage on going to background!
|
||||
protocol MainRepository: AnyObject {
|
||||
func saveStorage()
|
||||
func service(for secret: String) -> ServiceData?
|
||||
func listAllServicesWithingCategories(
|
||||
for phrase: String?,
|
||||
sorting: SortType,
|
||||
ids: [ServiceTypeID]
|
||||
) -> [CategoryData]
|
||||
func countServices() -> Int
|
||||
var hasServices: Bool { get }
|
||||
func token(
|
||||
secret: Secret,
|
||||
time: Date?,
|
||||
digits: Digits,
|
||||
period: Period,
|
||||
algorithm: CommonWatch.Algorithm,
|
||||
counter: Int,
|
||||
tokenType: TokenType
|
||||
) -> TokenValue
|
||||
|
||||
func markIntroductionAsShown()
|
||||
func wasIntroductionShown() -> Bool
|
||||
var sortType: SortType? { get }
|
||||
func setSortType(_ sortType: SortType)
|
||||
var pin: AppPIN? { get }
|
||||
func setPIN(_ pin: AppPIN)
|
||||
|
||||
func registerForCloudStateChanges(_ listener: @escaping CloudStateListener, id: CloudStateListenerID)
|
||||
func unregisterForCloudStageChanges(with id: CloudStateListenerID)
|
||||
func enableCloudBackup()
|
||||
func synchronizeBackup()
|
||||
func syncDidReceiveRemoteNotification(
|
||||
userInfo: [AnyHashable: Any],
|
||||
fetchCompletionHandler completionHandler: @escaping (BackgroundFetchResult) -> Void
|
||||
)
|
||||
|
||||
final class MainRepositoryImpl {
|
||||
func serviceDefinition(using serviceTypeID: ServiceTypeID) -> ServiceDefinition?
|
||||
func iconTypeID(for serviceTypeID: ServiceTypeID?) -> UIImage
|
||||
|
||||
func listFavoriteServices() -> [ServiceData]
|
||||
func addFavoriteService(_ secret: Secret)
|
||||
func removeFavoriteService(_ secret: Secret)
|
||||
}
|
||||
|
||||
final class MainRepositoryImpl: MainRepository {
|
||||
var storageError: ((String) -> Void)?
|
||||
|
||||
|
||||
let service: ServiceHandler
|
||||
let protection: Protection
|
||||
let storage: Storage
|
||||
let categoryHandler: CategoryHandler
|
||||
let sectionHandler: SectionHandler
|
||||
let cloudHandler: CloudHandlerType
|
||||
// let userDefaultsRepository: UserDefaultsRepository
|
||||
let userDefaultsRepository: UserDefaultsRepository
|
||||
let iconDatabase: IconDescriptionDatabase
|
||||
let serviceDefinitionDatabase: ServiceDefinitionDatabase
|
||||
let iconDescriptionDatabase: IconDescriptionDatabase
|
||||
@ -57,8 +99,8 @@ final class MainRepositoryImpl {
|
||||
return _shared
|
||||
}
|
||||
|
||||
var _isLockScreenActive = false
|
||||
|
||||
private var favoriteServicesCache: [Secret]?
|
||||
|
||||
//
|
||||
// let handler = SyncInstanceWatch.getCloudHandler()
|
||||
// handler.registerForStateChange({ state in
|
||||
@ -75,23 +117,23 @@ final class MainRepositoryImpl {
|
||||
// handler.enable()
|
||||
// handler.synchronize()
|
||||
//}
|
||||
|
||||
|
||||
static func create() {
|
||||
let protection = Protection()
|
||||
|
||||
EncryptionHolder.initialize(with: protection.localKeyEncryption)
|
||||
|
||||
let storage = Storage(readOnly: false) { Log($0, module: .storage) }
|
||||
|
||||
|
||||
let serviceMigration = ServiceMigrationController(storageRepository: storage.storageRepository)
|
||||
serviceMigration.serviceNameTranslation = "Service"//T.Commons.service
|
||||
serviceMigration.serviceNameTranslation = "Service"// TODO: Add translation T.Commons.service
|
||||
|
||||
SyncInstanceWatch.initialize(commonSectionHandler: storage.section, commonServiceHandler: storage.service) {
|
||||
Log("Sync: \($0)")
|
||||
}
|
||||
SyncInstanceWatch.migrateStoreIfNeeded()
|
||||
serviceMigration.migrateIfNeeded()
|
||||
|
||||
|
||||
_ = MainRepositoryImpl(
|
||||
protection: protection,
|
||||
storage: storage,
|
||||
@ -100,7 +142,7 @@ final class MainRepositoryImpl {
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
private init(
|
||||
protection: Protection,
|
||||
storage: Storage,
|
||||
cloudHandler: CloudHandlerType,
|
||||
@ -118,9 +160,11 @@ final class MainRepositoryImpl {
|
||||
serviceDefinitionDatabase = ServiceDefinitionDatabaseImpl()
|
||||
iconDescriptionDatabase = IconDescriptionDatabaseImpl()
|
||||
|
||||
userDefaultsRepository = UserDefaultsRepositoryImpl()
|
||||
|
||||
storageRepository = storage.storageRepository
|
||||
MainRepositoryImpl._shared = self
|
||||
|
||||
|
||||
storage.addUserPresentableError { [weak self] error in
|
||||
self?.storageError?(error)
|
||||
}
|
||||
@ -169,15 +213,35 @@ extension MainRepositoryImpl {
|
||||
tokenType: tokenType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension MainRepositoryImpl {
|
||||
func markIntroductionAsShown() {
|
||||
userDefaultsRepository.markIntroductionAsShown()
|
||||
}
|
||||
|
||||
// func setIntroductionAsShown() {
|
||||
// userDefaultsRepository.setIntroductionAsShown()
|
||||
// }
|
||||
//
|
||||
// func introductionWasShown() -> Bool {
|
||||
// userDefaultsRepository.introductionWasShown()
|
||||
// }
|
||||
//
|
||||
func wasIntroductionShown() -> Bool {
|
||||
userDefaultsRepository.wasIntroductionShown
|
||||
}
|
||||
|
||||
var sortType: SortType? {
|
||||
userDefaultsRepository.sortType
|
||||
}
|
||||
|
||||
func setSortType(_ sortType: SortType) {
|
||||
userDefaultsRepository.setSortType(sortType)
|
||||
}
|
||||
|
||||
var pin: AppPIN? {
|
||||
userDefaultsRepository.pin
|
||||
}
|
||||
|
||||
func setPIN(_ pin: AppPIN) {
|
||||
userDefaultsRepository.setPIN(pin)
|
||||
}
|
||||
}
|
||||
|
||||
extension MainRepositoryImpl {
|
||||
func registerForCloudStateChanges(_ listener: @escaping CloudStateListener, id: CloudStateListenerID) {
|
||||
cloudHandler.registerForStateChange({ listener($0.toCloudState) }, with: id)
|
||||
cloudHandler.checkState()
|
||||
@ -201,7 +265,9 @@ extension MainRepositoryImpl {
|
||||
) {
|
||||
SyncInstanceWatch.didReceiveRemoteNotification(userInfo: userInfo, fetchCompletionHandler: completionHandler)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension MainRepositoryImpl {
|
||||
func serviceDefinition(using serviceTypeID: ServiceTypeID) -> ServiceDefinition? {
|
||||
serviceDefinitionDatabase.service(using: serviceTypeID)
|
||||
}
|
||||
@ -212,12 +278,41 @@ extension MainRepositoryImpl {
|
||||
}
|
||||
return ServiceIcon.for(iconTypeID: serviceDef.iconTypeID)
|
||||
}
|
||||
|
||||
// var sortType: SortType? {
|
||||
// userDefaultsRepository.sortType
|
||||
// }
|
||||
//
|
||||
// func setSortType(_ sortType: SortType) {
|
||||
// userDefaultsRepository.setSortType(sortType)
|
||||
// }
|
||||
}
|
||||
|
||||
extension MainRepositoryImpl {
|
||||
func listFavoriteServices() -> [ServiceData] {
|
||||
initializeFavoriteServicesCache()
|
||||
guard let favoriteServicesCache else { return [] }
|
||||
return favoriteServicesCache.compactMap({ storageRepository.findService(for: $0) })
|
||||
}
|
||||
|
||||
func addFavoriteService(_ secret: Secret) {
|
||||
initializeFavoriteServicesCache()
|
||||
guard favoriteServicesCache?.first(where: { $0 == secret }) == nil else { return }
|
||||
favoriteServicesCache?.append(secret)
|
||||
saveFavoriteServicesCache()
|
||||
}
|
||||
|
||||
func removeFavoriteService(_ secret: Secret) {
|
||||
initializeFavoriteServicesCache()
|
||||
favoriteServicesCache?.removeAll(where: { $0 == secret })
|
||||
saveFavoriteServicesCache()
|
||||
}
|
||||
|
||||
private func initializeFavoriteServicesCache() {
|
||||
if favoriteServicesCache != nil {
|
||||
return
|
||||
}
|
||||
let value = userDefaultsRepository.favoriteServices() ?? []
|
||||
favoriteServicesCache = Array(Set(value))
|
||||
}
|
||||
|
||||
private func saveFavoriteServicesCache() {
|
||||
guard let favoriteServicesCache else {
|
||||
Log("Can't get Favorite Services for saving")
|
||||
return
|
||||
}
|
||||
userDefaultsRepository.setFavoriteServices(favoriteServicesCache)
|
||||
}
|
||||
}
|
108
TwoFAS/TwoFASWatch Watch App/UserDefaultsRepository.swift
Normal file
108
TwoFAS/TwoFASWatch Watch App/UserDefaultsRepository.swift
Normal file
@ -0,0 +1,108 @@
|
||||
//
|
||||
// This file is part of the 2FAS iOS app (https://github.com/twofas/2fas-ios)
|
||||
// Copyright © 2024 Two Factor Authentication Service, Inc.
|
||||
// Contributed by Zbigniew Cisiński. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CommonWatch
|
||||
import ProtectionWatch
|
||||
|
||||
protocol UserDefaultsRepository: AnyObject {
|
||||
var pin: AppPIN? { get }
|
||||
func setPIN(_ pin: AppPIN)
|
||||
|
||||
var sortType: SortType? { get }
|
||||
func setSortType(_ sortType: SortType)
|
||||
|
||||
var wasIntroductionShown: Bool { get }
|
||||
func markIntroductionAsShown()
|
||||
|
||||
func setFavoriteServices(_ services: [Secret])
|
||||
func favoriteServices() -> [Secret]?
|
||||
}
|
||||
|
||||
final class UserDefaultsRepositoryImpl: UserDefaultsRepository {
|
||||
private enum Keys: String, CaseIterable {
|
||||
case sortType
|
||||
case introductionShown
|
||||
case pin
|
||||
case favoriteServices
|
||||
}
|
||||
private let userDefaults = UserDefaults()
|
||||
|
||||
private let localEncryption = LocalKeyEncryption()
|
||||
|
||||
private let encoder = JSONEncoder()
|
||||
private let decoder = JSONDecoder()
|
||||
|
||||
var pin: AppPIN? {
|
||||
guard
|
||||
let data = userDefaults.object(forKey: Keys.pin.rawValue) as? Data,
|
||||
let object = try? decoder.decode(AppPIN.self, from: data)
|
||||
else { return nil }
|
||||
let decrypted = AppPIN(type: object.type, value: localEncryption.decrypt(object.value))
|
||||
return decrypted
|
||||
}
|
||||
|
||||
func setPIN(_ pin: AppPIN) {
|
||||
do {
|
||||
let encrypted = AppPIN(type: pin.type, value: localEncryption.encrypt(pin.value))
|
||||
let encodedNode = try encoder.encode(encrypted)
|
||||
userDefaults.set(encodedNode, forKey: Keys.pin.rawValue)
|
||||
userDefaults.synchronize()
|
||||
} catch {
|
||||
Log("Can't save App PIN! Error: \(error)", severity: .error)
|
||||
}
|
||||
}
|
||||
|
||||
var sortType: SortType? {
|
||||
guard let value = userDefaults.string(forKey: Keys.sortType.rawValue) else { return nil }
|
||||
return SortType(rawValue: value)
|
||||
}
|
||||
|
||||
func setSortType(_ sortType: SortType) {
|
||||
userDefaults.set(sortType.rawValue, forKey: Keys.sortType.rawValue)
|
||||
userDefaults.synchronize()
|
||||
}
|
||||
|
||||
var wasIntroductionShown: Bool {
|
||||
userDefaults.bool(forKey: Keys.introductionShown.rawValue)
|
||||
}
|
||||
|
||||
func markIntroductionAsShown() {
|
||||
userDefaults.set(true, forKey: Keys.introductionShown.rawValue)
|
||||
userDefaults.synchronize()
|
||||
}
|
||||
|
||||
func setFavoriteServices(_ services: [Secret]) {
|
||||
do {
|
||||
let encodedNode = try encoder.encode(services)
|
||||
userDefaults.set(encodedNode, forKey: Keys.favoriteServices.rawValue)
|
||||
userDefaults.synchronize()
|
||||
} catch {
|
||||
Log("Can't save Favorite Services! Error: \(error)", severity: .error)
|
||||
}
|
||||
}
|
||||
|
||||
func favoriteServices() -> [Secret]? {
|
||||
guard
|
||||
let data = userDefaults.object(forKey: Keys.favoriteServices.rawValue) as? Data,
|
||||
let list = try? decoder.decode([Secret].self, from: data)
|
||||
else { return nil }
|
||||
return list
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user