I'm trying to wrap my head around Combine.
Here's a method I want to translate into Combine, so that it would return AnyPublisher.
func getToken(completion: @escaping (Result<String, Error>) -> Void) {
dispatchQueue.async {
do {
if let localEncryptedToken = try self.readTokenFromKeychain() {
let decryptedToken = try self.tokenCryptoHelper.decrypt(encryptedToken: localEncryptedToken)
DispatchQueue.main.async {
completion(.success(decryptedToken))
}
} else {
self.fetchToken(completion: completion)
}
} catch {
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}
The whole thing executes on a separate dispatch queue because reading from Keychain and decryption can be slow.
My first attempt to embrace Combine
func getToken() -> AnyPublisher<String, Error> {
do {
if let localEncryptedToken = try readTokenFromKeychain() {
let decryptedToken = try tokenCryptoHelper.decrypt(encryptedToken: localEncryptedToken)
return Result.success(decryptedToken).publisher.eraseToAnyPublisher()
} else {
return fetchToken() // also rewritten to return AnyPublisher<String, Error>
}
} catch {
return Result.failure(error).publisher.eraseToAnyPublisher()
}
}
But how would I move reading from Keychain and decryption onto separate queue? It probably should look something like
func getToken() -> AnyPublisher<String, Error> {
return Future<String, Error> { promise in
self.dispatchQueue.async {
do {
if let localEncryptedToken = try self.readTokenFromKeychain() {
let decryptedToken = try self.tokenCryptoHelper.decrypt(encryptedToken: localEncryptedToken)
promise(.success(decryptedToken))
} else {
// should I fetchToken().sink here?
}
} catch {
promise(.failure(error))
}
}
}.eraseToAnyPublisher()
}
How would I return a publisher from my private method call? (see comment in code)
Are there any prettier solutions?