如何解决用不同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的类型信息:
- 定义全局共用的请求、响应枚举:
enum PostRequest { case getPosts } enum PostResponse { case recievedPosts(_ posts: [String]) }
- 修改BaseUseCase协议:
protocol BaseUseCase { associatedtype Request = PostRequest associatedtype Response = PostResponse func execute(_ command: Request, response: @escaping (Response) -> Void) }
- 简化两个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"])) } } }
- 改成泛型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的行为:
- 先按方式一的步骤统一
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) } }
- 修改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
相关产品推荐
相关产品推荐

