Created
February 26, 2021 09:53
-
-
Save below/9ff99bde03fb9209d540c6ee0b96d3d3 to your computer and use it in GitHub Desktop.
Controlling your Future: DispatchSemaphore and Combine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*: | |
### Controlling your Future | |
For my little Combine Project, I need to control acccess to a critical | |
section. (For those following along it won't be hard to guess: I don't want the accessToken mechanism to be accessed from two places). | |
So, building on an existing [sample](https://medium.com/swiftcairo/avoiding-race-conditions-in-swift-9ccef0ec0b26), I built | |
something in combine. | |
As always, your feedback is appreciated! | |
*/ | |
import Combine | |
import PlaygroundSupport | |
import Foundation | |
PlaygroundPage.current.needsIndefiniteExecution = true | |
var balance = 1200 | |
enum ATMError: Error { | |
case insufficientFunds | |
case timedOut | |
} | |
extension ATMError: LocalizedError { | |
public var errorDescription: String? { | |
switch self { | |
case .insufficientFunds: | |
return "There are insufficent funds in your account" | |
case .timedOut: | |
return "Access to the ATM timed out" | |
} | |
} | |
} | |
var semaphore = DispatchSemaphore(value: 1) | |
func withdrawFuture(tag: String, value: Int) -> Future<Int, Error> { | |
return Future { | |
promise in | |
print("\(tag): Waiting for access") | |
DispatchQueue.global().async { | |
let timeout = DispatchTime.now() + .seconds(3) | |
switch semaphore | |
.wait(timeout: timeout) | |
{ | |
case .success: | |
print("\(tag): checking if balance containing sufficent money") | |
if balance > value { | |
print("\(tag): Balance is sufficent, please wait while processing withdrawal") | |
// sleeping for some random time, simulating a long process | |
Thread.sleep(forTimeInterval: Double.random(in: 0...2)) | |
balance -= value | |
print("\(tag): Done: \(value) has been withdrawed") | |
print("\(tag): current balance is \(balance)") | |
promise(.success(balance)) | |
} else { | |
print("\(tag): Can't withdraw: insufficent balance") | |
promise(.failure(ATMError.insufficientFunds)) | |
} | |
case .timedOut: | |
promise(.failure(ATMError.timedOut)) | |
} | |
semaphore.signal() | |
} | |
} | |
} | |
let atm1 = withdrawFuture(tag: "FutureATM 1", value: 1000) | |
atm1 | |
.sink { | |
switch $0 { | |
case .finished: | |
print ("ATM1 successfully finished") | |
case .failure(let error): | |
print ("ATM1 failed: \(error.localizedDescription)") | |
} | |
} receiveValue: { (balance) in | |
print ("ATM 1: balance is now \(balance)") | |
} | |
let atm2 = withdrawFuture(tag: "FutureATM 2", value: 800) | |
atm2 | |
.sink { | |
switch $0 { | |
case .finished: | |
print ("ATM2 successfully finished") | |
case .failure(let error): | |
print ("ATM2 failed: \(error.localizedDescription)") | |
} | |
} receiveValue: { (balance) in | |
print ("ATM 2: balance is now \(balance)") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment