How can I correctly use associatedType in my protocol
Asked Answered
C

2

1

I’m trying to come up with an protocol-oriented MVVM for my tableviewcells. I have lots of them.

my viewModel

protocol PlainTableViewCellModelType {
    var backgroundColor : UIColor {get}
    var textColor: UIColor {get}
    var titleFont : UIFont {get }
    var accessoryType : UITableViewCellAccessoryType {get}
    var textLabelNumberOfLines: Int {get}
}

my view

protocol PlainTableViewCellType{
    associatedtype viewModel : PlainTableViewCellModelType
    func setupUI(forViewModel viewModel: viewModel)
}

my class conformance

class PlainTableViewCell : CCTableViewCell, PlainTableViewCellType{

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError()
    }

    func setupUI(forViewModel viewModel: PlainTableViewCellModelType){
        contentView.backgroundColor = viewModel.backgroundColor
        textLabel?.textColor = viewModel.textColor
        textLabel?.font = viewModel.titleFont
        accessoryType = viewModel.accessoryType
        textLabel?.numberOfLines = viewModel.textLabelNumberOfLines
    }
}

The current setups results in the following error:

Type 'PlainTableViewCell' does not conform to protocol 'PlainTableViewCellType'

I can get it to work if I do:

protocol PlainTableViewCellType{
    func setupUI(forViewModel viewModel: PlainTableViewCellModelType)
}

But I want to have an associatedType so I can enforce same model in all my PlainTableViewCellType functions

EDIT: I'm happy to listen to alternatives, but first I want to know why this doesn't work.

Cherianne answered 9/10, 2018 at 21:52 Comment(4)
I don't think you really need to add a dataType to an associatedType. Correct me if Im wrong, I just thought associatedType are just placeholders for your protocol where in the class that conforms to it is where you set the typeAlias for the associated type. You can get it to work by changing from associatedType to direct protocol instance on setupUI since you are explicitly specifying what class/protocol you are expecting.Thenar
Thanks. Did you read the last line of my question? I want to constrain the associatedType and have uniformity of type across multiple functions of my protocolCherianne
then instead of associatedtype viewModel : PlainTableViewCellModelType shouldn't it be associatedtype viewModel = PlainTableViewCellModelType ? directly assigning the value of the associatedType. For the why, I believe that associatedType needs a value and you only did a dataType castThenar
This is the classic problem of protocols not conforming to themselves – the viewModel associated type cannot be satisfied by PlainTableViewCellModelType, as that's not currently a type that conforms to PlainTableViewCellModelType (see also https://mcmap.net/q/835705/-unable-to-use-protocol-as-associatedtype-in-another-protocol-in-swift/2976878 which is specific to associated types).Phalansterian
F
1

In protocol you need remove PlainTableViewCellModelType:

protocol PlainTableViewCellType{
    associatedtype viewModel
    func setupUI(forViewModel viewModel: viewModel)
}

and in PlainTableViewCell you need add something like this:

typealias viewModel = PlainTableViewCellModelType
Frae answered 9/10, 2018 at 22:8 Comment(5)
you need remove PlainTableViewCellModelType I would like constrain it though.Cherianne
then you don't need associatedtype, just write func setupUI(forViewModel viewModel: PlainTableViewCellType)Frae
right. But what if I want I 2 other functions in my protocol and want all of them to have the same parameter typeCherianne
apart from that I still don't get why removing PlainTableViewCellModelType from viewModel: PlainTableViewCellModelType resolves the issue. That's the core of my questionCherianne
and typealias viewModel = PlainTableViewCellModelType in PlainTableViewCell not help?Frae
O
1

You must create PlainTableViewCellModelType implementation and declare typealias in cell implementation. Use it as type for parameter in setupUI(forViewModel:) because compiler cant infer associated type.

protocol PlainTableViewCellModelType {
    var backgroundColor : UIColor { get }
    var textColor: UIColor { get }
    var titleFont : UIFont { get }
    var accessoryType : Int { get}
    var textLabelNumberOfLines: Int { get }
}

protocol PlainTableViewCellType{
    associatedtype viewModel : PlainTableViewCellModelType
    func setupUI(forViewModel viewModel: viewModel)
}

struct PlainTableViewCellModel: PlainTableViewCellModelType {
    var backgroundColor : UIColor
    var textColor: UIColor
    var titleFont : UIFont
    var accessoryType : Int
    var textLabelNumberOfLines: Int
}


class PlainTableViewCell : UITableViewCell, PlainTableViewCellType {
    typealias viewModel = PlainTableViewCellModel

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError()
    }

    func setupUI(forViewModel viewModel: PlainTableViewCellModel) {
        contentView.backgroundColor = viewModel.backgroundColor
        textLabel?.textColor = viewModel.textColor
        textLabel?.font = viewModel.titleFont
        textLabel?.numberOfLines = viewModel.textLabelNumberOfLines
    }
}
Olethea answered 10/10, 2018 at 9:6 Comment(2)
because compiler cant infer associated type. It can. See here. Just not sure why it can't do it here.Cherianne
Because associated type wasn't associated with concrete type implicitly (via declaration in property type) or explicitly (via typealias)Olethea

© 2022 - 2024 — McMap. All rights reserved.