Я работаю над приложением, которому необходимо запрашивать несколько API. Я придумал классы для каждого поставщика API (и, в более крайних случаях, класс для каждой конкретной конечной точки API). Это связано с тем, что каждый запрос API должен возвращать очень строгий тип ответа, поэтому, если API может, например, возвращать как профили пользователей, так и изображения профилей, я хочу, чтобы ответ был специфичным для любого из них.
Я реализовал это примерно следующим образом:
protocol MicroserviceProvider {
associatedtype Response
}
protocol ProfilePictureMicroserviceProvider: MicroserviceProvider {
func getPicture(by email: String, _ completion: (Response) -> Void)
}
class SomeProfilePictureAPI: ProfilePictureMicroserviceProvider {
struct Response {
let error: Error?
let picture: UIImage?
}
func getPicture(by email: String, _ completion: (Response) -> Void) {
// some HTTP magic
// will eventually call completion(_:) with a Response object
// which either holds an error or a UIImage.
}
}
Поскольку я хочу иметь возможность использовать классы модульного тестирования, которые будут полагаться на этот API, мне нужно иметь возможность динамически вводить эту зависимость изображения профиля. По умолчанию он будет использовать SomeProfilePictureAPI
, но при запуске тестов я смогу заменить его на MockProfilePictureAPI
, который по-прежнему будет соответствовать ProfilePictureMicroserviceProvider
.
И поскольку я использую связанные типы, мне нужно создавать классы, зависящие от ProfilePictureMicroserviceProvider
generic.
Сначала я наивно пытался написать свой контроллер представления вот так
class SomeClass {
var profilePicProvider: ProfilePictureMicroserviceProvider
}
Но это только привело к печально известному «Протокол ProfilePictureMicroserviceProvider, который можно использовать только как общее ограничение, потому что он имеет собственные или связанные требования типа», ошибка времени компиляции.
Теперь я читал об этой проблеме последние пару дней, пытаясь осмыслить протоколы со связанными типами (PATS), и решил, что выберу маршрут таких общих классов. :
class SomeClass<T: ProfilePictureMicroserviceProvider> {
var profilePicProfider: T = SomeProfilePictureAPI()
}
Но даже тогда я получаю следующую ошибку:
Cannot convert value of type 'SomeProfilePictureAPI' to specified type 'T'
Даже несмотря на то, что T
ограничен протоколом ProfilePictureMicroserviceProvider
и SomeProfilePictureAPI
его придерживается ...
По сути, основная идея заключалась в достижении двух целей: обеспечить соблюдение структуры микросервиса с обязательным типом ответа и сделать каждый микросервис имитируемым для модульных тестов зависимых классов.
Сейчас я застрял на выборе одного из двух, так как не могу заставить его работать. Любая помощь в объяснении того, что я делаю не так, будет очень приветствоваться.
Я также посмотрел на стирание шрифта. Но мне это кажется очень странным и довольно сложным для чего-то, что выглядит не так во многих аспектах.
Итак, в основном мой вопрос состоит из двух частей: как я могу заставить свои микросервисы определять свой собственный тип ответа? И как я могу легко заменить их фиктивными микросервисами в классах, которые от них зависят?