使用 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。
我正在 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。