使用 Swift 泛型编写带有完成块的 API 请求

Writing API requests with completion blocks using Swift generics

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

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

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 包装器 class 设为通用 class 可以像这样解决问题:

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
    }
}

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

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

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

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

    }
}

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

    }
}

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

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

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 步:现在您只需调用此获取函数即可。可以这样做:

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

    }

您不仅会得到您的响应对象,它还会映射到您的模式 class。