Completion handler never called - swift

I have a function like this.
class func getAccountBalances(completionHandler:#escaping ((_ balances:Any) -> Void)){
//Alamofire request and we get the result. But sometimes the result fails.
switch response.result {
case .success(let value):
completionHandler(value)
case .failure(let error):
print ("error is: \(error)")
}
I am not putting code to handle the result if it fails. Is that a bad thing? Do I need to have a completion handler in the case that the call fails so that this function does not stay in the memory waiting for that completion handler to be called? What is the best practice?

In general, it is a good practice to call the completion on every case. The reason for this is that you usually want to let the upper lever(business logic layer) decide if it should mark some balances(for example) as saved, or maybe show a dialog when an error has occurred. That is a good practice with the thinking that everything should be a module. That being said, if another module will want to call the same function at some point, it may be a good thinking to let that module what happened with the result. That can be implemented in several ways, I won't enter here, it's your decision.
However, it's not a must to do it. If a block won't be called it should be deallocated, and then everything is good memory-wise. So in your example, if you don't retain the block somewhere else(for example holding it in a variable inside the class that makes getAccountBalances call), you should be just fine.
Another important part is when you call the function be careful to not create a memory leak where you retain the self inside the block:
getAccountBalances() { _ in
self.updateUI()
}
This block will create a retain to self and if everything goes okay with the call, but user left the screen, you may end up using variables that were deallocated and crash the app. A good practice here is to not retain the self in the callback, but make it weak before that:
getAccountBalances() { [weak self] _ in
// This is not necessarily needed, and you could use self?.updateUI() instead.
// However, this is usually another thing i like to do and consider it a good practice
guard let `self` = self else { return }
self.updateUI()
}

Your title: Completion handler never called
Your code: does not return any value to the completion handler if there is an error.
How would you expect to see a result of the competion handler if there is a failure? Would you like to crash the app? This way is better because it handles both cases:
class func getAccountBalances(completionHandler:#escaping ((_ balances:Any?) -> Void)){ //make Any an optional
//Alamofire request and we get the result. But sometimes the result fails.
switch response.result {
case .success(let value):
completionHandler(value)
case .failure(let error):
print ("error is: \(error)")
completionHandler(nil)
}
Your new call to this function:
getAccountBalances() { value in
guard let _value = value else { // anticipate on the error return }
// use _value, the balances are inside.
}
An other approach would be not making it nil, but downcasting the value inside of it. That would look like this:
class func getAccountBalances(completionHandler:#escaping ((_ balances:Any) -> Void)){
//Alamofire request and we get the result. But sometimes the result fails.
switch response.result {
case .success(let value):
completionHandler(value)
case .failure(let error):
print ("error is: \(error)")
completionHandler(error) //notice this change
}
Then your function would look like this:
getAccountBalances() { value in
if let error = value as? Error { //or whatever type your error is in the function
//there is an error
}
}

Related

How do I correctly handle & complete this function that takes in an #escaping function correctly?

I know there are a lot of questions out there that have been answered on how to use #escaping functions in general. My issue is a bit more niche as I am working with an API that gives me a function that takes in an #escaping function (or so I think). I need help decoding both (1) the function declaration I am working with and what it means and (2) how I write a function to effectively call it, and complete it and be able to exit.
The API function is declared as so (with some stuff hidden), wrapped in a larger struct, i'll call specialStruct:
public func context(completion: #escaping ((Result<String, SpecialClassError>) -> Void)) {
class.something() { result in
switch result {
case .success(let response):
completion(.success(response.cid))
case.failure(let error):
completion(.failure(.network(error: error), data: nil)))
}
}
}
Currently, I am running this:
specialStruct.context(completion: {result in
switch result {
case .success(let str):
let _ = print(str)
case .failure(let error):
let _ = print(error.localizedDescription)
}
})
This is what happens as I step through my handler, which is a bit confusing to me:
It is wrapped in an init() in a SwiftUI View. It goes through once at the start, but doesn't actually step into context? It seems to start, but doesn't do anything with result.
Code keeps running...eventually comes back to my call at case .success(let str):.
Runs the next line, and this successfully prints the expected value from the API after connecting to it. let _ = print(str)
Goes to end of call line at bottom })
Which brings me back to the context() declaration shown above, at completion(.success(response.cid))
Jumps to the second to last } in the function declaration.
Jumps into the something() call, specifically a line that is completion(.success(decoded))
Continues in something() call, eventually landing back at an Apple Module FPRNSURL...nInstrument and line 307 completionHandler(data, response, error);
Here it stays for good.
Let me know if that made it more confusing that it needs to be! Thanks!
EDIT:
Added a note that in step (2) above, the API has already been connected to and returned the expected value. For some reason it goes back to the completion handler and gets hung up, even though I am already done.
The steps you wrote exactly describes Asynchronous API calls, this is the expected behavior as the api takes time to give you the requested result & Swift won't wait until it does..
Also, in the function context, you don't need all that code, you can simply do:
public func context(completion: #escaping ((Result<String, SpecialClassError>) -> Void)) {
class.something() { result in
completion(result)
}
}

