Swift Conforming Multiple Protocols inherits from Same Protocol with Associated Type

433 views Asked by At

I am trying to build a Publisher/Observer system to easily observe data changes from API and publish through the app regarding controllers.

The point I have come so far is just working for single observing. However today I came across an issue, which prevents an UIViewController to observe more than one Publisher.

I am not sharing the whole code just to prevent confusions and problem below is the main reason, I think. If there is a solution for it, then my problem would be solved.

protocol Base {
    associatedtype BaseType
}

protocol One: Base where BaseType == String {}

protocol Two: Base where BaseType == Int {}

class BaseClass {}

extension BaseClass: One {
    typealias BaseType = String
}

extension BaseClass: Two {}

When I try to extend the BaseClass to conform Two, it throws

'Two' requires the types 'BaseClass.BaseType' (aka 'String') and 'Int' be equivalent

There are additional methods in the Base protocol and they depend on the BaseType parameter-wise. But as I said before, I do not think that is the issue.

Any suggestions?

UPDATE: More details with an use-case

My base protocols are as follows;

protocol Publishable {
    associatedtype Publisher: Service
}

protocol Service {
    associatedtype Publishing: Publishable
    var data: Publishing? { get set }
    var observers: [AnyObserver<Publishing>] { get set }
    func publish(_ data: Publishing)
    func add(_ observer: AnyObserver<Publishing>)
}

protocol Observer {
    associatedtype ObservingType: Publishable
    func subscribe(toService service: ObservingType.Publisher)
    func received(_ data: ObservingType)
}

Then I needed a solution for adding different types conforms to Observer into same array. Type erasure applied;

struct AnyObserver<Observing: Publishable>: Observer {
    
    private let _subscribe: (Observing.Publisher) -> Void
    private let _received: (Observing) -> Void
    
    init<Base: Observer>(_ base: Base) where Observing == Base.ObservingType {
        _received = base.received
        _subscribe = base.subscribe
    }
    
    func subscribe(toService service: Observing.Publisher) {
        _subscribe(service)
    }
    
    func received(_ data: Observing) {
        _received(data)
    }
}

Afterwards our use case is here. Lets say we have a AViewController which needs data about Books and Movies from API. I did not include the API part because it is an another layer.

struct Book: Codable {
    var name: String?
    var author: String?
}

struct BookList: Codable {
    var data: [Book]?
    var status: Int?
}

extension BookList: Publishable {
    typealias Publisher = BookListService
}

struct Movie: Codable {
    var name: String?
    var director: String?
}

struct MovieList: Codable {
    var data: [Movie]?
    var status: Int?
}

extension MovieList: Publishable {
    typealias Publisher = MovieListService
}

Publishable extends BookList and MovieList because they need to carry information about which Service object can publish them. BookListService and MovieListService are declared as follows;

class BookListService: Service {
    var data: BookList?
    
    var observers: [AnyObserver<BookList>] = []
    
    func publish(_ data: BookList) {
        //publish
    }
    
    func add(_ observer: AnyObserver<BookList>) {
        observers.append(observer)
    }
}

class MovieListService: Service {
    var data: MovieList?
    
    var observers: [AnyObserver<MovieList>] = []
    
    func publish(_ data: MovieList) {
        //publish
    }
    
    func add(_ observer: AnyObserver<MovieList>) {
        observers.append(observer)
    }
}

And finally Observer protocols regarding BookList and MovieList.

protocol BookListObserver: Observer where ObservingType == BookList {}
protocol MovieListObserver: Observer where ObservingType == MovieList {}

And for our final use-case regards AViewController:

class AViewController: UIViewController {
    
    let bookListService = BookListService()
    let movieListService = MovieListService()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        subscribe(toService: bookListService)
    }
}

extension AViewController: BookListObserver {
    
    func received(_ data: BookList) {
        // booklist received
    }
    
    func subscribe(toService service: BookListService) {
        service.add(AnyObserver(self))
    }
    
}

Until this point there are no kind of an error. However, all after this, if extending AViewController conforming to MovieListObserver throws 'MovieListObserver' requires the types 'BookList' and 'MovieList' be equivalent

extension AViewController: MovieListObserver {}

Just let me know, if you need further updates.

UPDATE:

I have found this topic, but could not apply to my situation somehow.

0

There are 0 answers