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
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.