Combine sink: ignore receiveValue, only completion is needed

Consider the following code:
CurrentValueSubject<Void, Error>(())
.eraseToAnyPublisher()
.sink { completion in
switch completion {
case .failure(let error):
print(error)
print("FAILURE")
case .finished:
print("SUCCESS")
}
} receiveValue: { value in
// this should be ignored
}
Just by looking at the CurrentValueSubject initializer, it's clear that the value is not needed / doesn't matter.
I'm using this particular publisher to make an asynchronous network request which can either pass or fail.
Since I'm not interested in the value returned from this publisher (there are none), how can I get rid of the receiveValue closure?
Ideally, the call site code should look like this:
CurrentValueSubject<Void, Error>(())
.eraseToAnyPublisher()
.sink { completion in
switch completion {
case .failure(let error):
print(error)
print("FAILURE")
case .finished:
print("SUCCESS ")
}
}
It also might be the case that I should use something different other than AnyPublisher, so feel free to propose / rewrite the API if it fits the purpose better.
The closest solution I was able to find is ignoreOutput, but it still returns a value.
You could declare another sink with just completion:
extension CurrentValueSubject where Output == Void {
func sink(receiveCompletion: #escaping ((Subscribers.Completion<Failure>) -> Void)) -> AnyCancellable {
sink(receiveCompletion: receiveCompletion, receiveValue: {})
}
}
CurrentValueSubject seems a confusing choice, because that will send an initial value (of Void) when you first subscribe to it.
You could make things less ambiguous by using Future, which will send one-and-only-one value, when it's done.
To get around having to receive values you don't care about, you can flip the situation round and use an output type of Result<Void, Error> and a failure type of Never. When processing your network request, you can then fulfil the promise with .failure(error) or .success(()), and deal with it in sink:
let pub = Future<Result<Void, Error>, Never> {
promise in
// Do something asynchronous
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
promise(.success(.success(())))
//or
//promise(.success(.failure(error)))
}
}.eraseToAnyPublisher()
// somewhere else...
pub.sink {
switch $0 {
case .failure(let error):
print("Whoops \(error)")
case .success:
print("Yay")
}
}
You're swapping ugly code at one end of the chain for ugly code at the other, but if that's hidden away behind AnyPublisher and you're concerned with correct usage, that seems the way to go. Consumers can see exactly what to expect from looking at the output and error types, and don't have to deal with them in separate closures.

Handling exceptions in guard blocks

