Миграция AFNetworking 1.x на 3.x для NSURLSessionTask?

У меня есть старый проект, который не работает в iOS 9. Я прочитал официальную документацию по AFNetworking и выполнил большую часть миграции.

Сетевой менеджер:

_requestManager = [[AFHTTPSessionManager alloc]initWithBaseURL:[NSURL URLWithString:baseURL ]];
//here we can set the request header as the access token once we have logged in.
AFHTTPRequestSerializer *requestSerializer = [AFHTTPRequestSerializer serializer];
AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
[_requestManager setRequestSerializer:requestSerializer];
[_requestManager setResponseSerializer:responseSerializer];

Более ранняя версия:

// 2. Create an `NSMutableURLRequest`.
NSMutableURLRequest *request =
    [[NetworkManager sharedInstance].requestManager.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:fullPath
                                                                                          parameters:nil
                                                                           constructingBodyWithBlock: ^(id <AFMultipartFormData> formData) {
    NSString *fileName = [NSString stringWithFormat:@"%@.caf", theAudioItem.media_item_name];
    [formData appendPartWithFileData:audioData name:theAudioItem.media_item_name fileName:fileName mimeType:@"audio/caf"];
}];

// 3. Create and use `AFHTTPRequestOperationManager` to create an `AFHTTPRequestOperation` from the `NSMutableURLRequest` that we just created.
AFHTTPRequestOperation *operation =
    [[NetworkManager sharedInstance].requestManager HTTPRequestOperationWithRequest:request
                                                                            success: ^(AFHTTPRequestOperation *operation, id responseObject) {
    result(YES, @"");
} failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
    if (operation.responseData) {
        NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:operation.responseData options:NSJSONReadingMutableContainers error:nil];

        result(NO, [responseDict valueForKey:@"Message"]);
        [self deleteTmpFilesFromParts:formParts];
    } else {
        result(NO, [NSString stringWithFormat:@"Failed to upload media to %@!", gallery.gallery_name]);
        [self deleteTmpFilesFromParts:formParts];
    }


    result(NO, errorMessage);
}];

// 4. Set the progress block of the operation.
[operation setUploadProgressBlock: ^(NSUInteger __unused bytesWritten,
                                     long long totalBytesWritten,
                                     long long totalBytesExpectedToWrite) {
    DLog(@"progress is %i %lld %lld", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    progress((float)totalBytesWritten / (float)totalBytesExpectedToWrite);
}];

// 5. Begin!
[operation start];

Преобразованный ответ: (обновлен комментариями, что дает ошибки)

//No visible @interface for 'AFHTTPRequestSerializer<AFURLRequestSerialization>' declares the selector 'multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:'
    NSMutableURLRequest *request =
        [[NetworkManager sharedInstance].requestManager.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:fullPath parameters:nil
                                                                               constructingBodyWithBlock: ^(id <AFMultipartFormData> formData) {
        NSString *fileName = [NSString stringWithFormat:@"%@.caf", theAudioItem.media_item_name];
        [formData appendPartWithFileData:audioData name:theAudioItem.media_item_name fileName:fileName mimeType:@"audio/caf"];
    }];

    // 3. Create and use `AFHTTPRequestOperationManager` to create an `AFHTTPRequestOperation` from the `NSMutableURLRequest` that we just created.
   //No visible @interface for 'AFHTTPSessionManager' declares the selector 'HTTPRequestOperationWithRequest:success:failure:'
    NSURLSessionTask *operation =
        [[NetworkManager sharedInstance].requestManager HTTPRequestOperationWithRequest:request
                                                                                success: ^(NSURLSessionTask *operation, id responseObject) {
        result(YES, @"");
    } failure: ^(NSURLSessionTask *operation, NSError *error) {

        //Error for operation doesn't have responseData
        if (operation.responseData) {
            NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:operation.responseData options:NSJSONReadingMutableContainers error:nil];

            result(NO, [responseDict valueForKey:@"Message"]);
            [self deleteTmpFilesFromParts:formParts];
        } else {
            result(NO, [NSString stringWithFormat:@"Failed to upload media to %@!", gallery.gallery_name]);
            [self deleteTmpFilesFromParts:formParts];
        }

        // get the response
        result(NO, errorMessage);
    }];

    // 4. Set the progress block of the operation.
    //No visible @interface for 'NSURLSessionTask' declares the selector 'setUploadProgressBlock:'
    [operation setUploadProgressBlock: ^(NSUInteger __unused bytesWritten,
                                         long long totalBytesWritten,
                                         long long totalBytesExpectedToWrite) {
        DLog(@"progress is %i %lld %lld", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
        progress((float)totalBytesWritten / (float)totalBytesExpectedToWrite);
    }];

    // 5. Begin!
    //No visible @interface for 'NSURLSessionTask' declares the selector 'start'
    [operation start];

person happycoder    schedule 11.02.2016    source источник


Ответы (1)


Эквивалентный код для AFHTTPSessionManager просто:

NSURLSessionTask *task = [[[NetworkManager sharedInstance] requestManager] POST:fullPath parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
    NSString *fileName = [NSString stringWithFormat:@"%@.caf", theAudioItem.media_item_name];
    [formData appendPartWithFileData:audioData name:theAudioItem.media_item_name fileName:fileName mimeType:@"audio/caf"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
    NSLog(@"%.3f", uploadProgress.fractionCompleted);
} success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
    // success
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    // failure

    // if you need to process the `NSData` associated with this error (if any), you'd do:

    NSData *data = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
    if (data) { ... }
}];

Это имеет два основных отличия от вашего кода версии 1.x: во-первых, в версии 2.x они представили GET/POST/и т. д. методы, которые избавили вас от ручного запуска операции (или добавления ее в собственную очередь). Во-вторых, в 3.x они отказались от NSOperation фреймворка AFHTTPRequestOperationManager и теперь используют класс AFHTTPSessionManager, где GET/POST/и т. д. не возвращайте подкласс NSOperation, а скорее ссылку NSURLSessionTask.

person Rob    schedule 11.02.2016
comment
Большое спасибо, Роб, но как я могу прочитать данные ответа об ошибке в NSURLSessionDataTask? - person happycoder; 11.02.2016
comment
В случае неудачи будет вызван код в блоке failure. - person Rob; 11.02.2016
comment
Я имею в виду, что в NSURLSessionDataTask нет свойства task.responseData. Разве я не могу преобразовать его в словарь и прочитать данные, используя определенный ключ, отправленный веб-сервисом? (образец находится в моем фрагменте кода) - person happycoder; 12.02.2016
comment
Ключ AFNetworkingOperationFailingURLResponseDataErrorKey имеет NSData, связанный с ответом (если есть) в блоке failure. Если вам по какой-либо причине нужен NSHTTPURLResponse, это под AFNetworkingOperationFailingURLResponseErrorKey. Смотрите пересмотренный ответ. - person Rob; 13.02.2016