Need help in using generics with protocol

protocol ServiceManagerProtocol {

    func downloadData<T: Decodable>(fromUrl: URL,
                                    withParameters: [String: Any]?,
                                    outputDataFormat: String,
                                    type: T.Type) -> AnyPublisher<T, AFError>

    }

class ServiceManager: ServiceManagerProtocol {

    func downloadData<T: Decodable>(fromUrl: URL,
                                    withParameters: [String: Any]?,
                                    outputDataFormat: String,
                                    type: T.Type) -> AnyPublisher<T, AFError> {
        print("Network call.")
    }

}

final class MockServiceManager<T: Decodable>: ServiceManagerProtocol {

    var downloadedData: AnyPublisher<T, AFError>?

    func downloadData<T: Decodable>(fromUrl: URL,
                                    withParameters: [String: Any]?,
                                    outputDataFormat: String,
                                    type: T.Type) -> AnyPublisher<T, AFError> {
        if let data = downloadedData {
            return data    // Error: Cannot convert return expression of type 'Combine.AnyPublisher<T, Alamofire.AFError>' to return type 'Combine.AnyPublisher<T, Alamofire.AFError>'
        } else {
            fatalError("Data must not be nil.")
        }
    }
}

class SomeClass: NSObject {

    let serviceManager: ServiceManagerProtocol

    init(serviceManager: ServiceManagerProtocol = ServiceManager()) {
        self.serviceManager = serviceManager
    }
}

Need help in fixing above compilation error. Thanks!

You have two distinct generic parameters named T, labeled (1) and (2) below:

Those are two different types and you can't convert between them.

1 Like

The error message here could definitely be improved, though! Worth filing a bug for on GitHub.

1 Like

Agreed. I also think the compiler should warn when you shadow a type parameter. Every single time I've seen a programmer do this this, it's been by mistake and has caused poor diagnostics and general confusion.

EDIT: Emit a warning when type parameters are shadowed. · Issue #62767 · apple/swift · GitHub

3 Likes

Sometimes in life, you're a developer before Codable comes into existence. And then, you've got

subscript(key: CKRecord.FieldKey) -> __CKRecordObjCValue?

to work with before a Swifty solution is provided to replace it.

You don't want to perform dynamic type checking, and cast accordingly. But you also don't want to deal with someone else imposing their opinion on your naming choices.

I'm sure there are plenty of valid use cases that people have come up with. You all are better at this than I am but nobody has the superset of all of our creativity.

public extension Optional {
  /// [An alternative to overloading `??` to throw errors upon `nil`.](
  /// https://forums.swift.org/t/unwrap-or-throw-make-the-safe-choice-easier/14453/7)
  /// - Note: Useful for emulating `break`, with `map`, `forEach`, etc.
  /// - Throws: `UnwrapError` when `nil`.
  var unwrapped: Wrapped {
    get throws {
      switch self {
      case let wrapped?:
        return wrapped
      case nil:
        throw UnwrapError.nil
      }
    }
  }

  /// [An alternative to overloading `??` to throw errors upon `nil`.](
  /// https://forums.swift.org/t/unwrap-or-throw-make-the-safe-choice-easier/14453/7)
  /// - Note: Useful for emulating `break`, with `map`, `forEach`, etc.
  /// - Throws: `UnwrapError`
  func unwrap<Wrapped>() throws -> Wrapped {
    guard case let wrapped as Wrapped = try unwrapped
    else { throw UnwrapError.typeMismatch }

    return wrapped
  }
}
/// Acts as a dictionary that `throw`s instead of returning optionals.
public protocol DictionaryLike<Key, Value> {
  associatedtype Key
  associatedtype Value

  subscript(key: Key) -> Value? { get }
}

public extension DictionaryLike {
  /// `self[key].unwrapped()`
  subscript<Value>(key: Key) -> Value {
    get throws { try self[key].unwrap() }
  }

  /// Allows lookup by enumeration cases backed by `Key`s,
  /// instead of having to manually use their raw values.
  subscript<Value>(key: some RawRepresentable<Key>) -> Value {
    get throws { try self[key.rawValue] }
  }
}

Yeah, I understood the case and tried to fix it with below code but ended up with few more errors.

protocol ServiceManagerProtocol {

    associatedtype T

    func requestForDataDownload(fromUrl: URL,
                                withParameters: [String: Any]?,
                                outputDataFormat: String,
                                type: T.Type) -> AnyPublisher<T, AFError>

}

class ServiceManager<T: Decodable>: ServiceManagerProtocol {

    func requestForDataDownload(fromUrl: URL,
                                withParameters: [String: Any]?,
                                outputDataFormat: String,
                                type: T.Type) -> AnyPublisher<T, AFError> {

    }

}

final class MockServiceManager<T: Decodable>: ServiceManagerProtocol {

    var downloadedData: AnyPublisher<T, AFError>?

    func requestForDataDownload(fromUrl: URL,
                                withParameters: [String: Any]?,
                                outputDataFormat: String,
                                type: T.Type) -> AnyPublisher<T, AFError> {
        if let data = downloadedData {
            return data
        } else {
            fatalError("Data must not be nil.")
        }
    }
}

class SomeClass: NSObject {

    let serviceManager: ServiceManagerProtocol    //    Error: Use of protocol 'ServiceManagerProtocol' as a type must be written 'any ServiceManagerProtocol'

    init(serviceManager: ServiceManagerProtocol = ServiceManager()) {    //    Error 1: Generic parameter 'T' could not be inferred
//    Error 2: Use of protocol 'ServiceManagerProtocol' as a type must be written 'any ServiceManagerProtocol'
        self.serviceManager = serviceManager
    }
}



You must make the changes that the error messages tell you to do. What is your question about them?

Did it. But when I try to fix the second error it throws me another one and it goes on.

class SomeClass: NSObject {
    
    let serviceManager: any ServiceManagerProtocol
    
    init(serviceManager: any ServiceManagerProtocol = ServiceManager<T: Decodable>()) {    //    Error 1: Cannot find 'T' in scope
//    Error 2: Expected ',' separator
//    Error3: Expected parameter name followed by ':' 
        self.serviceManager = serviceManager
    }
}

The compiler isn't asking to write the letter T here: you need to say what T actually is—

init(serviceManager: any ServiceManagerProtocol = ServiceManager<???>())

Okay, got it now. Thanks!

But still, I do have one more doubt. What if I need two different types from ServiceManager from the same class where I am initialising it.

Like in this case ServiceManager is a class where I make the network calls and it can return objects of any type. And, the class where I initialise it can make network calls to different APIs which in turn will return data of different types.