Before Anyone makes it duplicate I have scene all the RxDatasource tags on SO and other sites also. But anyone didn't worked for me.
So my question is totally related to this one that I followed for my case also. But also I have no idea what's going on in here. And its been two weeks of struggling. I have checked gitHub code sample also but was unable to understand.
I have created an app using RxSwift
and Realm
on MVVM architecture pattern, things are working fine but now I need to create two sections in my view using UICollectioView
for that I read about RxdataSource
and tried to apply it but i didn't get at all whats it actually doing. I tried creating other projects for learning also those also didn't worked. Still I tried and make this code but it gives me error.
What I did is from the link provided above is in the code below. I also do not know how to give my data source the data or lists from one array after spliting it. below is my whole code.
I don't have idea what is this block doing.
//Changed
struct SectionViewModel {
var header: String!
var items: [StudentModel]
}
extension SectionViewModel: SectionModelType {
typealias Item = StudentModel
init(original: SectionViewModel, items: [StudentModel]) {
self = original
self.items = items
}
}
then my CollectionView class is like
class StudentCV: UIViewController, UICollectionViewDelegateFlowLayout {
//MARK: - Outlets
@IBOutlet weak var studentsView: UICollectionView!
let studentCells = BehaviorRelay<[StudentModel]>(value: [])
var notificationToken: NotificationToken? = nil
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let flowLayout = UICollectionViewFlowLayout()
let size = CGSize(width: 105, height: 135)
flowLayout.itemSize = size
studentsView.setCollectionViewLayout(flowLayout, animated: true)
studentsView.rx.setDelegate(self).disposed(by: disposeBag)
setupBinding()
}
func studentLeft(value: Int, id: Int) {
SignalRService.sharedClass.chatHub.invoke(method: "StudentLeft", withArgs: [id, value]){ (result, error) in
if let e = error {
print("Error: \(e)")
} else {
print("Done!")
let vale = Database.singleton.updatePickupStatus(studentId: id, pickupValue: value)
TestDebug.debugInfo(fileName: "", message: "STUDENT LEFTTT:: \(vale)")
if let r = result {
print("Result: \(r)")
}
}
}
}
deinit {
notificationToken?.invalidate()
}
func setupBinding() {
studentsView.register(UINib(nibName: "StudentCVCell", bundle: nil), forCellWithReuseIdentifier: "studentCV")
//Cell creation Changed here..............................
dataSource.configureCell = { (ds, cv, ip, item) in
let cell = cv.dequeueReusableCell(withReuseIdentifier: "studentCV", for: ip) as! StudentCVCell
cell.viewModel = item
return cell
}
studentCells
.asObservable()
.debug("STudent View: ")
.map({ SectionViewModel(header: "Pickups Arrived", items: $0 ) })
.bind(to: studentsView.rx.items(dataSource: dataSource)) // now here it is giving me this error (Instance method 'items(dataSource:)' requires the types '[SectionViewModel]' and 'SectionViewModel' be equivalent)
.disposed(by: disposeBag)
// item selection with model details.
Observable
.zip(
studentsView
.rx
.itemSelected,
studentsView
.rx
.modelSelected(StudentModel.self))
.bind { [weak self] indexPath, model in
let cell = self?.studentsView.cellForItem(at: indexPath) as? StudentCVCell
if (model.pickupStatus == 2) {
// updating view accordingly
}
}.disposed(by: disposeBag)
}
and ViewModels looks like this. from where I am populating.
class StudentCollectionViewViewModel {
//MARK: Outlets
let disposeBag = DisposeBag()
var notificationToken : NotificationToken? = nil
let studentCells = BehaviorRelay<[StudentModel]>(value: [])
var studentCell : Observable<[StudentModel]> {
return studentCells.asObservable()
}
deinit {
notificationToken?.invalidate()
}
func getStudentsData(id: Int) {
let studentsData = Database.singleton.fetchStudents(byCLassId: id)
self.notificationToken = studentsData.observe{[weak self] change in
TestDebug.debugInfo(fileName: "", message: "Switch:::: change")
switch change {
case .initial(let initial):
TestDebug.debugInfo(fileName: "", message: "INIT: \(initial)")
self!.studentCells.accept(Array(studentsData))
case .update(_, let deletions, let insertions, let modifications):
TestDebug.debugInfo(fileName: "", message: "MODIF::: \(modifications)")
self!.studentCells.accept(Array(studentsData))
case .error(let error):
print(error)
}
}
}
}
I am populating data from DB but I need to make two lists , I also don't get where i have to send two lists of data to populate. plus when I tried to use it in my code to see how things works but it gives my following error. Instance method 'items(dataSource:)' requires the types '[SectionModel]' and '[StudentModel]' be equivalent. any advise or help will be appreciated. thanks in advance
RxCollectionViewSectionedReloadDataSource<SectionModel>
expects that you will bind items of SectionModel
type, because you passed SectionModel
as a generic parameter. Apparently, you would like to use StudentModel
. To achieve this, you might make your StudentModel
conform to SectionModelType
protocol, and then use RxCollectionViewSectionedReloadDataSource<StudentModel>
:
extension StudentModel: SectionModelType {
// implement
}
let dataSource = RxCollectionViewSectionedReloadDataSource<StudentModel>(configureCell: { (datasource, collectionView, indexPath, element) in
// configure a cell
})
studentCells.bind(to: studentsView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag) // don't forget to setup disposal
But I suppose that StudentModel
is describing a single cell, rather than an entire section. Is such case, it may be a better idea to map StudentModel
to SectionModel
, like this:
let dataSource = RxCollectionViewSectionedReloadDataSource<SectionModel>(configureCell: { (datasource, collectionView, indexPath, element) in
// configure a cell
})
studentCells
.map { [SectionModel(model: "", items: $0)] }
.bind(to: studentsView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
Obviously, I mapped all your studentCells
into a single section, which might not be your case. In more complex scenarios, you might consider implementing a custom type conforming to SectionModelType
.
Also, you may pass something more valuable than an empty string as a model
, but again it depends on your needs.
Attension!
In an example above SectionModel
stands for RxDataSources.SectionModel
, not:
enum SectionModel {
case SectionOne(items: [SectionItem])
case SectionTwo(items: [SectionItem])
}