I am using Firebase to observe event and then setting an image inside completion handler
FirebaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let _ = snapshot.value as? NSNull {
self.img = UIImage(named:"Some-image")!
} else {
self.img = UIImage(named: "some-other-image")!
}
})
However I am getting this error
Closure cannot implicitly capture a mutating self parameter
I am not sure what this error is about and searching for solutions hasn't helped
You can try this! I hope to help you.
Another solution is to explicitly capture self (since in my case, I was in a mutating function of a protocol extension so I couldn't easily specify that this was a reference type).
So instead of this:
I have this:
Which seems to have silenced the warning.
Note this does not work for value types so if self is a value type you need to be using a reference type wrapper in order for this solution to work.
If someone is stumbling upon this page (from search) and you are defining a
protocol
/protocol extension
, then it might help if you declare yourprotocol
as class bound. Like this:Sync Solution
If you need to mutate a value type (
struct
) in a closure, that may only work synchronously, but not for async calls, if you write it like this:You cannot otherwise capture a "mutating self" with value types except by providing a mutable (hence
var
) copy.Why not Async?
The reason this does not work in async contexts is: you can still mutate
result
without compiler error, but you cannot assign the mutated result back toself
. Still, there'll be no error, butself
will never change because the method (peel()
) exits before the closure is even dispatched.To circumvent this, you may try to change your code to change the async call to synchronous execution by waiting for it to finish. While technically possible, this probably defeats the purpose of the async API you're interacting with, and you'd be better off changing your approach.
Changing
struct
toclass
is a technically sound option, but doesn't address the real problem. In our example, now being aclass Banana
, its property can be changed asynchronously who-knows-when. That will cause trouble because it's hard to understand. You're better off writing an API handler outside the model itself and upon finished execution fetch and change the model object. Without more context, it is hard to give a fitting example. (I assume this is model code becauseself.img
is mutated in the OP's code.)Adding "async anti-corruption" objects may help
I'm thinking about something among the lines of this:
BananaNetworkRequestHandler
executes requests asynchronously and then reports the resultingBananaPeelingResult
back to aBananaStore
BananaStore
then takes the appropriateBanana
from its inside by looking forpeelingResult.bananaID
banana.bananaID == peelingResult.bananaID
, it then setsbanana.isPeeled = peelingResult.isPeeled
,You see, from the quest to find a simple fix it can become quite involved easily, especially if the necessary changes include changing the architecture of the app.
The short version
The type owning your call to
FirebaseRef.observeSingleEvent(of:with:)
is most likely a value type (astruct
?), in which case a mutating context may not explicitly captureself
in an@escaping
closure.The simple solution is to update your owning type to a reference once (
class
).The longer version
The
observeSingleEvent(of:with:)
method of Firebase is declared as followsThe
block
closure is marked with the@escaping
parameter attribute, which means it may escape the body of its function, and even the lifetime ofself
(in your context). Using this knowledge, we construct a more minimal example which we may analyze:Now, the error message becomes more telling, and we turn to the following evolution proposal was implemented in Swift 3:
@noescape
contextsStating [emphasis mine]:
Now, this is a key point. For a value type (e.g.
struct
), which I believe is also the case for the type that owns the call toobserveSingleEvent(...)
in your example, such an explicit capture is not possible, afaik (since we are working with a value type, and not a reference one).The simplest solution to this issue would be making the type owning the
observeSingleEvent(...)
a reference type, e.g. aclass
, rather than astruct
:Just beware that this will capture
self
by a strong reference; depending on your context (I haven't used Firebase myself, so I wouldn't know), you might want to explicitly captureself
weakly, e.g.