Swift Generics - Попытка сделать конкретный протокол конкретным завершается неудачно при попытке использовать специализированный суб-протокол в качестве переменной - PullRequest
0 голосов
/ 14 октября 2019

Я хочу знать, почему мой SomeResourceRepository по-прежнему является общим, хотя он определен только в одном случае, когда я устанавливаю ResourceType = SomeResource, который форматирует XCode, как показано ниже с предложением where. Приведенный ниже код, который показывает точную настройку, которую я пытаюсь выполнить, написан на игровой площадке.

Я пытаюсь определить общий протокол для любого заданного ResourceType, так что протокол ResourceTypeRepository затем автоматически требуеттот же набор функций, без необходимости копировать-вставлять большую часть GenericRepository только для того, чтобы вручную заполнить ResourceType для каждого создаваемого мной репозитория. Причина, по которой я нуждаюсь в этом как протоколе, заключается в том, что я хочу смоделировать это для целей тестирования позже. Поэтому я предоставлю реализацию указанного протокола где-то еще в реальном приложении.

Моя интерпретация приведенного ниже кода заключается в том, что он должен работать, потому что и SomeResourceLocalRepository, и SomeResourceRemoteRepository являются конкретными, как яисключил связанный тип, определив их "поверх" SomeResourceRepository, который определяется только в том случае, если ResourceType == SomeResource.

import Foundation

struct SomeResource: Identifiable {
    let id: String
    let name: String
}

struct WhateverResource: Identifiable {
    let id: UUID
    let count: UInt
}

protocol GenericRepository: class where ResourceType: Identifiable {
    associatedtype ResourceType

    func index() -> Array<ResourceType>
    func show(id: ResourceType.ID) -> ResourceType?
    func update(resource: ResourceType)
    func delete(id: ResourceType.ID)
}

protocol SomeResourceRepository: GenericRepository where ResourceType == SomeResource {}
protocol SomeResourceLocalRepository: SomeResourceRepository {}
protocol SomeResourceRemoteRepository: SomeResourceRepository {}

class SomeResourceLocalRepositoryImplementation: SomeResourceLocalRepository {
    func index() -> Array<SomeResource> {
        return []
    }

    func show(id: String) -> SomeResource? {
        return nil
    }

    func update(resource: SomeResource) {
    }

    func delete(id: String) {
    }
}

class SomeResourceService {
    let local: SomeResourceLocalRepository

    init(local: SomeResourceLocalRepository) {
        self.local = local
    }
}

// Some Dip code somewhere
// container.register(.singleton) { SomeResourceLocalRepositoryImplementation() as SomeResourceLocalRepository }

Ошибки:

error: Generic Protocols.xcplaygroundpage:45:16: error: protocol 'SomeResourceLocalRepository' can only be used as a generic constraint because it has Self or associated type requirements
let local: SomeResourceLocalRepository
           ^

error: Generic Protocols.xcplaygroundpage:47:17: error: protocol 'SomeResourceLocalRepository' can only be used as a generic constraint because it has Self or associated type requirements
    init(local: SomeResourceLocalRepository) {

Возможно, мне придетсянайти другой способ сделать это, но это утомительно и довольно раздражает, так как мы создадим много дублирующего кода, и когда мы решим изменить API наших репозиториев, нам придется вручную изменить его для всех протоколов, как мыне следуйте общему «родительскому» протоколу в этом обходном пути.

Я прочитал Как передать протокол со связанным типом в качестве параметра в Swift и связанный с этим вопрос, найденный в ответе наэтот вопрос, а также Specializing Generic Protocol и др.

Мне кажется, что это должно работать, но это не так. Конечной целью является конкретный протокол, который можно использовать для внедрения зависимостей, например, container.register(.singleton) { ProtocolImplementation() as Protocol } согласно Dip - Простой контейнер внедрения зависимостей , НО без вставки копий, когда интерфейс протокола явно может быть сделан универсальным,как и выше.

...