docs: update playground pages for AsyncOperation and SingleValueExpirationStorage
This commit is contained in:
parent
bf613b99e8
commit
1be28959bc
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
```
|
||||
|
|
|
|||
Loading…
Reference in New Issue