AFNetworking HTTPClient подкласс с XMLParser - PullRequest
1 голос
/ 30 ноября 2011

Я пишу небольшое приложение для iOS, которое запрашивает веб-сервис XML REST. Используемая сетевая структура: AFNetworking .

Положение

Чтобы запросить веб-сервис, я подкласс AFHTTPClient:

@interface MyApiClient : AFHTTPClient

и в реализации я делаю это доступным как синглтон:

+ (MyApiClient *)sharedClient {
    static MySharedClient *_sharedClient = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _sharedClient = [[self alloc] initWithBaseUrl:[NSUrl URLWithString:@"http://url.to.the.webservice"]];
    });

    return self;
} 

и в initWithBaseURL я говорю AFNetworking ожидать XML-содержимого:

[self registerHTTPOperationClass:[AFXMLRequestOperation class]];

Теперь я могу вызвать getPatch для синглтона из моего ViewController и в блоке успеха начать синтаксический анализ моего возвращенного XML. В методах NSXMLParserDelegate в ViewController я могу затем выбрать интересующие меня части XML и что-то с ними делать.

Задача

Я хочу, чтобы в моем синглтоне HTTPClient были методы, которые обрабатывают все, что связано с веб-сервисом, и возвращают модели данных или список моделей вместо XML.

Например, я хочу сделать что-то вроде этого:

ServerModel *status = [[MyApiClient sharedClient] getServerStatus];

Затем ApiClient внутренне вызовет веб-службу, проанализирует XML и вернет модель. Как я могу это сделать? Обычно я использовал бы делегат, который вызывается после синтаксического анализа XML, но из-за одноэлементной природы ApiClient может быть несколько делегатов?

Надеюсь, кто-то может пролить свет на это, спасибо!

Ответы [ 2 ]

4 голосов
/ 30 ноября 2011

Использовать блоки вместо делегатов.

Из моего класса ApiClient:

- (void)getPath:(NSString *)path 
     parameters:(NSDictionary *)parameters 
        success:(void (^)(id response))success 
        failure:(void (^)(NSError *error))failure 
{   
    NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters];
    [self enqueueHTTPOperationWithRequest:request success:success failure:failure];
}

-(void)fetchAllUsersSuccess:(void (^)(id))success 
                     failure:(void (^)(NSError *))failure
{
    [self getPath:@"/api/mobile/user/" 
       parameters:nil 
          success:^(id response) {  

                      if([response isKindOfClass:[NSXMLParser class]]){
                          //parse here to new dict
                          success(newDict);
                      } else
                          success(response);

          } failure:^(NSError *error) {
              failure(error);
          }];
}

Теперь я могу использовать его как:

ServiceApiClient *apiClient = [ServiceApiClient sharedClient];

[apiClient fetchAllUsersSuccess:^(id dict) {
    for (NSDictionary *object in [dict objectForKey:@"objects"]) {
        [ServiceUser addUserFromDictionary:object
                                 inContext:self.managedObjectContext];
    }
    NSError *error= nil;
    [self.managedObjectContext save:&error];
    if (error) {
        NSLog(@"%@", error);
    }
} failure:^(NSError * error) {
    NSLog(@"%@", error);
}];
0 голосов
/ 30 ноября 2011

(Заранее извиняюсь за этот «своего рода» ответ, но мы работаем над лучшим решением ...)

Вам нужно сделать шаг назад и тщательно продумать свой дизайн.

У вас проблемы, потому что у вас есть идея, что что-то в вашем дизайне должно быть одноэлементным, но либо:

1) это на самом деле не нужно,

2) может уже существовать что-то, что выполняет эту работу за вас (например, HTTP-библиотека, которую вы используете),

или

3) Вы делаете неправильную вещь одиночной, или вы не разбили свой дизайн на соответствующие части, чтобы хорошо работать с единственной идеей

Итак, вы можете сказать мне, почемувы собираетесь на одиночный подход?Это просто для того, чтобы гарантировать, что только один сетевой запрос может произойти одновременно?Есть ли в вашем одноэлементном объекте понятие состояния?Затем я обновлю этот ответ или комментарий и т. Д.

( Отступление: Я также добавил бы, что в некоторых случаях может возникнуть реальная потребность в ' strong 'singleton - под этим я подразумеваю, что на самом деле существует только один возможный экземпляр, и этот механизм встроен прямо в ваш объект, как вы делаете, но это не так. Альтернатива -' слабый 'singleton, под которым я подразумеваю ваш основной объект, который фактически выполняет работу, как обычно, имеет простой метод init, но общий доступ к общему объекту осуществляется через другой объект, который является своего рода простой «фабрикой», которая создает экземпляры/ содержит общий экземпляр. Преимущество этой слабой единой идеи состоит в том, что ваш код более пригоден для повторного использования в различных контекстах - например, вы можете решить сделать несколько HTTP-запросов / сеансов одновременно в более позднее время - и это иногда делает написание тестов менеепроблематичный).

...