Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alamofire extension for Alamofire 3.x #53

Closed
balazsgerlei opened this issue Jun 21, 2016 · 8 comments
Closed

Alamofire extension for Alamofire 3.x #53

balazsgerlei opened this issue Jun 21, 2016 · 8 comments

Comments

@balazsgerlei
Copy link

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
Copy link
Author

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
Copy link
Owner

aryaxt commented Jun 21, 2016

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
Copy link
Author

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
Copy link
Author

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
Copy link
Owner

aryaxt commented Jun 23, 2016

right for errors you want to use Failure

public enum Result<Value, Error: ErrorType> {
    case Success(Value)
    case Failure(Error)

@aryaxt
Copy link
Owner

aryaxt commented Jun 23, 2016

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
Copy link
Author

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
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants