LeadKit/docs/tikeychainutils/singlevaluestorage.md

4.6 KiB
Raw Permalink Blame History

SingleValueStorage - протокол для доступа к значению которое может храниться в Keychain, UserDefaults или ещё где-то.

Позволяет:

  • инкапсулировать внутри себя логику получения, записи и удаления значения
  • добавлять дополнительную логику для получения или изменения значений через композицию или наследование
  • ограничить доступ к данным в UserDefaults или Keychain в разных частях приложения

StringValueKeychainStorage

Класс для работы со строковым значением нахоящимся в keychain (самый частый кейс)

import TIKeychainUtils
import TIFoundationUtils
import KeychainAccess

extension StorageKey {
    static var apiToken: StorageKey<String> {
        .init(rawValue: "apiToken")
    }

    static var deleteApiToken: StorageKey<Bool> {
        .init(rawValue: "deleteApiToken")
    }
}

let keychain = Keychain()

let apiTokenKeychainStorage = StringValueKeychainStorage(keychain: keychain, storageKey: .apiToken)

if apiTokenKeychainStorage.hasStoredValue() {
    // open auth user flow, perform requests
} else {
    // show login screen
    // ...

    // login

//    switch await userService.login() {
//    case .success:
//        // open auth user flow, perform requests
//    case .failure:
//        // show login screen
//    }
}

AppInstallLifetimeSingleValueStorage<SingleValueStorage>

Класс позволяющий добавить дополнительную функциональность очистки значения по конкретному ключу в keychain после переустановки приложения

import Foundation

let defaults = UserDefaults.standard // or AppGroup defaults

let appReinstallChecker = DefaultAppFirstRunCheckStorage(defaults: defaults,
                                                         storageKey: .deleteApiToken)

let appInstallAwareTokenStorage = apiTokenKeychainStorage.appInstallLifetimeStorage(appFirstRunCheckStorage: appReinstallChecker)

if appInstallAwareTokenStorage.hasStoredValue() {
    // app wasn't reinstalled, token is exist
} else {
    // app was reinstalled or token is empty
    // ...
}

SingleValueExpirationStorage<SingleValueStorage>

Класс позволяющий добавить дополнительную функциональность очистки значения по конкретному ключу если истёк срок действия объекта (например refresh token'а)

struct Token: Codable, Equatable {
    var value: String
    var expiration: Date

    init(value: String, expiration: Date) {
        self.value = value
        self.expiration = expiration
    }
}

extension StorageKey {
    static var accessToken: StorageKey<Token> {
        .init(rawValue: "accessToken")
    }
}

let accessTokenStorage = DefaultSingleValueCodableStorage(storage: keychain,
                                                          storageKey: .accessToken,
                                                          decoder: JSONKeyValueDecoder(),
                                                          encoder: JSONKeyValueEncoder())

let expirationCheckStorage = accessTokenStorage.isExpireCheck { $0.expiration.timeIntervalSinceNow > 0 }

switch expirationCheckStorage.getValue() {
case let .success(token):
    // use token
    break
case let .failure(storageError):
    if case .valueNotFound = storageError {
        // token is missing or expired, request new token
    } else {
        // handle storage error
    }
    break
}

MigratingStorage

При необходимости мигрировать с одного keychain на другой можно воспользоваться классом MigratingSingleValueStorage. При создании "мигрирующего" хранилища необходимо будет указать:

  • source storage: хранилище с которого мигрируем
  • target storage: хранилище на которое мигрируем
let groupKeychain = Keychain(service: "app.group.identifier")
let targetApiTokenKeychainStorage = StringValueKeychainStorage(keychain: groupKeychain, storageKey: .apiToken)

let migratingApiTokenKeychainStorage = apiTokenKeychainStorage.migrating(to: targetApiTokenKeychainStorage)

let token = migratingApiTokenKeychainStorage.getValue()