I'm trying some patterns and at some point I realised my passthrough subject doesn't send anything when self is weak. Here what i did :
import Foundation
import Combine
class ViewModel {
var publisher = PassthroughSubject<[WebData], Never>()
let apis : [ApiProtocol]
init(apis: [ApiProtocol]){
self.apis = apis
getData()
}
func getData(){
guard !apis.isEmpty else { return }
apis.forEach {api in
api.getApiData { [weak self] webData in
self?.publisher.send(webData)
}
}
}
}
Since I declare [weak self] and write
self?.publisher.send(webData)
I don't receive any data from my sink closure in the view controller. But if I delete [weak self] and call use send method with a strong self :
func getData(){
guard !apis.isEmpty else { return }
apis.forEach {api in
api.getApiData { webData in
self.publisher.send(webData)
}
}
}
It sends data and view controller receives it perfectly. But now, didn't it cause a retain cycle? Why it doesn't send the data when self declared as weak? What am I missing here?
Edit : Here how I use sink closure:
class DataVC: UIViewController {
weak var viewModel : ViewModel?
var models = [WebData]()
var cancellable = [AnyCancellable]()
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
downloadData()
tableView.delegate = self
tableView.dataSource = self
}
func downloadData() {
guard let model = viewModel else { return }
model.publisher
.receive(on: DispatchQueue.main)
.sink { _ in
} receiveValue: { [weak self] value in
self?.models.append(contentsOf: value)
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}.store(in: &cancellable)
}
}
DataVC table view protovol confirmances are in the extension. They work just fine, so I didn't add them here.
Edit2: Now I change declaration of viewModel in view controller from :
weak var viewModel : ViewModel?
to not weak one :
var viewModel : ViewModel!
... and it works with weak referenced publisher at the beginning. Ok, this solved my problem. But, then, why when viewModel is weak, even I (guard-let) unwrap it before use, it doesn't work?
It's because weak is weak!
That means "Don't retain viewModel". Nothing else retains your ViewModel object either, so it just vanishes instantly before you can use it. By the time you say
viewModelis gone and has become nil, and we just return.