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

Swift带关联类型协议的final类多态:Apollo GraphQLMutation通用返回方案

问题根源

Swift中带关联类型的协议(PAT)无法直接作为返回值类型使用,仅能作为泛型约束。Apollo的GraphQLOperation协议定义了Data关联类型,其子协议GraphQLMutation自然也属于PAT,这是触发编译错误的核心原因。Apollo自动生成的final类不影响泛型、类型擦除方案的使用,final仅限制类不可被继承,只要类实现了GraphQLMutation协议即可被正常处理。

方案一:泛型约束工厂(推荐,适用于绝大多数场景)

通过泛型约束让调用方指定Mutation类型,编译器可自动推导关联类型,完全保留编译期类型安全,无需额外类型转换。

实现代码

func getMutation<T: GraphQLMutation>(for type: T.Type, parameters: [String: Any]) -> T {
    switch type {
    case is CreateUserMutation.Type:
        return CreateUserMutation(
            name: parameters["name"] as! String,
            age: parameters["age"] as! Int
        ) as! T
    case is UpdatePostMutation.Type:
        return UpdatePostMutation(
            postId: parameters["postId"] as! String,
            content: parameters["content"] as! String
        ) as! T
    // 其他Mutation类型按需补充case
    default:
        fatalError("未匹配到对应的Mutation类型")
    }
}

调用示例

// 直接传入生成的Mutation类即可,返回值可直接喂给ApolloClient的perform方法
let mutation = getMutation(for: CreateUserMutation.self, parameters: ["name": "张三", "age": 20])
apollo.perform(mutation: mutation) { result in
    // result类型自动推导为GraphQLResult<CreateUserMutation.Data>,无需手动转换
    switch result {
    case .success(let res):
        // 直接处理返回结果
    case .failure(let error):
        // 处理错误
    }
}

优缺点

  • 优点:实现成本最低,无性能损耗,完全保留类型安全
  • 缺点:调用时必须明确指定Mutation类型,不支持运行时动态决定调用的Mutation

方案二:类型擦除工厂(适用于运行时动态选Mutation的场景)

如果需要根据运行时参数动态决定返回的Mutation类型,可以通过类型擦除封装GraphQLMutation的执行逻辑,抹平关联类型差异。

实现代码

// 自定义类型擦除结构体
struct AnyGraphQLMutation {
    private let _perform: (ApolloClient, @escaping (Result<GraphQLSelectionSet, Error>) -> Void) -> Cancellable
    
    init<T: GraphQLMutation>(_ mutation: T) {
        _perform = { client, completion in
            client.perform(mutation: mutation) { result in
                switch result {
                case .success(let graphQLResult):
                    if let data = graphQLResult.data {
                        completion(.success(data))
                    } else if let errors = graphQLResult.errors, !errors.isEmpty {
                        let errMsg = errors.map{ $0.localizedDescription }.joined(separator: "\n")
                        completion(.failure(NSError(domain: "ApolloMutation", code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])))
                    } else {
                        completion(.failure(NSError(domain: "ApolloMutation", code: -2, userInfo: [NSLocalizedDescriptionKey: "空返回"])))
                    }
                case .failure(let error):
                    completion(.failure(error))
                }
            }
        }
    }
    
    /// 直接调用该方法执行Mutation,无需再传入ApolloClient的perform
    func perform(with client: ApolloClient, completion: @escaping (Result<GraphQLSelectionSet, Error>) -> Void) -> Cancellable {
        return _perform(client, completion)
    }
}

// 工厂方法
func getMutation(by action: String, parameters: [String: Any]) -> AnyGraphQLMutation {
    switch action {
    case "createUser":
        let mutation = CreateUserMutation(
            name: parameters["name"] as! String,
            age: parameters["age"] as! Int
        )
        return AnyGraphQLMutation(mutation)
    case "updatePost":
        let mutation = UpdatePostMutation(
            postId: parameters["postId"] as! String,
            content: parameters["content"] as! String
        )
        return AnyGraphQLMutation(mutation)
    // 其他Mutation按需补充case
    default:
        fatalError("未匹配到对应的Mutation类型")
    }
}

调用示例

// 运行时根据字符串动态选择Mutation
let mutation = getMutation(by: "createUser", parameters: ["name": "张三", "age": 20])
mutation.perform(with: apollo) { result in
    switch result {
    case .success(let data):
        // 需要手动把通用结果转为对应Mutation的Data类型
        if let userData = data as? CreateUserMutation.Data {
            // 处理返回结果
        }
    case .failure(let error):
        // 处理错误
    }
}

优缺点

  • 优点:支持运行时动态选择Mutation,可将不同类型的Mutation实例存入同一集合统一管理
  • 缺点:丢失部分编译期类型安全,返回结果需要手动转换类型

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 13:24:08