协议<name>只能用作通用约束,因为它具有Self或关联类型要求

时间:2018-03-23 10:13:36

标签: swift generics

说明

我发现了一些针对此编译错误消息的StackOverflow问题,但没有一个答案对我有帮助。

从一开始,我就定义了一个ViewModel协议:

protocol FormVMProtocol: FormCellVMsProvider {

    associatedtype RequestModel: GatewayObjectRequestModel

    weak var delegate: EventFromVMDelegate? { get set }

    var handleDidCancelForm: (() -> ())? { get set }
    var handleDidAcceptForm: ((RequestModel) -> ())? { get set }

    var title: String { get }

    var cellsViewModels: [EventFormCellVMProtocol] { get }
    var numberOfRows: Int { get }

    func viewModelForRow(at indexPath: IndexPath) -> EventFormCellVMProtocol
    func handleDidSelectRow(at indexPath: IndexPath)

    func getIndexPath(for rowViewModel: EventFormCellVMProtocol) -> IndexPath?
    func refreshRow(with viewModel: EventFormCellVMProtocol)
    func refreshAllRows()

    func createRequestModel() -> Promise<RequestModel>

    func cancelForm()
    func sendForm() -> Promise<()>
}

带扩展名:

extension FormVMProtocol {

    var numberOfRows: Int {
        return cellsViewModels.count
    }

    func viewModelForRow(at indexPath: IndexPath) -> EventFormCellVMProtocol {
        return cellsViewModels[indexPath.row]
    }

    func handleDidSelectRow(at indexPath: IndexPath) {
        viewModelForRow(at: indexPath).onSelected?()
    }

    func cancelForm() {
        handleDidCancelForm?()
    }

    func sendForm() -> Promise<()> {
        return createRequestModel()
            .then {
                self.handleDidAcceptForm?($0)
            }
    }

    func getIndexPath(for rowViewModel: EventFormCellVMProtocol) -> IndexPath? {
        guard let index = cellsViewModels.index(where: { rowViewModel === $0 }) else { return nil }

        return IndexPath(row: index, section: 0)
    }

    func refreshRow(with viewModel: EventFormCellVMProtocol) {
        guard let indexPath = getIndexPath(for: viewModel) else { return }

        delegate?.reloadRow(at: indexPath)
    }

    func refreshAllRows() {
        delegate?.reloadAllRows()
    }
}

然后我创建了ViewController:

final class AddEventFormVC: BaseTileVC {

    private var bottomButtonsStackView: UIStackView!
    private var cancelButton: CustomButton!
    private var acceptButton: CustomButton!
    private var tableView: UITableView!

    private let viewModel: FormVMProtocol

    init(viewModel: FormVMProtocol) {
        self.viewModel = viewModel

        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        buildUI()
        viewModel.delegate = self
    }
}

///REST OF VC IS NOT IMPORTANT FOR THIS QUESTION

现在,我的想法是创建大量FormVMProtocol子类并将它们注入AddEventFormVC以重用一次创建的VC。

例如(其中一个):

final class NoteEventFormVM: FormVMProtocol {

    typealias RequestModel = NoteEvent.InsertRequestModel

    weak var delegate: EventFromVMDelegate?

    var handleDidCancelForm: (() -> ())?
    var handleDidAcceptForm: ((RequestModel) -> ())?

    private let databaseHelper: DatabaseHelperProtocol
    private let authorizationService: AuthorizationServiceProtocol

    private lazy var noteTypeCellVM: EventFormSelectedValueCellVM<AddEventNoteTypeSelectionModel> = {
        return provideFormSelectedValueCellVM(title: "Note Type".localized)
    }()
    private lazy var commentCellVM: EventFormStringDataCellVM = {
        return provideFormStringDataCellVM(title: "Comment".localized)
    }()
    private lazy var eventDateCellVM: EventFormDateCellVM = {
        return provideFormDateCellVM(title: "Event Date")
    }()

    lazy var cellsViewModels: [EventFormCellVMProtocol] = {
        return [noteTypeCellVM, commentCellVM, eventDateCellVM]
    }()

    var title: String {
        return "Notes".localized
    }

    init(databaseHelper: DatabaseHelperProtocol,
         authorizationService: AuthorizationServiceProtocol) {

        //INIT
    }

    func createRequestModel() -> Promise<RequestModel> {
        //PROVIDE RequestModel
    }
}

我想注意,我在此处将associatedtype指定为NoteEvent.InsertRequestModel

使用示例:

    let vm: FormVMProtocol

    switch eventType {
    case .note:
        vm = NoteEventFormVM()
    default:
        //rest of event types
    }

    vm.handleDidCancelForm = { [weak self] in
        let _ = self?.navigationController.popViewController(animated: true)
    }

    let eventFromVC = AddEventFormVC(viewModel: vm)
    navigationController.pushViewController(eventFromVC, animated: true)

问题

在我的AddEventFormVC编译器中说

Protocol 'FormVMProtocol' can only be used as a generic constraint because it has Self or associated type requirements

我不知道如何满足编译器。

编辑1

@Jack,FormCellVMsProvider有助于构建cellVM。我认为在这种情况下并不重要,但您可以检查一个实现:

protocol FormCellVMsProvider: class {

    func reloadSelectionValueCellVM<T>(_ cellVM: EventFormSelectedValueCellVM<T>, using fetchMethod: () -> Promise<[T]>, selectInitalValue: Bool) -> Promise<()>

    func provideFormDateCellVM(title: String) -> EventFormDateCellVM
    func provideFormSelectedValueCellVM<T: SelectionDialogRenderable>(title: String) -> EventFormSelectedValueCellVM<T>
    func provideFormStringDataCellVM(title: String) -> EventFormStringDataCellVM
}

编辑2

好的,我取得了一些成就。我将我的VC改为通用版:

final class AddEventFormVC<T: FormVMProtocol>: BaseVC {

    private let viewModel: T

    init(viewModel: T) {
        self.viewModel = viewModel

        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // REST OF VC
}

编译允许我以这种方式使用它:

let vm = NoteEventFormVM()
let vc = EventFormVC(viewModel: vm)

navigationController.pushViewController(vc, animated: true)   

还有一个问题。我无法利用协议:

let vm: FormVMProtocol
vm.handleCancel = { //CODE }
vm.handleSuccess = { //CODE }

switch eventType {
case .note:
    vm = NoteEventFormVM()
case .custom:
    vm = CustomEventFromVM()
default:
    //rest of event types
}

let vc = AddEventFormVC()
navigationController.pushViewController(vc, animated: true)

因为在行let vm: FormVMProtocol中编译器正在喊Protocol <NAME> can only be used as a generic constraint because it has Self or associated type requirements

我需要通过案例复制/通过我的代码:

switch eventType {
case .note:
    let vm = NoteEventFormVM()
    vm.handleCancel = { //CODE }
    vm.handleSuccess = { //CODE }

    let vc = AddEventFormVC()
    navigationController.pushViewController(vc, animated: true)
case .custom:
    let vm = CustomEventFromVM()
    vm.handleCancel = { //CODE }
    vm.handleSuccess = { //CODE }

    let vc = AddEventFormVC()
    navigationController.pushViewController(vc, animated: true)
default:
    //rest of event types
} 

我正在寻找更好的解决方案。

0 个答案:

没有答案