mfaani
mfaani

Reputation: 36317

How to write protocol oriented sections

This is just a mockup of my real example, my rows are complex objects

My tableview has different section types.

enum Type{
    case devices
    case users
    case status
}

Obviously each section has some rows, may have a headerTitle and has a sectionType, I've tried to generalize that as much as I can. Not sure if using associatedType is the right way...probably there's a much simpler solution for just using protocols

protocol SectionType{
    associatedtype Section
    associatedtype Rows

    init(sectionType: Section, rows: Rows)

    var sectionType: Section {get set}
    var rows: Rows  {get set}
    var headerTitle: String? {get set}
}

The main problem is that is the rows of each Section can be entirely different (more than the difference between customObject1 and customObject2) One solution is to just do var rows: Any and then cast back, but that's not really a good idea.

class CustomObject1{
    var number: Int

}

class CustomObject2{
    var name : String?

}

My conformance to the protocol:

class SomeSection: SectionType{

    var sectionType: Type
    var rows: [CustomObject1]
    var headerTitle: String?

    required init(sectionType: Type, rows: [CustomObject1]){
        self.sectionType = sectionType
        self.rows = rows
    }
}

As you can see my SomeSection class is useless, it's only workable for CustomObject1

var dataSource : [SectionType] = []

let firstSection = SomeSection(sectionType: .devices, rows: [CustomObject1(), CustomObject1(),CustomObject1()])
let secondSection = SomeSection(.users, rows: [???????]) // I can't add `CustomObject2` instances...nor I think creating a new class is a good idea


dataSource.append(firstSection)
dataSource.append(secondSection)
tableview.datasource = dataSource

How can I resolve this?


EDIT

I think my protocol approach was totally unnecessary. However now I have a different problem. My viewController was previously like:

class ViewController: UIViewController{
var dataSource : [Section] = []

}

now I have to change it to:

class ViewController<Row>: UIViewController{
var dataSource : [Section<Row>] = []

}

right?

The problem right now with the approach that Rich has suggested is that I can't apppend these two instances of the Generic class into a single array, because their generic properties types are eventually not the same.

Upvotes: 3

Views: 108

Answers (1)

Rich Tolley
Rich Tolley

Reputation: 3842

I think you can simplify the solution by using a generic class for this use case, but if you want to use protocols and associated types, the following should work:

protocol SectionType {
  associatedtype Section
  associatedtype Row

  init(sectionType: Section, rows: [Row]) 
  var sectionType: Section {get set}
  var rows: [Row]  {get set}
  var headerTitle: String? {get set}
}

class SomeSection<T,U>: SectionType{
  typealias Section = T
  typealias Row = U

  var sectionType: Section
  var rows: [Row]
  var headerTitle: String?

  required init(sectionType: Section, rows: [Row]){
    self.sectionType = sectionType
    self.rows = rows
  }
}

enum FoodType {
  case cheese
  case beer
}

enum Currency {
  case dollars
  case pounds
}

let s1 = SomeSection(sectionType: FoodType.cheese, rows: ["cheddar", "stilton"])
let s2 = SomeSection(sectionType: FoodType.beer, rows: ["rochefort12", "Spring Sprinter"])
let s3 = SomeSection(sectionType: Currency.dollars, rows: [1,2])

The generic version would just be:

class SomeSection<Section,Row> {
  var sectionType: Section
  var rows: [Row]
  var headerTitle: String?

  required init(sectionType: Section, rows: [Row]){
    self.sectionType = sectionType
    self.rows = rows
  }
}

which might be better if the class isn't required to implement any other functionality

Upvotes: 4

Related Questions