Разработайте простое приложение ios с помощью NSURLSession

Я пишу свое первое приложение target-c для iOS 7.1. Я наверное чего-то принципиально не понимаю, почему у меня этот вопрос возник.

Приложение довольно простое: клиент для связи с внешним сервисом использует веб-API. Для отправки http-запросов я использую NSURLSession. У меня простая архитектура.

THETALETestViewController — это контроллер представления. Этот класс использует THETALEAPI.h THETALEAPI — это реализация веб-API. Я выделил в отдельный класс, чтобы каждый метод внешнего API был реализован в одном месте. Вот формирование запроса для каждого метода. Этот класс использует THETALEHttpHandler.h. THETALEHttpHandler — этот класс напрямую отвечает за отправку http-запроса и получение http-ответа.

Вот код из THETALEHttpHandler.m для отправки POST-запроса.

-(void) sendPostToURL:(NSURL *)url withParams: (NSString *) inParams competion:(void     (^) (NSData *data)) completion
{
    NSLog(@"sendPostToURL");

    // _csrftoken a token in cookie
    NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
    [cookieProperties setObject:@"csrftoken" forKey:NSHTTPCookieName];
    [cookieProperties setObject:_csrftoken forKey:NSHTTPCookieValue];

    [cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];

    NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
    [_httpCookieStorage setCookie:cookie];

    [_sessionConfig setHTTPCookieStorage:_httpCookieStorage];


    // tried and so
    // //   NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil //delegateQueue: [NSOperationQueue mainQueue]];   
    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:_sessionConfig];


    NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:url];

    // _csrftoken a token in the parameter
    NSString *params = [@[inParams, _csrftoken] componentsJoinedByString:@""];

    [urlRequest setHTTPMethod:@"POST"];
    [urlRequest setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]];

    NSURLSessionDataTask * dataTask =[defaultSession dataTaskWithRequest:urlRequest
                                                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                    NSLog(@"Got response %@ with error %@.\n", response, error);
                                                       if(error == nil)
                                                       {
                                                           NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
                                                           NSLog(@"Data = %@",text);
                                                       }
                                                   completion(data);
                                                   }];
    [dataTask resume];

}

Итак, завершение блока выполняется не в основном потоке, а асинхронно, поэтому пока идет запрос и идет прием и обработка ответа - основной поток работает. В принципе все понятно и логично, но я хочу "авторизоваться" с помощью этого метода, соответственно нажав на кнопку "Войти" я должен пройти после успешного запроса и обработки ответа и пользователь должен увидеть ответ. Какими возможностями iOS добиться такого поведения, чтобы основной поток ждал завершения асинхронного блока и отображал соответствующую информацию? А может, и не стоит его ждать, и его просто как-то надо оповестить?

Если, конечно, не делать отдельные классы, а делать все в viewController, то, допустим, подошли бы делегаты, но хочется сделать немного модульности в его применении.

Может я неправильно спроектировал систему, тогда можете пояснить или показать на примере, как это обычно делается?

Я думаю, что это требование довольно банально и ответ где-то рядом, поясните пожалуйста, заранее спасибо!


person njc    schedule 17.04.2014    source источник
comment
Извиняюсь. Перевел комментарии в коде.   -  person njc    schedule 17.04.2014


Ответы (2)


Вы можете использовать AFNetworking для вызовов API.Учебное пособие, подробно объясняющее, как правильно его использовать. Также вы можете использовать NSNotificationCenter для уведомления контроллера представления об успешном/неудачном вызове

person wootage    schedule 17.04.2014
comment
Я уже думал об Уведомлении и делегировании.То есть мне нужно будет сделать внутри блока уведомления для просмотра контроллера? - person njc; 17.04.2014
comment
Я не уверен, что понял ваш вопрос - person wootage; 17.04.2014
comment
Меня интересует реализация. В рамках указанной архитектуры, как бы вы сделали уведомление? - person njc; 17.04.2014
comment
опубликовать уведомление, когда вызов API завершен (оно должно быть внутри блока): [[NSNotificationCenter defaultCenter] postNotificationName: @некоторое имя для идентификации объекта уведомления: nil userInfo:nil]; и всегда удаляйте наблюдателя: [[NSNotificationCenter defaultCenter] removeObserver:self name:@same объект имени уведомления: nil]; зарегистрировать контроллер представления для «прослушивания» уведомления: [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(methodToHandleTheNotification) name:@same объект имени уведомления: nil]; - person wootage; 17.04.2014
comment
Спасибо! Это полезно. Я попробую этот метод. Скажите, пожалуйста, правильная ли архитектура этого приложения? - person njc; 17.04.2014
comment
Примите ответ, если это то, что вам нужно - person wootage; 17.04.2014
comment
Это действительно то, что мне нужно. хотелось бы услышать еще варианты. Это будет возможно, если я соглашусь? - person njc; 17.04.2014

Во-первых, вы можете выполнить блок в основном потоке, используя dispatch_sync. Но проблема не в этом. Вы пытаетесь сделать слишком много в одном блоке кода.

Ваш поток должен выглядеть примерно так:

  1. Вы записали имя пользователя/пароль?
  2. Если нет, попросите один
  3. Запустите сетевой запрос
  4. Обработка ошибок будет включать поиск неверных учетных данных, и в этом случае запрос будет отменен, а ваш код перейдет к шагу 2.
person Stephen Darlington    schedule 17.04.2014
comment
Почему вы думаете, что я хочу в одном блоке сделать слишком много? Я просто хочу принять ответ и дать его дальше в THETALEAPI. Я пытался использовать 'код' dispatch_sync 'код', но программа не дождалась выполнения блока. На самом деле мой вопрос заключается в том, как реализовать то, что вы описали в списке. - person njc; 17.04.2014
comment
@njc Абзац текста под вашим кодом предполагает, что вы хотите попросить пользователя войти в систему во время сетевого запроса, т. е. сетевой запрос должен подождать, пока информация не будет получена от пользователя, прежде чем продолжить. Это не так, как это должно работать. Вам необходимо получить учетные данные, а затем выполнить запрос. - person Stephen Darlington; 17.04.2014
comment
Возможно, что я неправильно объяснил. Учетные данные уже есть, пользователь их вводит. Теперь я хочу дождаться ответа и в зависимости от ответа показать информацию пользовательскому интерфейсу. - person njc; 17.04.2014
comment
Значит, проблема в том, что блок завершения не запускается в основном потоке? - person Stephen Darlington; 17.04.2014
comment
Моя проблема заключалась в том, что я не знал, как сказать мне об интерфейсе, что обрабатывается ответ, но я предложил выше. Я буду использовать или делегирование или уведомление. Это правильно? - person njc; 17.04.2014