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
The short version
The type owning your call to FirebaseRef.observeSingleEvent(of:with:)
is most likely a value type (a struct
?), in which case a mutating context may not explicitly capture self
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 follows
func observeSingleEvent(of eventType: FIRDataEventType, with block: @escaping (FIRDataSnapshot) -> Void)
The block
closure is marked with the @escaping
parameter attribute, which means it may escape the body of its function, and even the lifetime of self
(in your context). Using this knowledge, we construct a more minimal example which we may analyze:
struct Foo {
private func bar(with block: @escaping () -> ()) { block() }
mutating func bax() {
bar { print(self) } // this closure may outlive 'self'
/* error: closure cannot implicitly capture a
mutating self parameter */
}
}
Now, the error message becomes more telling, and we turn to the following evolution proposal was implemented in Swift 3:
Stating [emphasis mine]:
Capturing an
inout
parameter, includingself
in a mutating method, becomes an error in an escapable closure literal, unless the capture is made explicit (and thereby immutable).
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 to observeSingleEvent(...)
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. a class
, rather than a struct
:
class Foo {
init() {}
private func bar(with block: @escaping () -> ()) { block() }
func bax() {
bar { print(self) }
}
}
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 capture self
weakly, e.g.
FirebaseRef.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in ...