ADB
ADB

Reputation: 719

How to use a protocol with associated type with ObservableObject?

I'm trying to create a protocol for something that must also conform to ObservableObject, which will live within another object Services:

protocol ThumbnailServiceProtocol: ObservableObject where ObjectWillChangePublisher == ObservableObjectPublisher {
    var processingThumbnails: [String] { get }
}

final class ThumbnailService: ThumbnailServiceProtocol {
    var processingThumbnails: [String] = []
}

struct Services {
    var thumbnailService: ThumbnailServiceProtocol
}

var services = Services(thumbnailService: ThumbnailService())

My problem comes when I try to swap out services to use a mock in my tests like this:

final class MockThumbnailService: ThumbnailServiceProtocol {
    var processingThumbnails: [String] = []
}

services = Services(thumbnailService: MockThumbnailService()) /// Cannot convert value of type 'MockThumbnailService' to expected argument type 'ThumbnailServiceProtocol'

I think the problem is coming from the fact that ObservableObject is a protocol with an associated type (associatedtype ObjectWillChangePublisher) but I thought I'd 'filled in the hole' by specifying where ObjectWillChangePublisher == ObservableObjectPublisher.

Am I missing something please?

Edit: to clarify, services is a global object which I'd like to swap out with a new instance of Services containing mocks in my unit tests, which is why I've tried to make this work:

var services = Services(thumbnailService: ThumbnailService())
services = Services(thumbnailService: MockThumbnailService()) /// Cannot convert value of type 'MockThumbnailService' to expected argument type 'ThumbnailServiceProtocol'

Upvotes: 1

Views: 720

Answers (1)

Rengers
Rengers

Reputation: 15218

The problem in your example is that Services.processingThumbnails is of type ThumbnailService, and not ThumbnailServiceProtocol. These types are not the same. You need to make Services generic. Try this:

protocol ThumbnailServiceProtocol: ObservableObject {
    var processingThumbnails: [String] { get }
}

final class ThumbnailService: ThumbnailServiceProtocol {
    var processingThumbnails: [String] = []
}

final class MockThumbnailService: ThumbnailServiceProtocol {
    var processingThumbnails: [String] = []
}

struct Services<T> where T: ThumbnailServiceProtocol, T.ObjectWillChangePublisher == ObservableObjectPublisher {
    var thumbnailService: T
}

let servicesA = Services(thumbnailService: ThumbnailService())
let servicesB = Services(thumbnailService: MockThumbnailService())

Upvotes: 3

Related Questions