2

I have an error when I try to assign a value to the function parameter while inside a completion block, I get an error that reads 'escaping closures can only capture inout parameters explicitly by value' .

How could I fix this? Any tip is much appreciated!

func fetchCurrentUser(user: inout User? ) {
    self.fetchUser(withId: AuthProvider.sharedInstance.currentUserId(), completionHandler: {
        fetchedUser in
        guard let newUser = fetchedUser else { return }
        user = newUser // error Here
    })
}
  • 1
    You may also find [this answer](https://stackoverflow.com/a/39572470/709202) helpful. – Chris Frederick Jun 02 '17 at 01:37
  • [This solution](https://stackoverflow.com/a/51619714/2880627) show how to modify a pass-by-reference variable in an escaping closure. – ukim Jul 31 '18 at 18:19

2 Answers2

2

This will not work, because you use a completion handler. The self.fetchUser will (almost) immediately return and the completion handler will be executed whenever the background work (most likely a network request) is finished.

Your function fetchCurrentUser calls self.fetchUser and than returns so it will return before the completion block is even executed.

You cannot use inout parameter on escaping closures (this is what the error message also tells you). A escaping closure is a closure which will be executed after the function which you pass it in returns.

You can either rewrite your function to also use a completion hander or change you function to wait for the completion handler to run before ending the fetchCurrentUser function. But for the second approach please be aware that this also will block the caller of the function from executing anything else.

Nef10
  • 926
  • 2
  • 16
  • 26
  • 1
    Thank you for your insight. I figured the time at which fetchedUser is accessible, the inout param (user) would then get assigned that value. –  Jun 02 '17 at 01:25
1

I suggest this refactor:

func fetchCurrentUser(callback: @escaping (User) -> ()) {
    self.fetchUser(withId: AuthProvider.sharedInstance.currentUserId(), completionHandler: {
        fetchedUser in
        guard let newUser = fetchedUser else { return }
        callback(newUser)
    })
}

or if you want fetchCurrentUser to be synchronous you could use semaphores

Anton Belousov
  • 1,140
  • 15
  • 34
  • 1
    I'd suggest making the closure parameter optional, e.g. `func fetchCurrentUser(callback: @escaping (User?) -> Void) { ... }`, and upon failure, call closure with `nil`. You really want the caller to know if there was an error. Re the synchronous suggestion, that almost always a bad idea, typically code smell for deep rooted design flaws. – Rob Jun 02 '17 at 06:19