I'm looking for an elegant way of combining guard blocks with do-try-catch semantics in Swift, and haven't been satisfied with my own efforts so far.
For background, I'm getting hold of some necessary data from a function that throws an exception and can return an Optional value. As a concrete example, let's say I'm looking up the most recent item from a TODO list stored on the filesystem. This can legitimately be nil (nothing on the list), or it can thrown an exception (some sort of I/O error accessing the filesystem), so both cases are distinct.
For the rest of the method though, we only care if there is a item to operate on - so this sounds like an ideal case for guard:
func myCoolMethod() {
guard let todoItem = try? fetchItemFromList() else {
// No item to act on, nothing to do
return
}
// rest of function acts on todoItem
...
}
This is indeed fine - until the guard block starts failing when you expect it to succeed, and you'd really like to (if nothing else) log the error message to try and investigate. As such, the try? needs to be replaced by a try to allow the error to be captured, and now we'll need to wrap the guard in a do block:
func myCoolMethod() {
do {
guard let todoItem = try fetchItemFromList() else {
// No item to act on, nothing to do
return
}
// rest of function acts on todoItem
...
}
catch {
// Now we know there was an error of some sort (can specialize the
// catch block if we need to be more precise)
// log error or similar, then...
return
}
}
There are two problems with this that I see:
The do block needs to wrap the whole method (due to the scope of todoItem). This means that the happy-path logic is indented (which is something that guard specifically helps to avoid); but additionally, it means that any errors from that logic will be caught by the catch block, perhaps accidentally. I'd much prefer the do block to only be scoped around the guard.
There's duplication of the "what to do if we have no item" block. In this trivial example it's just a return, but it still pains me a little to have two separate blocks that need to do broadly the same thing (where one just adds a little extra context with the error being available).
I'd like to get as close to this as possible:
func myCoolMethod() {
// `do guard` is made up but would enter the else block either if an exception
// is thrown, or the pattern match/conditional fails
do guard let todoItem = try fetchItemFromList() else {
if let error = error {
// Optional `error` is only bound if the guard failed due to an exception
// here we can log it etc.
}
// This part is common for both the case where an exception was thrown,
// and the case where the method successfully returned `nil`, so we can
// implement the common logic for handling "nothing to act on"
return
}
// rest of function acts on todoItem
...
}
What are my options here?
First, look for errors and deal with them. Then look for existence, and deal with that:
func myCoolMethod() {
// Making this `let` to be stricter. `var` would remove need for `= nil` later
let todoItem: Item?
// First check for errors
do {
todoItem = try fetchItemFromList()
} catch {
todoItem = nil // Need to set this in either case
// Error handling
}
// And then check for values
guard let todoItem = todoItem else {
// No item to act on (missing or error), nothing to do
return
}
// rest of function acts on todoItem
print(todoItem)
}
Or separate the problems into functions. When things get complicated, make more focused functions:
func myCoolMethod() {
// And then check for values
guard let todoItem = fetchItemAndHandleErrors() else {
// No item to act on (missing or error), nothing to do
return
}
// rest of function acts on todoItem
print(todoItem)
}
func fetchItemAndHandleErrors() -> Item? {
// First check for errors
do {
return try fetchItemFromList()
} catch {
// Error handling
return nil
}
}

Lifetime purpose on ReactiveSwift

