aryaxt / OCMapper

Objective-C library to easily map NSDictionary to model objects, works perfectly with Alamofire. ObjectMapper works similar to GSON
MIT License
347 stars 45 forks source link

Alamofire extension for Alamofire 3.x #53

Closed balazsgerlei closed 7 years ago

balazsgerlei commented 8 years ago

Hi!

I see there is an Alamofire Request extension example in the README, but if I'm correct it is not for the newest, 3.x version of Alamofire. Can you update it to show how can OCMapper be used with Alamofire 3.x. It would be really appreciated!

balazsgerlei commented 8 years ago

If I'm correct, it would be something like this (correct me if I'm wrong):

import Alamofire
import OCMapper

public extension Request {

    public func responseObjects<T: NSObject> (type: T.Type, completionHandler: Result<[T]?, NSError> -> Void) -> Self {

        return response(responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments)) { response in
            switch response.result {
            case .Success:
                let objects = ObjectMapper.sharedInstance().objectFromSource(response.result.value, toInstanceOfClass: type) as? [T]
                completionHandler(Result.Success(objects))
            case .Failure(let error):
                completionHandler(Result.Failure(error))
            }
        }
    }

    public func responseObject<T: NSObject> (type: T.Type, completionHandler: Result<T?, NSError> -> Void) -> Self {

        return response(responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments)) { response in
            switch response.result {
            case .Success:
                let object = ObjectMapper.sharedInstance().objectFromSource(response.result.value, toInstanceOfClass: type) as? T
                completionHandler(Result.Success(object))
            case .Failure(let error):
                completionHandler(Result.Failure(error))
            }
        }
    }

}
aryaxt commented 8 years ago

Hey, I don't have Alamofire around or a test project, but maybe

public func responseObjects<T: NSObject> (type: T.Type, completionHandler: Result<[T]?, NSError> -> Void) -> Self {

to

public func responseObjects<T: NSObject> (type: T.Type, completionHandler: Result<[T], ErrorType> -> Void) -> Self {
balazsgerlei commented 8 years ago

Why switching the optional to a normal type make it better?

However I realized that my suggested implementation is not the best solution, I will suggest another way soon, but I have to try it out properly first. My fault was that these two "responseObject" methods should use the Alamofire type "Response" instead of "Result".

balazsgerlei commented 8 years ago

Ok, after some experiment I now get why you suggested changing "T?" to "T" and "[T]?" to "[T]": to avoid double optionals. Handling serialization failures is better by using Errors than returning success with Optional value.

aryaxt commented 8 years ago

right for errors you want to use Failure

public enum Result<Value, Error: ErrorType> {
    case Success(Value)
    case Failure(Error)
aryaxt commented 8 years ago

Haven't tested this just wrote it based on what you have

extension Request {

    public func responseObjects<T: NSObject> (type type: T.Type, completion: Result<[T], NSError> -> Void) -> Self {
        return responseJSON {
            switch $0.result {
            case .Success(let json):
                guard let object = ObjectMapper.sharedInstance().objectFromSource(json, toInstanceOfClass: type) as? [T] else {
                    let errorResult = Result.Failure(NSError(domain: "replaceMeWithErrorTypeEnum", code: 0, userInfo: nil))
                    completion(errorResult)
                    return
                }

                completion(Result.Success(object))

            case .Failure(let error):
                completion(Result.Failure(error))
            }
        }
    }

}

Usage

request.responseObjects(type: User.self) {
   switch $0 {
   case .Success(let users):
      print(users.count)

   case .Failure(let error):
      print(error)
   }
}
balazsgerlei commented 8 years ago

I came up with something similar, though I prefer having if-else for the success and failure rather than guard, but that is just personal preference. Also, I declared a domain and error code for errors.

import Alamofire
import OCMapper

public extension Request {

    public static let OcMapperErrorDomain = "com.alamofire.ocmapper.error"

    public enum ErrorCode: Int {
        case ObjectSerializationFailed = -7000
    }

    public func responseObjects<T: NSObject> (type: T.Type, completionHandler: Response<[T], NSError> -> Void) -> Self {

        return response(responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments)) {
            response
            in
            switch response.result {
            case .Success:
                if let objects = ObjectMapper.sharedInstance().objectFromSource(response.result.value, toInstanceOfClass: type) as? [T] {
                    completionHandler(Response(request: response.request, response: response.response, data: response.data, result: Result.Success(objects)))
                } else {
                    let failureReason = "Objects could not be serialized from JSON."
                    let userInfo: Dictionary<NSObject, AnyObject> = [NSLocalizedFailureReasonErrorKey: failureReason]
                    let error = NSError(domain: Request.OcMapperErrorDomain, code: ErrorCode.ObjectSerializationFailed.rawValue, userInfo: userInfo)
                    completionHandler(Response(request: response.request, response: response.response, data: response.data, result: Result.Failure(error)))
                }
            case .Failure(let error):
                completionHandler(Response(request: response.request, response: response.response, data: response.data, result: Result.Failure(error)))
            }
        }
    }

    public func responseObject<T: NSObject> (type: T.Type, completionHandler: Response<T, NSError> -> Void) -> Self {

        return response(responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments)) {
            response
            in
            switch response.result {
            case .Success:
                if let object = ObjectMapper.sharedInstance().objectFromSource(response.result.value, toInstanceOfClass: type) as? T {
                    completionHandler(Response(request: response.request, response: response.response, data: response.data, result: Result.Success(object)))
                } else {
                    let failureReason = "Object could not be serialized from JSON."
                    let userInfo: Dictionary<NSObject, AnyObject> = [NSLocalizedFailureReasonErrorKey: failureReason]
                    let error = NSError(domain: Request.OcMapperErrorDomain, code: ErrorCode.ObjectSerializationFailed.rawValue, userInfo: userInfo)
                    completionHandler(Response(request: response.request, response: response.response, data: response.data, result: Result.Failure(error)))
                }
            case .Failure(let error):
                completionHandler(Response(request: response.request, response: response.response, data: response.data, result: Result.Failure(error)))
            }
        }
    }

}
balazsgerlei commented 7 years ago

Why are you closing this? As I detailed in #59, I only asked the example to be added to the README.