Binding a sectioned table with RxDataSources using a TableViewSectionedDataSource, requires sections which conform to SectionModelType.
This SectionModelType protocol has the following initializer as one of it's requirements:
init(original: Self, items: [Item])
In addition, the same protocol enforces var items: [Item] { get }
. We can now initialize the items array backing variable (in above mentioned init
) with either from original.items
, or items
passed as init parameter. This is very confusing. The SectionModelType code has no comments.
How section of README which explains creation of sections for this very case, talks about creating typealias (for associated value), and the items array, but not a word about following implementation of init
with original: Self
:
init(original: SectionOfCustomData, items: [Item]) {
self = original
self.items = items
}
While this can work in a struct, doing the same in a class shouts:
Cannot assign to value: 'self' is immutable
Can anyone here explain what is happening here and why do we NEED to init with Self
Lastly, is there another (cleaner) way to reactively (in RXSwift / RxCocoa only) bind a sectioned table view to an observable datasource. e.g. * my cells, and sections have their own data model, which need to be mutable (hence classes) * there are multiple screens with this requirement for different entities, so i would be interesetd in achieving this with protocols instead, and slap the corresponding data model with the protocol ans have a common implementation for RXBinding
Any pointers to either get clarity more on existing implementation, or achieving above points would be really helpful. P.S.: I am already working in a huge code base which uses Rx, so not using Rx, or moving to SwiftUI etc is not what I am looking for.
init(original:items:)
is a copy initializer. It takes an already existing instance of the struct and makes a new one that is identical except that it changes what's in the items
property.
So in a protocol declaration, init(original: Self, items: [Item])
means: "You hand me an already existing instance of whatever this type is that is conforming to me, the protocol, and we'll make a new instance that copies it while changing its items
."
And that is exactly what the example does. It's easier to see if you put it all together, like this:
protocol SectionModelType {
associatedtype Item
var items: [Item] { get }
init(original: Self, items: [Item])
}
struct CustomData {
var anInt: Int
var aString: String
var aCGPoint: CGPoint
}
struct SectionOfCustomData {
var header: String
var items: [Item]
}
extension SectionOfCustomData: SectionModelType {
typealias Item = CustomData
init(original: SectionOfCustomData, items: [Item]) {
self = original
self.items = items
}
}
It's up to you, in your implementation of the extension, to do what they did: set self
to original
, which is some already existing SectionOfCustomData object (that is permitted in a struct initializer) and then change its items
. And you must do things like that, and in that order, because if you don't, you'd be trying to initialize a SectionOfCustomData object without setting its header
property — whereas, this way, we are guaranteed that there is already a header
property value, because we are starting with an already existing SectionOfCustomData object, and you can't make one without setting its header
property.
As for your other worry, that that won't compile if this is a class, not a struct: yes, you're perfectly right, but don't do that. It isn't what they said to do. They said make this a struct, and they meant it.