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

如何解决用不同UseCase初始化ViewModel时的泛型约束错误?

问题描述

我想用MockUseCase(测试用)和APIUseCase(实际业务逻辑)初始化ViewModel,但过程中报错,试了各种方法都没搞定。当前错误截图如下:
错误截图

相关代码如下:

BaseUseCase 协议

protocol BaseUseCase {
    associatedtype _Request
    associatedtype _Response
    func execute(_ command: _Request, response: @escaping (_Response) -> Void)
}

APIUseCase 实现

struct APIUseCase : BaseUseCase {
   enum Request {
       case getPosts
   }
   enum Response {
       case recievedPosts(_ posts: [String])
   }
    
   typealias _Request = Request
   typealias _Response = Response
   
   func execute(_ command: _Request, response: @escaping (_Response) -> Void) {
       switch command {
           case .getPosts:
               response(.recievedPosts(["Post Returning from API"]))
       }
   }
}

MockUseCase 实现

struct MockUseCase : BaseUseCase {
    enum Request {
        case getPosts
    }
    enum Response {
        case recievedPosts(_ posts: [String])
    }
    typealias _Request = Request
    typealias _Response = Response
    
    func execute(_ command: _Request, response: @escaping (_Response) -> Void) {
        switch command {
            case .getPosts:
               response(.recievedPosts(["Post Returning from Mock"]))
        }
    }
}

ViewModel 代码

class ViewModel {
    var usecase: any BaseUseCase
            
    init(usecase: some BaseUseCase  = APIUseCase()) {
         self.usecase =  usecase
    }
        
    func execute() {
         usecase.execute(.getPosts) { response in }
    }
}

解决方案

问题根源有两个:一是any BaseUseCase的类型擦除导致编译器无法识别.getPosts对应的具体请求类型;二是APIUseCase和MockUseCase各自定义了独立的Request/Response枚举,哪怕case完全一样,它们也是不同的类型。

给你两种可行的修复方式:

方式一:统一请求响应类型 + 泛型ViewModel

把请求和响应类型抽成共用的,再用泛型让ViewModel保留UseCase的类型信息:

  1. 定义全局共用的请求、响应枚举:
enum PostRequest {
    case getPosts
}

enum PostResponse {
    case recievedPosts(_ posts: [String])
}
  1. 修改BaseUseCase协议:
protocol BaseUseCase {
    associatedtype Request = PostRequest
    associatedtype Response = PostResponse
    func execute(_ command: Request, response: @escaping (Response) -> Void)
}
  1. 简化两个UseCase的实现:
struct APIUseCase : BaseUseCase {
    func execute(_ command: PostRequest, response: @escaping (PostResponse) -> Void) {
        switch command {
            case .getPosts:
                response(.recievedPosts(["Post Returning from API"]))
        }
    }
}

struct MockUseCase : BaseUseCase {
    func execute(_ command: PostRequest, response: @escaping (PostResponse) -> Void) {
        switch command {
            case .getPosts:
                response(.recievedPosts(["Post Returning from Mock"]))
        }
    }
}
  1. 改成泛型ViewModel:
class ViewModel<UseCase: BaseUseCase> where UseCase.Request == PostRequest, UseCase.Response == PostResponse {
    private let usecase: UseCase
    
    init(usecase: UseCase = APIUseCase() as! UseCase) {
        self.usecase = usecase
    }
    
    func execute() {
        usecase.execute(.getPosts) { response in
            // 这里处理响应逻辑
        }
    }
}

使用示例:

// 正常业务用API
let apiVM = ViewModel()
// 测试用Mock
let mockVM = ViewModel(usecase: MockUseCase())

方式二:类型擦除封装 AnyBaseUseCase

如果不想用泛型ViewModel,可以封装一个类型擦除的容器来保留具体UseCase的行为:

  1. 先按方式一的步骤统一PostRequest和PostResponse,然后定义AnyBaseUseCase:
class AnyBaseUseCase<Request, Response>: BaseUseCase {
    private let _execute: (Request, @escaping (Response) -> Void) -> Void
    
    init<UseCase: BaseUseCase>(_ usecase: UseCase) where UseCase.Request == Request, UseCase.Response == Response {
        _execute = usecase.execute
    }
    
    func execute(_ command: Request, response: @escaping (Response) -> Void) {
        _execute(command, response)
    }
}
  1. 修改ViewModel:
class ViewModel {
    private let usecase: AnyBaseUseCase<PostRequest, PostResponse>
    
    init(usecase: AnyBaseUseCase<PostRequest, PostResponse> = AnyBaseUseCase(APIUseCase())) {
        self.usecase = usecase
    }
    
    func execute() {
        usecase.execute(.getPosts) { response in
            // 处理响应逻辑
        }
    }
}

使用示例:

// 正常业务
let apiVM = ViewModel()
// 测试用Mock
let mockVM = ViewModel(usecase: AnyBaseUseCase(MockUseCase()))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 09:55:19