Search code examples
swiftgenericssendable

Cannot convert value of type '[DataModel<T>]' to expected argument type '[DataModel<T>]'


I have a data model where product can change based on the experimentation (AB Testing), so I have a struct with generics

struct DataModel<T: Sendable & Equatable> {
    var product: T?
    // there are other properties,
}

I have data provider and a protocol

protocol SomeProtocol: AnyObject {
    func fetchedData<T: Sendable & Equatable>(data: [DataModel<T>])
}

class DataProvider {
    weak var delegate: SomeProtocol?

    func getData() {
        //this is a async flow for simplicity am adding synchronous code here
        var someObjects = [DataModel<SKProduct>]()
        delegate?.fetchedData(data: someObjects)
    }
}

Finally a UIViewController

class SomeViewController<T: Sendable & Equatable>: UIViewController {
    func renderUI(data: [DataModel<T>]) {
        debugPrint(data)
    }
}

extension SomeViewController: SomeProtocol {
    func fetchedData<T>(data: [DataModel<T>]) {
        renderUI(data: data)
    }
}

But I am getting the following error at line renderUI(data: data)

Cannot convert value of type '[SomeRandomProject.DataModel<T>]' to expected argument type '[SomeRandomProject.DataModel<T>]'

EDIT 1:

Following @Sulthan's answer below I modified my code to

protocol SomeProtocol: AnyObject {
    associatedtype DataType: Sendable & Equatable
    func fetchedData(data: [DataModel<DataType>])
}

class DataProvider {
    weak var delegate: (any SomeProtocol)?

    func getData() {
        var someObjects = [DataModel<SKProduct>]()
        delegate?.fetchedData(data: someObjects)
    }
}

class SomeViewController<T: Sendable & Equatable>: UIViewController, SomeProtocol {
    typealias DataType = T

    func fetchedData(data: [DataModel<T>]) {
        renderUI(data: data)
    }

    func renderUI(data: [DataModel<T>]) {
        debugPrint(data)
    }
}

Though this solves the issue at line renderUI(data: data) I get new error now at delegate?.fetchedData(data: someObjects)

Cannot convert value of type '[DataModel<SKProduct>]' to expected argument type '[DataModel<(any SomeProtocol).DataType>]'
Member 'fetchedData' cannot be used on value of type 'any SomeProtocol'; consider using a generic constraint instead

Solution

  • The problem is that the generic method in SomeProtocol can accept any type T but the renderUI can accept only the type that SomeViewController has been initialized with.

    For example, if you define SomeViewController<String> then SomeProtocol says you should be able to call fetchedData<Int> which would then have to pass an Int to SomeViewController<String>.

    There is no simple way to fix it, the architecture must be changed depending on what you really need.

    Maybe:

    protocol SomeProtocol: AnyObject {
        associatedtype DataModelType: Sendable & Equatable
    
        func fetchedData(data: [DataModel<DataModelType>])
    }
    

    is what you want for the view controller. However, that wouldn't work with the data provider without changes.