Files
purchases-ios/Sources/Networking/Operations/NetworkOperation.swift
T
Joshua Liebowitz 80905f94c9 Extract Backend dependencies into a BackendConfiguration object (#1696)
Extract out various dependencies that are required to instantiate the `Backend` object.

Refactor to assist with the next few PRs that extract out other pieces
See [CSDK-82]
Resolves [CSDK-155]
2022-06-14 15:12:46 -07:00

160 lines
4.0 KiB
Swift

//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// NetworkOperation.swift
//
// Created by Joshua Liebowitz on 11/18/21.
import Foundation
class CacheableNetworkOperation: NetworkOperation, CacheKeyProviding {
var cacheKey: String { "\(type(of: self)) \(individualizedCacheKeyPart)" }
let individualizedCacheKeyPart: String
/**
- Parameter individualizedCacheKeyPart: The part of the cacheKey that makes it unique from other operations of the
same type. Example: If you posted receipts two times in a row you'd have 2 operations. The cache key would be
PostOperation + individualizedCacheKeyPart where individualizedCacheKeyPart is whatever you determine to be unique.
*/
init(configuration: NetworkConfiguration, individualizedCacheKeyPart: String) {
self.individualizedCacheKeyPart = individualizedCacheKeyPart
super.init(configuration: configuration)
}
}
class NetworkOperation: Operation {
let httpClient: HTTPClient
private var _isExecuting: Atomic<Bool> = .init(false)
private(set) override final var isExecuting: Bool {
get {
return self._isExecuting.value
}
set {
self.willChangeValue(for: \.isExecuting)
self._isExecuting.value = newValue
self.didChangeValue(for: \.isExecuting)
}
}
private var _isFinished: Atomic<Bool> = .init(false)
private(set) override final var isFinished: Bool {
get {
return self._isFinished.value
}
set {
self.willChangeValue(for: \.isFinished)
self._isFinished.value = newValue
self.didChangeValue(for: \.isFinished)
}
}
private var _isCancelled: Atomic<Bool> = .init(false)
private(set) override final var isCancelled: Bool {
get {
return self._isCancelled.value
}
set {
self.willChangeValue(for: \.isCancelled)
self._isCancelled.value = newValue
self.didChangeValue(for: \.isCancelled)
}
}
init(configuration: NetworkConfiguration) {
self.httpClient = configuration.httpClient
super.init()
}
override final func main() {
if self.isCancelled {
self.isFinished = true
return
}
self.isExecuting = true
self.log("Started")
self.begin {
self.finish()
}
}
override final func cancel() {
self.isCancelled = true
self.isExecuting = false
self.isFinished = true
self.log("Cancelled")
}
/// Overriden by subclasses to define the body of the operation
/// - Important: this method may be called from any thread so it must be thread-safe.
/// - Parameter completion: must be called when the operation has finished.
func begin(completion: @escaping () -> Void) {
fatalError("Subclasses must override this method")
}
private final func finish() {
assert(!self.isFinished, "Operation \(type(of: self)) (\(self)) was already finished")
self.log("Finished")
self.isExecuting = false
self.isFinished = true
}
// MARK: -
final override var isAsynchronous: Bool {
return true
}
// MARK: -
private func log(_ message: String) {
Logger.debug("\(type(of: self)): \(message)")
}
// MARK: -
struct Configuration: NetworkConfiguration {
let httpClient: HTTPClient
}
struct UserSpecificConfiguration: AppUserConfiguration, NetworkConfiguration {
let httpClient: HTTPClient
let appUserID: String
}
}
protocol AppUserConfiguration {
var appUserID: String { get }
}
protocol NetworkConfiguration {
var httpClient: HTTPClient { get }
}