Generic Swift completion handler

I have a method that has a method called performRequest() . It takes a JSONRequest parameter. JSONRequest looks something like this:

 public typealias JSONCompletionHandler = ([Entity]?, NSError?) -> Void public class JSONRequest: Request { public var completionHandler: JSONCompletionHandler public var endPoint: String } 

And performRequest() looks like this:

 public func performJSONRequest<T where T: Entity>(jsonRequest: JSONRequest, _: Type) { // Make a request which returns a data object var entities = self.convertJSONData(data, jsonKey: jsonRequest.jsonKey, T.self) // Error: 'T' is not identical to 'Entity' jsonRequest.completionHandler(entities, error) } 

As you can see, it calls convertJSONData() , which looks like this:

 func convertJSONData<T where T: Entity>(jsonData: AnyObject, _: T.Type) -> [T] { // Convert the data into Swift collection classes, enumerate over them, and create model objects var json = JSON(data: jsonData as NSData, options: nil, error: nil) var entities = [T]() for obj in json { let book = T(json: obj) entities.append(book) } return entities 

Entity is a protocol that matches all of my model classes, such as Author and Book .

It defines one method: init(json: JSON) . Since T defined as T:Entity , I can simply call T:(json: obj) to instantiate any class matching Entity .

I want to use performJSONRequest() to execute a query for any object matching the object. For example, I want to create a query for book instances as follows:

 var request = JSONRequest(endPoint: "books") { (let object: [Entity]?, let error: NSError?) -> Void in // Cast object to [Book] and have fun } performJSONRequest<Book>(request) 

For life, I cannot find out how I will implement this. Right now, I am getting an error in the performJSONRequest() method saying 'T' is not identical to 'Entity' . If I define the array in the completion handler as [AnyObject] , I get the same error: 'T' is not identical to 'AnyObject' .

Thanks for any help!

+2
source share
1 answer

The solution is to convert the generic type to the JSONRequest class - this way the JSONCompletionHandler can be determined using the generated type that you request, not just the Entity protocol. (Some of your codes looked a bit pseudo, so this might require some tweaking to fit into your implementation.)

JSONRequest now a generic class with a restriction of type Entity :

 public class JSONRequest<T: Entity>: Request { // completion handler defined in terms of `T` public typealias JSONCompletionHandler = ([T]?, NSError?) -> Void // no further changes public var completionHandler: JSONCompletionHandler public var endPoint: String public init(endPoint: String, completionHandler: JSONCompletionHandler) { self.endPoint = endPoint self.completionHandler = completionHandler } } 

performJSONRequest no longer needs a type passed as a separate parameter. Since JSONRequest is specialized, it gets type information from this parameter:

 public func performJSONRequest<T: Entity>(jsonRequest: JSONRequest<T>) { // create array of `T` somehow var entities: [T] = [] var error: NSError? // completionHandler expects [T]? and NSError? jsonRequest.completionHandler(entities, error) } 

When creating a JSONRequest instance JSONRequest type specified in the completion handler (for example, [Book]? ) Will set the type for the general JSONRequest and hold for the whole process:

 var request = JSONRequest(endPoint: "books") { (books: [Book]?, error) in println(books?.count) } performJSONRequest(request) 
+8
source

Source: https://habr.com/ru/post/1208528/


All Articles