You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift编译报错:含关联类型/Self的协议仅能用作泛型约束求助

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

Hey there, let's break down this error and get your code back on track!

Why This Error Happens

The root issue here is your FormVMProtocol includes an associatedtype RequestModel. In Swift, protocols with associated types (or Self requirements) can't be used as regular types—you can't declare a variable like var viewModel: FormVMProtocol directly, because Swift can't infer the concrete type of RequestModel from just the protocol. Instead, these protocols only work as generic constraints.

Solutions to Try

1. Use Generic Constraints (Most Recommended)

The cleanest fix is to use a generic type wherever you're trying to use FormVMProtocol. For example, if you're using this protocol in a view controller:

class FormViewController<VM: FormVMProtocol>: UIViewController {
    var viewModel: VM!
    
    // Rest of your view controller code...
}

When you initialize this view controller, you'll pass in a concrete implementation of FormVMProtocol (like MyConcreteFormVM), and Swift will automatically resolve the associated RequestModel type for you.

2. Implement Type Erasure

If you need to use the protocol in a context where generics aren't feasible (like storing multiple view models in an array), you can create a "type-erased" wrapper that hides the associated type details:

class AnyFormVM<RequestModel: GatewayObjectRequestModel>: FormVMProtocol {
    // Store closures to forward calls to the underlying viewModel
    private let _getDelegate: () -> EventFromVMDelegate?
    private let _setDelegate: (EventFromVMDelegate?) -> Void
    private let _handleDidCancelForm: (() -> ())?
    private let _handleDidAcceptForm: ((RequestModel) -> ())?
    private let _title: () -> String
    private let _cellsViewModels: () -> [EventFormCellVMProtocol]
    private let _getIndexPath: (EventFormCellVMProtocol) -> IndexPath?
    private let _refreshRow: (EventFormCellVMProtocol) -> Void
    private let _refreshAllRows: () -> Void
    private let _createRequestModel: () -> Promise<RequestModel>
    
    // Initialize with a concrete viewModel that conforms to FormVMProtocol
    init<VM: FormVMProtocol>(_ viewModel: VM) where VM.RequestModel == RequestModel {
        _getDelegate = { viewModel.delegate }
        _setDelegate = { viewModel.delegate = $0 }
        _handleDidCancelForm = viewModel.handleDidCancelForm
        _handleDidAcceptForm = viewModel.handleDidAcceptForm
        _title = { viewModel.title }
        _cellsViewModels = { viewModel.cellsViewModels }
        _getIndexPath = viewModel.getIndexPath
        _refreshRow = viewModel.refreshRow
        _refreshAllRows = viewModel.refreshAllRows
        _createRequestModel = viewModel.createRequestModel
    }
    
    // Conform to FormVMProtocol by forwarding to the stored closures
    weak var delegate: EventFromVMDelegate? {
        get { _getDelegate() }
        set { _setDelegate(newValue) }
    }
    
    var handleDidCancelForm: (() -> ())? {
        get { _handleDidCancelForm }
        set { /* Add setter logic if your concrete VM requires it */ }
    }
    
    var handleDidAcceptForm: ((RequestModel) -> ())? {
        get { _handleDidAcceptForm }
        set { /* Add setter logic if needed */ }
    }
    
    var title: String { _title() }
    var cellsViewModels: [EventFormCellVMProtocol] { _cellsViewModels() }
    
    func getIndexPath(for rowViewModel: EventFormCellVMProtocol) -> IndexPath? {
        _getIndexPath(rowViewModel)
    }
    
    func refreshRow(with viewModel: EventFormCellVMProtocol) {
        _refreshRow(viewModel)
    }
    
    func refreshAllRows() {
        _refreshAllRows()
    }
    
    func createRequestModel() -> Promise<RequestModel> {
        _createRequestModel()
    }
    
    // Default implementations from your extension are already handled
}

Now you can use this wrapper like a regular type:

var viewModel: AnyFormVM<MyRequestModel> = AnyFormVM(MyConcreteFormVM())

3. Adjust Your Protocol Design (Conditional)

If your business logic allows, you could remove the associated type and use a base protocol instead. For example:

protocol FormVMProtocol: FormCellVMsProvider { 
    weak var delegate: EventFromVMDelegate? { get set } 
    var handleDidCancelForm: (() -> ())? { get set } 
    var handleDidAcceptForm: ((GatewayObjectRequestModel) -> ())? { get set } 
    var title: String { get } 
    var cellsViewModels: [EventFormCellVMProtocol] { get } 
    // ... rest of your methods
    func createRequestModel() -> Promise<GatewayObjectRequestModel> 
}

This works if you can accept working with the base GatewayObjectRequestModel type (you might need to downcast later), but it's less type-safe than the first two solutions.

Final Notes

Stick with the generic constraint approach (Solution 1) whenever possible—it's the most idiomatic Swift solution and keeps your code type-safe. Type erasure (Solution 2) is great for cases where you need flexibility with multiple conforming types.

内容的提问来源于stack exchange,提问作者Kamil Harasimowicz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:56:41