I'm trying to get my grips on the Combine framework from Apple and I'm unsure if I'm doing it right. I have the below error in my code. Can anybody please suggest how to get around this? Many thanks
Error: Escaping closure captures mutating 'self' parameter
import Combine
import Foundation
// Model
protocol Fetchable {
associatedtype T: Decodable
var foo: [T] { get set }
}
extension Fetchable {
internal mutating func fetch(
from url: URL
) {
let _: AnyCancellable? = URLSession.shared
.dataTaskPublisher(for: url)
.map({ $0.data })
.decode(type: Self.T.self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Received completion: ", completion)
break
case .failure(let error):
print("Received completion: ", completion, error)
}
}, receiveValue: { value in // Escaping closure captures mutating 'self' parameter
print(".sink() received \(value)")
self.foo.insert(value, at: 0) // Captured here
})
}
}
struct MyDecodableType: Codable {
...
}
// ViewModel
class MyClass: ObservableObject, Fetchable {
@Published internal var foo: [MyDecodableType] = []
}
jabb
(Ivo)
2
As you already identified, self is captured inside sink. Take in the account, that sink is not executed synchronously! It means, that when your function fetch will finished, you are not able to de-initialize instance of some class which implements Fetchable protocol. This is easy to solve, by using [weak self].
Be worry! The trouble just begin, even thought the code will compile and run. Now, when fetch finish (almost immediately after calling) nothing happens. Why? There is no AnyCancelable anymore, because it lives only while the fetch is executed ...
At least, you have to hold the reference of your AnyCancelable somewhere. There is no chance to declare stored property with the protocol. I suggest you to return it from fetch function, so you could hold (store) it while needed. This gives you another advantage, you will be able to cancel it.
2 Likes
sveinhal
(Svein Halvor Halvorsen)
3
I think Fetchable needs to be a reference type for async mutation to work. Try constraining your protocol (or your protocol extension) to AnyObject.
1 Like
Thank you both for your help, the code works with [weak self] and constraining Fetchable to class.