GET request using AFNetworking and saving response

2019-06-07 21:56发布

I am doing a simple GET request with AFNetworking

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:@"http://someapi.com/hello.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

Once I have made the request I want to be able to access the responseObject from any other method in the class.

I want to be able to save the responseObject so I can do something like display the output in a tableview.

3条回答
beautiful°
2楼-- · 2019-06-07 22:33

Just create a property:

@property(nonatomic, strong) id savedResponseObject;

and set it in the success handler of the request:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

[manager GET:@"http://someapi.com/hello.json" 
  parameters:nil 
     success:^(AFHTTPRequestOperation *operation, id responseObject)
{
    self.savedResponseObject = responseObject;
} 
    failure:^(AFHTTPRequestOperation *operation, NSError *error) 
{
    NSLog(@"Error: %@", error);
}];

Then you will be able to access it from other places in your class by referencing:

self.savedResponseObject
查看更多
forever°为你锁心
3楼-- · 2019-06-07 22:34

It's common to creat object models that will be represented by JSON. When you get the response you would then parse the data into the models. The approach we use is to return the response to the requester through a completion block. You don't have to parse the JSON into strongly typed objects, but it really is helpful long term. It's probably a good idea to farm out the network request operations into a separate class (called a service) as well. This way you can instantiate a new service and get notified through a completion block that it is finished. For example your service's request signature could look like this:

typedef void(^HelloWorldCompletionHandler)(NSString *helloWorld, NSError *error);


- (void)requestHelloWorldData:(HelloWorldCompletionHandler)completionHandler;

// implementation
- (void)requestHelloWorldData:(HelloWorldCompletionHandler)completionHandler {
   AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
   [manager GET:@"http://someapi.com/hello.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

    id JSONResponse = [operation responseObject];
    if (operation.error) {
        completionHandler(nil, error);
    } else {
        // parse the response to something
        id parserResult = [self parseJSONResponse:JSONResponse];
        completionHandler(parserResult, nil);
    }
 }];

This way you'll know when the network request is complete, and you can set the data you want on a property within your class. Then you could call tableView.reloadData in order to use the data in your table.

All that code would go into a service type class. I like to organize my services by responsibility. I don't know how many different data calls you make, but we have several for our project. If for instance you were making a weather app you could potentially organize by Current Conditions, Daily Forecasts, and Hourly Forecasts. I would make a service for each one of these requests. Say I created a CurrentConditionsService. The header would look something like this:

typedef void(^CurrentConditionsCompletionHandler)(CurrentConditions *currentConditions, NSError *error);

@interface CurrentConditionsService : NSObject

// locationKey is some unique identifier for a city
+ (instancetype)serviceWithLocationKey:(NSString *)locationKey;

- (void)retrieveCurrentConditionsWithCompletionHandler:(CurrentConditionsCompletionHandler)completionHandler;

@end

Then in my implementation file I would make the request and invoke the given completion handler like I demonstrated above. This pattern can be followed by many different services to the point where all your services could inherit from a base class that handles the request/response portions. Then your subclasses could override specific methods and handle/parse the data appropriately based on type.

If you go the route of parsing the JSON responses into model objects, all your parsers will need to conform to a protocol. This way in your super class it doesn't matter what the concrete implementation of your parser is. You supply the super class with a concrete implementation and all it knows how to do is invoke the parser and return the response.

An example JSON parser protocol would look like this:

@protocol AWDataParser <NSObject>

@required
- (id)parseFromDictionary:(NSDictionary *)dictionary;
- (NSArray *)parseFromArray:(NSArray *)array;

@end

And invoking it in your services super class:

- (id)parseJSONResponse:(id)JSONResponse error:(NSError **)error {

    NSAssert(self.expectedJSONResponseClass != nil, @"parseResponse: expectedJSONResponseClass cannot be nil");
    NSAssert(self.parser != nil, @"parseResponse: parser cannot be nil");

    id parserResult = nil;
    if (![JSONResponse isKindOfClass:self.expectedJSONResponseClass]) {
        //handle invalid JSON reponse object
        if (error) {
            *error = [NSError errorWithDomain:NetworkServiceErrorDomain code:kNetworkServiceErrorParsingFailure userInfo:@{@"Invalid JSON type": [NSString stringWithFormat:@"expected: %@, is: %@",self.expectedJSONResponseClass,  [JSONResponse class]]}];
        }
    } else {
        if (self.expectedJSONResponseClass == [NSArray class]) {
            parserResult = [self.parser parseFromArray:JSONResponse];
        }else {
            parserResult = [self.parser parseFromDictionary:JSONResponse];
        }
        if (!parserResult) {
            if (error) {
                *error = [NSError errorWithDomain:NetworkServiceErrorDomain code:kNetworkServiceErrorParsingFailure userInfo:nil];
            }
        }
    }

    return parserResult;
}
查看更多
Explosion°爆炸
4楼-- · 2019-06-07 22:40

Use this approach:

NSURL *COMBINAT = [[NSURL alloc] initWithString:@"http://someapi.com/hello.json"];
dispatch_async(kBgQueue, ^{
        NSData* data = [NSData dataWithContentsOfURL:
                        COMBINAT];
        [self performSelectorOnMainThread:@selector(savedata:)   withObject:data waitUntilDone:YES];
    });

then simply call:

- (void)savedata:(NSData *)responseData {
    NSError* error;
    NSLog(@"Answer from server %@", responseData);
    // ... your code to use responseData
}
查看更多
登录 后发表回答