Cannot convert value of type 'T?' to expected argument type 'T?'

I have this protocol:

protocol PostDataProviderProtocol{
    
    var delegate:DataProviderDelegate? { get set }
    
    func fetchData<T>(url:String, onload: @escaping ([T?], CustomErrorStruct?)  -> Void)
    
    func load(onload:@escaping(Bool, CustomErrorStruct?,PostDataProviderProtocol?) -> Void)
    
    func load(onload:@escaping(Bool, CustomErrorStruct?,PostDataProviderProtocol?) -> Void, force:Bool)

    func reload(onload:@escaping(Bool, CustomErrorStruct?,PostDataProviderProtocol?) -> Void)
    
    func hasNext() ->Bool
    
    func getItemAt<T>(index:Int)-> T?
    
    func getOffset() -> Int
    
    func setEndpoint(endpoint: String)
    
    func getEndpoint() -> String
    
    func setItemsPerPage(itemsPerPage: Int)
    
    func getItemsPerPage() -> Int
    
    func setPage(page: Int)
    
    func getPage() -> Int
    
    func getLength() -> Int
    
    func composeEndpointUrl()-> String
    
    func composeEndpointUrl(offset:Int?, limit:Int?)-> String

    func getItems<T>() -> [T]
    
    func replaceItemAt<T>(index:Int, item:T?)
    
    func removeItemAt(index:Int)
}

I have a class implementing this protocol with an extension, like this:

class BaseDataProvider<T> {
  internal var _itemsPerPage:Int
  internal var _page:Int
  internal var _endpoint:String
  internal var cachedData:[T?]
}    

extension BaseDataProvider: PostDataProviderProtocol{

    func getItems<T>() -> [T] {
      return self.cachedData as! [T]
    }

    func replaceItemAt<T>(index: Int, item: T?){
      self.cachedData.append(item)
    }
 }

As you can see, the cacheData type is an generic array. The problem is in the line

 self.cachedData.append(item)

The compiler throws this error:

Cannot convert value of type 'T?' to expected argument type 'T?'

enter image description here

Completed code without base protocol for T:

import Foundation

protocol DataProviderProtocol{

   func getItemAt<T>(index:Int)-> T?

   func getItems<T>() -> [T?]

   func replaceItemAt<T>(index:Int, item:T?)

   func removeItemAt(index:Int)
}



class BaseDataSource<T> {
  internal var cachedData:[T?] = []
}

extension BaseDataSource: DataProviderProtocol{
  func getItemAt<T>(index: Int) -> T? {
     return self.cachedData[index]
  }

  func getItems<T>() -> [T?] {
     return self.cachedData
  }

  func replaceItemAt<T>(index: Int, item: T?) {
    return self.cachedData[index] = item
  }

  func removeItemAt(index: Int) {
    self.cachedData.remove(at: index)
  }

}

With base protocol for T Type

import Foundation

protocol DataProviderProtocol{

  func getItemAt<T>(index:Int)-> T?

  func getItems<T>() -> [T?]

  func replaceItemAt<T>(index:Int, item:T?)

  func removeItemAt(index:Int)
}

class BaseTypeForT{

}

class BaseDataSource<T:BaseTypeForT>{
  internal var cachedData:[T?] = []
}



extension BaseDataSource: DataProviderProtocol{
  func getItemAt<T>(index: Int) -> T? {
    return self.cachedData[index]
  }

  func getItems<T>() -> [T?] {
    return self.cachedData
  }

  func replaceItemAt<T>(index: Int, item: T?) {
    return self.cachedData[index] = item
  }

  func removeItemAt(index: Int) {
     self.cachedData.remove(at: index)
  }

}

How can I solve this?

Upvotes: 0

Views: 2924

Answers (1)

Joakim Danielson
Joakim Danielson

Reputation: 51882

You need to use an associatedtype in your protocol so all functions/variables in the protocol uses the same type because otherwise each function with a <T> will be using a different type

Example (simplified

protocol PostDataProviderProtocol{
    associatedtype T

    func getItemAt(index:Int)-> T?
    func replaceItemAt(index:Int, item:T?)
}

Then in your class you can use it as

class BaseDataProvider<SomeType> {
    internal var _itemsPerPage:Int = 0
    internal var _page:Int = 0
    internal var _endpoint:String = ""
    internal var cachedData:[SomeType?] = []
}

extension BaseDataProvider: PostDataProviderProtocol {
    typealias T = SomeType

    func hasNext() -> Bool {
        true
    }

    func getItemAt(index: Int) -> T? {
        self.cachedData[index]
    }

    func getItems() -> [T] {
      return self.cachedData as! [T]
    }

 }

Upvotes: 3

Related Questions