Skip to content

Instantly share code, notes, and snippets.

@skunkworks
Last active August 29, 2015 14:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save skunkworks/cc85f7c54390284a9202 to your computer and use it in GitHub Desktop.
Save skunkworks/cc85f7c54390284a9202 to your computer and use it in GitHub Desktop.
Using AFNetworking 2.0 and Mantle for some hot RESTful JSON action
@interface NPClientAPI : AFHTTPSessionManager
- (instancetype)initWithBaseURL:(NSString *)baseURL;
/**
Singleton object.
*/
+ (NPClientAPI *)sharedAPI;
/**
Fetches topics. Array of @c NPTopic objects.
*/
- (NSURLSessionDataTask *)fetchTopicsOnSuccess:(void (^)(NSURLSessionDataTask *task, NSArray *topics))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
@end
@implementation NPClientAPI
NSString *const NPClientAPITopicsIndexURI = @"mobile/topics.json";
#pragma mark - Singleton
+ (NPClientAPI *)sharedAPI
{
static NPClientAPI *sharedClientAPI;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedClientAPI = [[self alloc] initWithBaseURL:[NSURL URLWithString:NP_BASE_URL]];
});
return sharedClientAPI;
}
#pragma mark - Lifecycle
- (instancetype)initWithBaseURL:(NSURL *)baseURL
{
if (self = [super initWithBaseURL:baseURL]) {
// Note: probably not necessary, since each API call will set the request/response serializers to custom subclasses
self.requestSerializer = [AFJSONRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
}
return self;
}
#pragma mark - Public methods
- (NSURLSessionDataTask *)fetchTopicsOnSuccess:(void (^)(NSURLSessionDataTask *task, NSArray *topics))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
// Set up the response serializer to our custom serializer
self.responseSerializer = [NPTopicsSerializer serializer];
NSURL *url = [NSURL URLWithString:NPClientAPITopicsIndexURI relativeToURL:self.baseURL];
NSURLSessionDataTask *task = [self GET:[url absoluteString] parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
if (success) success(task, responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
// Just a random note: it's a good practice to wrap NSError objects to provide more context
// about the type of failure
NSError *wrappedError = [NSError errorWithDomain:NPClientAPIErrorDomain code:NPClientAPIErrorTypeNetworkIssue userInfo:@{ NSUnderlyingErrorKey : error, NSLocalizedDescriptionKey : @"Failed to fetch topics" }];
if (failure) failure(task, wrappedError);
}];
return task;
}
@end
#import <Mantle/Mantle.h>
@interface NPTopic : MTLModel <MTLJSONSerializing>
@property (strong, nonatomic) NSNumber *identifier;
@property (strong, nonatomic) NSString *name;
@end
@implementation NPTopic
#pragma mark - Protocol conformance
#pragma mark MTLJSONSerializing
// This is a Mantle method override. It's a declarative method that maps the NPTopic object's
// property names to their equivalent JSON key name.
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return @{@"identifier" : @"id",
@"name" : @"name"
};
}
@end
#import "AFURLResponseSerialization.h"
@interface NPTopicsSerializer : AFJSONResponseSerializer
@end
#import "NPTopicsSerializer.h"
#import "NPTopic.h"
@implementation NPTopicsSerializer
// Returns array of @c NPTopic objects
- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
return nil;
}
NSDictionary *JSONDictionary = [super responseObjectForResponse:response data:data error:error];
if (!JSONDictionary) return nil;
// Note: the expected JSON format of this response is { data: [ { <a topic> }, { <another topic>} ], metadata: { ...} }
NSArray *JSONTopics = JSONDictionary[@"data"];
NSMutableArray *topics = [NSMutableArray array];
for (NSDictionary *JSONTopic in JSONTopics) {
// For each topic in JSON format, we can deserialize it from JSON to our desired model class using Mantle
NPTopic *topic = [MTLJSONAdapter modelOfClass:[NPTopic class] fromJSONDictionary:JSONTopic error:error];
if (!topic) return nil;
[topics addObject:topic];
}
*error = nil;
return [topics copy];
}
@end
@skunkworks
Copy link
Author

FWIW, if anyone copied this before 10/5, please take a close look at NPTopicsSerializer. I've made a few critical fixes that help it handle errors properly.

@skunkworks
Copy link
Author

FWIW, this gist uses a custom response serializer to deserialize the server response data into an array of NPTopic objects. If your custom AFHTTPSessionManager subclass will use different types of response serializers for different API calls, I highly recommend reading up on AFCompoundResponseSerializer. It prevents a lot of headaches that result from juggling response serializers for different methods -- i.e. calling two different methods back-to-back and having one overwrite the response serializer of the other.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment