Closure cannot implicitly capture a mutating self parameter
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:
- SE-0035: Limiting inout capture to
@noescape
contexts
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 ...
SwiftUI Escaping closure captures mutating 'self' parameter
An object's initializer cannot do anything asynchronous. Its job is to produce the object immediately, with all its properties initialized.
Escaping closure captures mutating 'self' parameter: struct
As you have found, the quick solution is to use a reference type, a class. But why is this the case?
Swift structs are value types, so they are immutable. You can mark a function as mutating
to indicate to the compiler that a function mutates the struct, but what does that actually mean?
Consider a simple struct:
struct Counter {
var count
init(_ count: Int = 0)
{
self.count = count
}
mutating func increment() {
self.count+=1
}
}
Now, try and assign an instance of this to a let
constant:
let someCounter = Counter()
someCounter.increment()
print(someCounter.count)
You will get an error; you need to use a var
.
var someCounter = Counter()
someCounter.increment()
print(someCounter.count)
What actually happens when you call a mutating
func is that a new Counter
is created, with the new count
and it is assigned to someCounter
. It is effectively saying someCounter = Counter(someCounter.count+1)
Now, think what would happen if you could mutate self
in an escaping closure - That new Counter
is going to be created at some unspecified time in the future, but execution has already moved on. It is too late to update someCounter
.
The other advantage of using a class
, as you have found, is that you can use ObservableObject
, which makes updating your SwiftUI views much easier.
SwiftUI: What's 'Escaping closure captures mutating 'self' parameter' and how to fix it
EDIT:
Seems like you cannot mutate structs anymore in escaping closure without removing @escaping which not be possible in your case. You might want to consider changing your implementation to a class.
Structs are immutable. Which mean they cannot be mutated. In your case you are modifying the value of self.bool1 = true
which is changing the value of self.
~~A better way (IMO) would be to create a mutating func
to do your firebase call and update the values inside mutating function.~~
Also, you shouldn’t use State property wrappers in models. They should only be used in views.
Closure cannot implicitly capture self parameter. Swift
It's because you're using struct
. Since structs are value, they are copied (with COW-CopyOnWrite) inside the closure for your usage. It's obvious now that copied properties are copied by "let" hence you can not change them. If you want to change local variables with callback you have to use class
. And beware to capture self weakly ([weak self] in
) to avoid retain-cycles.
Related Topics
Why Nsuserdefaults Failed to Save Nsmutabledictionary in Ios
Do Something Every X Minutes in Swift
Ios 6: How to Restrict Some Views to Portrait and Allow Others to Rotate
Xcode 4: Create Ipa File Instead of .Xcarchive
Add an Element to an Array in Swift
Dealing With Different iOS Device Resolutions in Spritekit
Getting a List of Files in a Directory With a Glob
How to Create Local Notifications
Get the Current Scroll Position of a Swiftui Scrollview
Create Uiimage With Solid Color in Swift
Detect If the App Was Launched/Opened from a Push Notification
How to Convert an Nsstring Value to Nsdata
Firebase Notification Records/Log API
Swiftui App Life Cycle Ios14 Where to Put Appdelegate Code