Search code examples
iosswiftgenericsprotocolsimplementation

Implement generic protocol which already has a default implementation?


I have the following code and it works:

import Foundation
import RealmSwift

protocol GetAllDBServiceProtocol {
    associatedtype T: Object
    func getall() -> [T]
}

extension GetAllDBServiceProtocol {
    func getAll() -> [T] {
        DBStorageService().fetch(by: T.self)
    }
}

class DBStorageService {
    private let storage: Realm?
        
    init(
        _ configuration: Realm.Configuration = Realm.Configuration()
    ) {
        self.storage = try? Realm(configuration: configuration)
    }

    func fetch<T: Object>(by type: T.Type) -> [T] {
        guard let storage else { return [] }
        return storage.objects(T.self).toArray()
    }
}

Note: maybe it is incorrect to use RealmSwift via this way but it is not linked with the problem.

Now I want to add a service which returns a concrete realm object array:

class SomeDBService {
    private let storage: DBStorageService
    
    init(storage: DBStorageService) {
        self.storage = storage
    }
}

extension SomeDBService: GetAllDBServiceProtocol {
    func getall() -> [SomeDBEntity] {
        DBStorageService().fetch(by: SomeDBEntity.self)
    }
}

This works too but the problem is I can't access the default implementation of GetAllDBServiceProtocol - it becomes extra and can be safety removed.

So is it possible to make SomeDBService using default GetAllDBServiceProtocol implementation?


Solution

  • There are 2 problems with your code. Firstly, the method name of the default protocol implementation has a typo in it - getall vs getAll, which means you don't actually provide a default implementation.

    Secondly, when conforming to the protocol with the default implementation, you should simply define the associatedType via a typealias without declaring the getAll method.

    Here's a simplified working example that doesn't rely on Realm (adding back the Object type constraints will still work fine, I've just removed it to make the code compilable without needing Realm):

    protocol GetAllDBServiceProtocol {
        associatedtype Model
        func getAll() -> [Model]
    }
    
    extension GetAllDBServiceProtocol {
        func getAll() -> [Model] {
            DBStorageService().fetch(by: Model.self)
        }
    }
    
    class DBStorageService {
        func fetch<T>(by type: T.Type) -> [T] {
            []
        }
    }
    
    class SomeDBService {
        private let storage: DBStorageService
    
        init(storage: DBStorageService) {
            self.storage = storage
        }
    }
    
    extension SomeDBService: GetAllDBServiceProtocol {
        typealias Model = Int
    }