I've been using ReactiveSwift for a few months now but there is a something that I dont fully understand: lifetime objects.
For example, lets say I have a SignalProducer which will make an API call, this is wrapped on a class:
class ServiceWrapped {
private let service: Service // the method called on this object returns the SignalProducer
private let (lifetime, token) = Lifetime.make()
// more stuff
func fetchSomething(completion: #escaping (Value?, Error?) -> Void) {
lifetime += service.fetchSomething()
.startWithResult { result in
switch result {
case .success(let value):
completion(value, nil)
case .failure(let error):
completion(nil, error)
}
}
}
}
My question is: Is it necessary to use lifetime on this case?
I understood that lifetime will retain the service call so it has something when it return but since this is also wrapped on ServiceWrapped I don't think using lifetime is really necessary.
Thanks in advance.
You're correct that you don't need to keep the result of startWithResult in order to keep the subscription alive. The relevant part of the documentation says:
A Signal must be publicly retained for attaching new observers, but not necessarily for keeping the stream of events alive. Moreover, a Signal retains itself as long as there is still an active observer.
So as long as you don't dispose the object returned from startWithResult, the operation will continue even if you don't retain it.
Rather, Lifetime is about cancelling operations. In this case, because you've attached the result of startWithResult to ServiceWrapped's lifetime, the operation will be cancelled when the ServiceWrapped object is deallocated. If you omit lifetime +=, then the operation will continue even if ServiceWrapped is deallocated.
A practical example of why this is useful is if you have a view controller that loads an image from the web. If the user dismisses the view controller before the image is finished loading then you probably want to cancel the web request. You can do that by tying the image load producer to the lifetime of the view controller. It's not about keeping the web request alive, it's about cancelling it when it's no longer necessary.
As an aside about style, the documentation recommends you use operators rather than handling the result of the startWithResult:
func fetchSomething(completion: #escaping (Value?, Error?) -> Void) {
service.fetchSomething()
.take(during: lifetime)
.startWithResult { result in
switch result {
case .success(let value):
completion(value, nil)
case .failure(let error):
completion(nil, error)
}
}
}

Is there a way to throw errors from asynchronous closures in Swift 3?

I’m executing some functions in a test asynchronously using a DispatchQueue like this:
let queue: DispatchQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated)
let group: DispatchGroup = DispatchGroup()
func execute(argument: someArg) throws {
group.enter()
queue.async {
do {
// Do stuff here
group.leave()
} catch {
Log.info(“Something went wrong")
}
}
group.wait()
}
Sometimes the code inside the do block can throw errors, that I have to catch later on. Since I’m developing a test, I want it to fail, if the code inside the do block throws an error.
Is there a way to throw an error, without catching it inside the queue.async call?
You cannot throw an error, but you can return an error:
First, you need to make your calling function asynchronous as well:
func execute(argument: someArg, completion: #escaping (Value?, Error?)->()) {
queue.async {
do {
// compute value here:
...
completion(value, nil)
} catch {
completion(nil, error)
}
}
}
The completion handler takes a parameter which we could say is a "Result" containing either the value or an error. Here, what we have is a tuple (Value?, Error?), where Value is the type which is calculated by the task. But instead, you could leverage a more handy Swift Enum for this, e.g. Result<T> or Try<T> (you might want to the search the web).
Then, you use it as follows:
execute(argument: "Some string") { value, error in
guard error == nil else {
// handle error case
}
guard let value = value else {
fatalError("value is nil") // should never happen!
}
// do something with the value
...
}
Some rules that might help:
If a function calls an asynchronous function internally, it inevitable becomes an asynchronous function as well. *)
An asynchronous function should have a completion handler (otherwise, it's some sort of "fire and forget").
The completion handler must be called, no matter what.
The completion handler must be called asynchronously (with respect the the caller)
The completion handler should be called on a private execution context (aka dispatch queue) unless the function has a parameter specifying where to execute the completion handler. Never use the main thread or main dispatch queue - unless you explicitly state that fact in the docs or you intentionally want to risk dead-locks.
*) You can force it to make it synchronous using semaphores which block the calling thread. But this is inefficient and really rarely needed.
Well, you might conclude, that this looks somewhat cumbersome. Fortunately, there's help - you might look for Future or Promise which can nicely wrap this and make the code more concise and more comprehensible.
Note: In Unit Test, you would use expectations to handle asynchronous calls (see XCTest framework).
Refactor your code to use queue.sync and then throw your error from there. (Since your execute function is actually synchronous, given the group.wait() call at the last line, it shouldn't really matter.)
For instance, use this method from DispatchQueue:
func sync<T>(execute work: () throws -> T) rethrows -> T
By the way, a good idiom for leaving a DispatchGroup is:
defer { group.leave() }
as the first line of your sync/async block, which guarantees you won't accidentally deadlock when an error happens.