Операция MKNetworkKit не возобновляется / не завершается при изменении достижимости

Я новичок в MKNetworkKit, но я смог добавить его в свой проект, и он отлично работает, за исключением случаев, когда речь идет об изменениях достижимости.

Вот ситуация:

  1. Я отключаю WiFi и запускаю приложение.
  2. Даже без доступности я прошу (используя POST) некоторые данные, создав MKNetworkOperation от моего MKNetworkEngine подкласс. Непосредственно перед запросом данных операция устанавливается как блокируемая (согласно документу Мугунта Кумара).
  3. После включения WiFi, checkAndRestoreFrozenOperations в MKNetworkEngine вызывается и обнаруживает, что существует одна ожидающая операция (созданная без достижимости), которая пытается поставить в очередь.
  4. После этого мой onCompletion Блок никогда не называется.

Есть ли что-то, чего я не понимаю в операциях замораживания + достижимости в MKNetworkKit? Замораживание работает только для операций, в которых достижимость изменяется после запуска запроса? Или я должен реализовать свой собственный измененный блок достижимости?

Вот код в моем MKNetworkEngine подкласс, который создает операцию и запускает запрос. Обратите внимание, что нерелевантный код был подавлен.

NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObject:@"value" forKey:@"param"];
MKNetworkOperation *op = [self operationWithPath:MYPATH
                                          params:params
                                      httpMethod:@"POST"];
[op setFreezable:YES];

[op onCompletion:^(MKNetworkOperation *completedOperation) {
   // ...
   // Here is where I process response and send the result to my completion block
   // It's called when WiFi is available, but not called otherwise.
   // ...
} onError:^(NSError *error) {
   // It's called when WiFi is available, but not called otherwise.
    DLog(@"Some error");
}];

[self enqueueOperation:op];

return op;

1 ответ

Решение

Это две отдельные проблемы: резюме против завершения.

  1. резюме: механизм Freeze/Unfreeze работает, только если кеш включен

    Вы должны вызвать -useCache в вашем AppDelegate -didFinishLaunchingWithOptions:

    self.networkEngine = [[MKNetworkEngine alloc] init ...];
    [self.networkEngine useCache]; // <- Add this line
    
  2. complete: обратный вызов завершения не вызывается при изменении состояния сети (т. е. после разморозки)

    Тем не менее, если вы выполните действие (1.) и поместите точку останова в MKNetworkOperation.m -checkAndRestoreFrozenOperations в строке:

    [self enqueueOperation:pendingOperation]
    

    вы обнаружите, что он вызывается при восстановлении сетевого подключения, и что pendingOperation - это ожидающий POST. Однако, поскольку был создан новый MKNetworkOperation (и к тому времени блок завершения может больше не существовать), ваш onCompletion Блок никогда не называется. Одним из возможных путей решения этой проблемы является использование уведомления вместо обратного вызова.

  3. полное исправление: более надежный подход, чем (2), который будет работать при запуске, состоит в замене ^{} блокировать обратные вызовы с помощью NSNotifications. Зарегистрируйте своих слушателей рано, как в вашем AppDelegate. Вот минимальные изменения, необходимые для создания уведомлений MKNetworkKit:

    3a. Вставить константы уведомлений в MKNetworkOperation.h

    #define MKNetworkOperationCompletionNotification @"MKNetworkOperationCompletionNotification"
    #define MKNetworkOperationErrorNotification @"MKNetworkOperationErrorNotification"
    

    3b. Передайте уведомление об успешном выполнении в MKNetworkOperation.m -operationSucceeded (обратите внимание, что я использую postNotificationOnMainThread, чтобы указанное уведомление можно было прослушать из основного потока и изменить пользовательский интерфейс; см. NSOperation и NSNotificationCenter в основном потоке):

    -(void) operationSucceeded {
        NSDictionary * aUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
            self, NSStringFromClass([MKNetworkOperation class]),
            nil];
        NSNotification * notification = [NSNotification notificationWithName:MKNetworkOperationCompletionNotification
            object:nil
            userInfo:aUserInfo];
        [[NSNotificationCenter defaultCenter] postNotificationOnMainThread:notification];
        ...
    

    3в. Трансляция уведомления об ошибке в MKNetworkOperation.m -operationFailedWithError

    -(void) operationFailedWithError:(NSError*) error {
        self.error = error;
        NSDictionary * aUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
            self,   NSStringFromClass([MKNetworkOperation class]),
            error,  NSStringFromClass([NSError class]),
            nil];
        NSNotification * notification = [NSNotification notificationWithName:MKNetworkOperationErrorNotification
            object:nil
            userInfo:aUserInfo];
    
        [[NSNotificationCenter defaultCenter] postNotificationOnMainThread:notification];
        ...
    

    3d. Зарегистрируйте довольно постоянный объект, такой как AppDelegate, в качестве прослушивателя (не забудьте отменить регистрацию):

        // Listen to POST changes
        NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
        [defaultCenter addObserver:self
            selector:@selector(mkNetworkOperationCompletionNotification:)
            name:MKNetworkOperationCompletionNotification
            object:nil];
        [defaultCenter addObserver:self
            selector:@selector(mkNetworkOperationErrorNotification:)
            name:MKNetworkOperationErrorNotification
            object:nil];
    

    3е. Пример кода того, как может выглядеть слушатель:

    - (void)mkNetworkOperationCompletionNotification:(NSNotification*)notification {
        MKNetworkOperation *operation = [[notification userInfo]
            objectForKey:NSStringFromClass([MKNetworkOperation class])];
        NSLog(@"operationSucceeded: %@", [operation responseString]);
    }
    
    - (void)mkNetworkOperationErrorNotification:(NSNotification*)notification {
        NSError * error = [[notification userInfo] objectForKey:NSStringFromClass([NSError class])];
        NSLog(@"operationFailedWithError: %@", [error localizedDescription]);
    }
    

Сделай это, все готово. ИКС.

(Отредактировано, чтобы удалить ненужные предложенные изменения в MKNetworkOperation.h в предыдущем ответе, и добавило параграф 3., чтобы показать, как преодолеть ограничения ^{}.)

Другие вопросы по тегам