【问题标题】:Writing API requests with completion blocks using Swift generics使用 Swift 泛型编写带有完成块的 API 请求
【发布时间】:2016-05-21 17:49:33
【问题描述】:

我正在 Swift 中试验泛型,并试图将其推向极限。

在我的应用程序中,我有一个非常简单的围绕 Alamofire 的 API 包装器。结构是这样的:

API -> Request -> Alamofire request

这里有一些通用代码,我将它们放入游乐场以测试一些概念。这是我目前所拥有的:

protocol SomeProtocol {
    var cheese: String { get }
    init()
}

class Something: SomeProtocol {
    required init() { }

    var cheese: String {
        return "wiz"
    }
}

class API {

    class func performRequest<T: SomeProtocol>(completion: (T?, NSError) -> Void) {

        // This code is irrelevant, just satisfying the completion param
        let test = T()
        let error = NSError(domain: "Pizza", code: 1, userInfo: nil)

        completion(test, error)
    }

}

func test() {
    API.performRequest<Something> { item, error in

    }
}

调用函数报错:

"Cannot explicitly specialize a generic function"

******更新******

根据下面的答案,删除典型的 泛型类型说明符并将预期类型添加到完成参数可以解决问题。只是一个简单的例子:

func test() {
    API.performRequest { (item: Something?, error) in

    }
}

此外,我发现将 API 包装器类设为通用类可以解决如下问题:

protocol SomeProtocol {
    var pizza: String { get }
}

class SomeObject: SomeProtocol {
    var pizza: String { return "pie" }
}

class API<T: SomeProtocol> {
    class func performRequest(completion: (T?, NSError?) -> Void) {

    }
}

func test() {
    API<SomeObject>.performRequest { item, error in
        // Do something with item, which has a type of SomeObject
    }
}

无论哪种方式,最终目标都已实现。我们有一个通用方法,它将执行一组任务并通过完成闭包返回基于每次使用时传入的类型的对象。

【问题讨论】:

  • 我相信编译器不知道该函数应用哪种类型。尝试在闭包中指定参数类型。
  • 这不会违背使用泛型的目的吗?据我了解,iOS 通常根据扫描参数推断类型。完成闭包似乎不能正确支持这一点。

标签: ios swift generics completionhandler type-alias


【解决方案1】:

泛型的工作方式是它们允许函数在其实现内部使用非专用变量。可以通过指定变量必须符合给定协议(这在声明中完成)来为这些变量添加功能。结果是一个可以用作许多类型的模板的函数。但是,当在代码本身中调用函数时,编译器必须能够专门化并将类型应用于泛型。

在上面的代码中,尝试替换

func test() {
    API.performRequest<Something> { item, error in

    }
}

func test() {
    API.performRequest { (item: Something?, error) in

    }
}

这让编译器知道它必须应用于函数的类型,而无需明确指定。您收到的错误消息现在应该更有意义了。

【讨论】:

  • 啊,好吧,我误解了你的第一句话。您的代码示例完全有意义。奇迹般有效!谢谢!
【解决方案2】:

这是我使用 alamofire 和 alamofire 对象映射器所做的: 第 1 步:创建符合 Mappable 协议的模态类。

class StoreListingModal: Mappable {
var store: [StoreModal]?
var status: String?
required init?(_ map: Map){

}

func mapping(map: Map) {
    store <- map["result"]
    status <- map["status"]
}
}

第 2 步:使用泛型类型创建获取请求:

func getDataFromNetwork<T:Mappable>(urlString: String, completion: (T?, NSError?) -> Void) {
    Alamofire.request(.GET, urlString).responseObject { (response: Response<T, NSError>) in
        guard response.result.isSuccess else{
            print("Error while fetching: \(response.result.error)")
            completion(nil, response.result.error)
            return
        }
        if let responseObject = response.result.value{
            print(responseObject)
            completion(responseObject, nil)
        }
    }
}

第 3 步:现在您只需调用这个 fetch 函数。可以这样做:

self.getDataFromNetwork("your url string") { (userResponse:StoreListingModal?, error) in

    }

您不仅会获得响应对象,还会将其映射到您的模态类。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-24
    • 2018-10-19
    • 2014-09-10
    • 2017-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-10
    相关资源
    最近更新 更多