Запрос GET с использованием AFNetworking и сохранением ответа

Я делаю простой запрос GET с 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);
}];

После того, как я сделал запрос, я хочу иметь доступ к responseObject из любого другого метода в классе.

Я хочу иметь возможность сохранить объект ответа, чтобы я мог сделать что-то вроде отображения вывода в виде таблицы.


person joshuahornby10    schedule 23.02.2014    source источник
comment
AFNetworking вызывает весь блок обратного вызова в основной очереди. Таким образом, проблема не в том, что AFNetworking является асинхронным.   -  person Sandeep    schedule 24.02.2014


Ответы (3)


Обычно создаются объектные модели, которые будут представлены в формате JSON. Когда вы получите ответ, вы затем разберете данные в модели. Подход, который мы используем, заключается в том, чтобы вернуть ответ запрашивающей стороне через блок завершения. Вам не нужно разбирать JSON на строго типизированные объекты, но это действительно полезно в долгосрочной перспективе. Вероятно, было бы неплохо также выделить операции сетевых запросов в отдельный класс (называемый службой). Таким образом, вы можете создать экземпляр новой службы и получить уведомление через блок завершения о том, что она завершена. Например, подпись запроса вашего сервиса может выглядеть так:

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);
    }
 }];

Таким образом, вы будете знать, когда сетевой запрос будет завершен, и вы сможете установить нужные данные для свойства в вашем классе. Затем вы можете вызвать tableView.reloadData, чтобы использовать данные в своей таблице.

Весь этот код попадет в класс типа службы. Мне нравится организовывать свои услуги по принципу ответственности. Я не знаю, сколько различных вызовов данных вы делаете, но у нас есть несколько для нашего проекта. Если, например, вы делаете приложение о погоде, вы потенциально можете организовать его по Текущим условиям, Ежедневным прогнозам и Почасовым прогнозам. Я бы сделал услугу для каждого из этих запросов. Скажем, я создал CurrentConditionsService. Заголовок будет выглядеть примерно так:

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

Затем в моем файле реализации я бы сделал запрос и вызвал данный обработчик завершения, как показано выше. За этим шаблоном может следовать множество различных сервисов до такой степени, что все ваши сервисы могут наследоваться от базового класса, который обрабатывает части запроса/ответа. Затем ваши подклассы могут переопределять определенные методы и соответствующим образом обрабатывать/анализировать данные в зависимости от типа.

Если вы пойдете по пути разбора ответов JSON в объекты модели, все ваши парсеры должны будут соответствовать протоколу. Таким образом, в вашем суперклассе не имеет значения, какова конкретная реализация вашего парсера. Вы предоставляете суперклассу конкретную реализацию, и все, что он умеет делать, — это вызывать синтаксический анализатор и возвращать ответ.

Пример протокола парсера JSON будет выглядеть так:

@protocol AWDataParser <NSObject>

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

@end

И вызывая его в суперклассе ваших услуг:

- (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;
}
person jervine10    schedule 23.02.2014
comment
Это кажется интересным, куда идет приведенный выше код? У вас есть еще пример такого метода? - person joshuahornby10; 24.02.2014
comment
Я обновил ответ для вас, чтобы включить немного больше информации. - person jervine10; 24.02.2014

Используйте этот подход:

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];
    });

затем просто позвоните:

- (void)savedata:(NSData *)responseData {
    NSError* error;
    NSLog(@"Answer from server %@", responseData);
    // ... your code to use responseData
}
person ares777    schedule 23.02.2014
comment
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //1 - person ares777; 24.02.2014
comment
Это возвращает массу чисел с сервера, например: - person joshuahornby10; 24.02.2014
comment
Это нормально, вам нужно сериализовать данные в формате json (не забудьте добавить JSONKit и #import JSONKit.h) NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error]; // получить значение ключа в json // NSString *REZULTATMEMO =[json objectForKey:@idmemo]; // оценить это if (![REZULTATMEMO isEqualToString:@-1] && [raspunsMEMO isEqualToString:@success]) { NSLog(@good data %@, REZULTATMEMO); - person ares777; 24.02.2014
comment
Великолепно! Если вы обновите ответ, указав, как сериализовать данные, я отмечу ответ. - person joshuahornby10; 24.02.2014
comment
Последний вопрос: если я сохраню NSDictionary как @property, можно ли вызвать это любым другим методом? например, не нужно декальировать пустоту. - person joshuahornby10; 24.02.2014
comment
Обновленный ответ. Вы просто конвертируете responseData в NSDictionary, а затем используете любые ключи, которые вам нужно оценить. Имейте в виду, чтобы проверить, не является ли responseData нулевым: if(responseData) { NSDictionary* json = [NSJSONSerialization JSONObjectWithData: optionsresponseData options:kNilOptions error:&error]; - person ares777; 24.02.2014
comment
Можно прочитать уже отвеченный вопрос здесь stackoverflow.com/questions/6754686/ - person ares777; 24.02.2014

Просто создайте свойство:

@property(nonatomic, strong) id savedResponseObject;

и установите его в обработчике успеха запроса:

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);
}];

Затем вы сможете получить к нему доступ из других мест в вашем классе, сославшись на:

self.savedResponseObject
person Rafa de King    schedule 23.02.2014
comment
Что именно возвращает null? - person Rafa de King; 24.02.2014
comment
NSLog(@%@, self.savedResponseObject); Запрос вызывается до инициализации свойства - person joshuahornby10; 24.02.2014