docs: update playground pages for AsyncOperation and SingleValueExpirationStorage

This commit is contained in:
Ivan Smolin 2023-06-09 11:45:55 +03:00
parent bf613b99e8
commit 1be28959bc
6 changed files with 229 additions and 7 deletions

View File

@ -22,8 +22,10 @@
import Foundation
open class SingleValueCodableDefaultsStorage<ValueType: Codable>: BaseSingleValueStorage<ValueType, UserDefaults> {
public init(defaults: UserDefaults,
open class DefaultSingleValueCodableStorage<CodableStorage: CodableKeyValueStorage, ValueType: Codable>:
BaseSingleValueStorage<ValueType, CodableStorage> {
public init(storage: CodableStorage,
storageKey: StorageKey<ValueType>,
decoder: CodableKeyValueDecoder,
encoder: CodableKeyValueEncoder) {
@ -44,7 +46,7 @@ open class SingleValueCodableDefaultsStorage<ValueType: Codable>: BaseSingleValu
$0.removeCodableValue(forKey: $1)
}
super.init(storage: defaults,
super.init(storage: storage,
storageKey: storageKey,
hasValueClosure: hasValueClosure,
deleteValueClosure: deleteValueClosure,

View File

@ -32,11 +32,12 @@ let intResultOperation = ClosureAsyncOperation<Int, Never> { completion in
/*:
## Базовые операторы
На данный момент реализовано всего два оператора:
На данный момент реализовано четыре оператора:
- `map(mapOutput:mapFailure:)` - конвертирует ResultType в новый NewResultType и ErrorType в новый NewErrorType
- `observe(onSuccess:onFailure)` - просто вызывает переданные callback'и при получении результата или ошибки
- `flatMap<NewOutput>(_:)` - подписывается на результат выполнения нового AsyncOperation полученного из closure
- `flatMapError<NewFailure>(_:)` - подписывается на результат выполнения нового AsyncOperation полученного из closure
*/
//: ### Пример запуска асинхронных операци с применением операторов в последовательной очереди и вывод результатов
@ -75,3 +76,53 @@ ClosureAsyncOperation<String, Never> { completion in
"Async operation two has finished with Success"
```
*/
//: ### Пример использования оператора flatMap у AsyncOperaton
import TISwiftUtils
extension StorageKey {
static var loyaltyCardNumber: StorageKey<String> {
.init(rawValue: "loyaltyCardNumber")
}
}
struct CardService {
enum Failure: Error {
case noCardFound
case cardFetchError
}
private let operationQueue = OperationQueue()
private let asyncStorage = StringValueDefaultsStorage(defaults: .standard, storageKey: .loyaltyCardNumber)
.async(on: .global())
func requestCardTitle(cardNumber: String) -> AsyncOperation<String, Failure> {
.just(success: "Supreme card")
// .just(failure: .cardFetchError)
}
func getSavedCardTitle(completion: @escaping UIParameterClosure<Result<String, Failure>>) -> Cancellable {
ClosureAsyncOperation { completion in
asyncStorage.getValue {
completion($0.mapError { _ in
.noCardFound
})
}
}
.flatMap {
requestCardTitle(cardNumber: $0)
}
.observe(onResult: completion)
.add(to: operationQueue)
}
}
let cardService = CardService()
cardService.getSavedCardTitle { result in
debugPrint(result)
}
Nef.Playground.needsIndefiniteExecution(true)

View File

@ -0,0 +1,30 @@
import UIKit
public protocol NefPlaygroundLiveViewable {}
extension UIView: NefPlaygroundLiveViewable {}
extension UIViewController: NefPlaygroundLiveViewable {}
#if NOT_IN_PLAYGROUND
public enum Nef {
public enum Playground {
public static func liveView(_ view: NefPlaygroundLiveViewable) {}
public static func needsIndefiniteExecution(_ state: Bool) {}
}
}
#else
import PlaygroundSupport
public enum Nef {
public enum Playground {
public static func liveView(_ view: NefPlaygroundLiveViewable) {
PlaygroundPage.current.liveView = (view as! PlaygroundLiveViewable)
}
public static func needsIndefiniteExecution(_ state: Bool) {
PlaygroundPage.current.needsIndefiniteExecution = state
}
}
}
#endif

View File

@ -71,3 +71,46 @@ if appInstallAwareTokenStorage.hasStoredValue() {
// 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 .valueNotFound = storageError {
// token is missing or expired, request new token
} else {
// handle storage error
}
break
}

View File

@ -30,11 +30,12 @@ let intResultOperation = ClosureAsyncOperation<Int, Never> { completion in
## Базовые операторы
На данный момент реализовано всего два оператора:
На данный момент реализовано четыре оператора:
- `map(mapOutput:mapFailure:)` - конвертирует ResultType в новый NewResultType и ErrorType в новый NewErrorType
- `observe(onSuccess:onFailure)` - просто вызывает переданные callback'и при получении результата или ошибки
- `flatMap<NewOutput>(_:)` - подписывается на результат выполнения нового AsyncOperation полученного из closure
- `flatMapError<NewFailure>(_:)` - подписывается на результат выполнения нового AsyncOperation полученного из closure
### Пример запуска асинхронных операци с применением операторов в последовательной очереди и вывод результатов
@ -72,3 +73,55 @@ ClosureAsyncOperation<String, Never> { completion in
"Async operation one has finished with 2"
"Async operation two has finished with Success"
```
### Пример использования оператора flatMap у AsyncOperaton
```swift
import TISwiftUtils
extension StorageKey {
static var loyaltyCardNumber: StorageKey<String> {
.init(rawValue: "loyaltyCardNumber")
}
}
struct CardService {
enum Failure: Error {
case noCardFound
case cardFetchError
}
private let operationQueue = OperationQueue()
private let asyncStorage = StringValueDefaultsStorage(defaults: .standard, storageKey: .loyaltyCardNumber)
.async(on: .global())
func requestCardTitle(cardNumber: String) -> AsyncOperation<String, Failure> {
.just(success: "Supreme card")
// .just(failure: .cardFetchError)
}
func getSavedCardTitle(completion: @escaping UIParameterClosure<Result<String, Failure>>) -> Cancellable {
ClosureAsyncOperation { completion in
asyncStorage.getValue {
completion($0.mapError { _ in
.noCardFound
})
}
}
.flatMap {
requestCardTitle(cardNumber: $0)
}
.observe(onResult: completion)
.add(to: operationQueue)
}
}
let cardService = CardService()
cardService.getSavedCardTitle { result in
debugPrint(result)
}
Nef.Playground.needsIndefiniteExecution(true)
```

View File

@ -70,3 +70,46 @@ if appInstallAwareTokenStorage.hasStoredValue() {
// ...
}
```
### `SingleValueExpirationStorage<SingleValueStorage>`
Класс позволяющий добавить дополнительную функциональность очистки значения по конкретному ключу
если истёк срок действия объекта (например refresh token'а)
```swift
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 .valueNotFound = storageError {
// token is missing or expired, request new token
} else {
// handle storage error
}
break
}
```