Browse Source

Merge pull request #3187 from AFNetworking/feature/progress_improvement

New Progress Reporting API using `NSProgress`
Kevin Harwood 9 years ago
parent
commit
3106e9d06f

+ 61 - 7
AFNetworking/AFHTTPSessionManager.h

@@ -142,7 +142,25 @@ NS_ASSUME_NONNULL_BEGIN
 - (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
 - (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                    parameters:(nullable id)parameters
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                       success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
-                      failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;
+                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
+
+
+/**
+ Creates and runs an `NSURLSessionDataTask` with a `GET` request.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param progress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
+                            parameters:(nullable id)parameters
+                              progress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
+                               success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+                               failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
 
 
 /**
 /**
  Creates and runs an `NSURLSessionDataTask` with a `HEAD` request.
  Creates and runs an `NSURLSessionDataTask` with a `HEAD` request.
@@ -157,7 +175,7 @@ NS_ASSUME_NONNULL_BEGIN
 - (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
 - (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
                     parameters:(nullable id)parameters
                     parameters:(nullable id)parameters
                        success:(nullable void (^)(NSURLSessionDataTask *task))success
                        success:(nullable void (^)(NSURLSessionDataTask *task))success
-                       failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;
+                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
 
 
 /**
 /**
  Creates and runs an `NSURLSessionDataTask` with a `POST` request.
  Creates and runs an `NSURLSessionDataTask` with a `POST` request.
@@ -172,7 +190,24 @@ NS_ASSUME_NONNULL_BEGIN
 - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
 - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                     parameters:(nullable id)parameters
                     parameters:(nullable id)parameters
                        success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                        success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
-                       failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;
+                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
+
+/**
+ Creates and runs an `NSURLSessionDataTask` with a `POST` request.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param progress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
+                             parameters:(nullable id)parameters
+                               progress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
+                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
 
 
 /**
 /**
  Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
  Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
@@ -189,7 +224,26 @@ NS_ASSUME_NONNULL_BEGIN
                     parameters:(nullable id)parameters
                     parameters:(nullable id)parameters
      constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
      constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                        success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                        success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
-                       failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;
+                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
+
+/**
+ Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
+ @param progress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
+                             parameters:(nullable id)parameters
+              constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
+                               progress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
+                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
 
 
 /**
 /**
  Creates and runs an `NSURLSessionDataTask` with a `PUT` request.
  Creates and runs an `NSURLSessionDataTask` with a `PUT` request.
@@ -204,7 +258,7 @@ NS_ASSUME_NONNULL_BEGIN
 - (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
 - (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
                    parameters:(nullable id)parameters
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                       success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
-                      failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;
+                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
 
 
 /**
 /**
  Creates and runs an `NSURLSessionDataTask` with a `PATCH` request.
  Creates and runs an `NSURLSessionDataTask` with a `PATCH` request.
@@ -219,7 +273,7 @@ NS_ASSUME_NONNULL_BEGIN
 - (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
 - (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
                      parameters:(nullable id)parameters
                      parameters:(nullable id)parameters
                         success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                         success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
-                        failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;
+                        failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
 
 
 /**
 /**
  Creates and runs an `NSURLSessionDataTask` with a `DELETE` request.
  Creates and runs an `NSURLSessionDataTask` with a `DELETE` request.
@@ -234,7 +288,7 @@ NS_ASSUME_NONNULL_BEGIN
 - (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
 - (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
                       parameters:(nullable id)parameters
                       parameters:(nullable id)parameters
                          success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                          success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
-                         failure:(nullable void (^)(NSURLSessionDataTask * __nullable task, NSError *error))failure;
+                         failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
 
 
 @end
 @end
 
 

+ 49 - 8
AFNetworking/AFHTTPSessionManager.m

@@ -105,7 +105,24 @@
                       success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                       success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                       failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
                       failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
 {
 {
-    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure];
+
+    return [self GET:URLString parameters:parameters progress:nil success:success failure:failure];
+}
+
+- (NSURLSessionDataTask *)GET:(NSString *)URLString
+                   parameters:(id)parameters
+                     progress:(void (^)(NSProgress * _Nonnull))downloadProgress
+                      success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
+                      failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
+{
+
+    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
+                                                        URLString:URLString
+                                                       parameters:parameters
+                                                   uploadProgress:nil
+                                                 downloadProgress:downloadProgress
+                                                          success:success
+                                                          failure:failure];
 
 
     [dataTask resume];
     [dataTask resume];
 
 
@@ -117,7 +134,7 @@
                        success:(void (^)(NSURLSessionDataTask *task))success
                        success:(void (^)(NSURLSessionDataTask *task))success
                        failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
                        failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
 {
 {
-    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters success:^(NSURLSessionDataTask *task, __unused id responseObject) {
+    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:^(NSURLSessionDataTask *task, __unused id responseObject) {
         if (success) {
         if (success) {
             success(task);
             success(task);
         }
         }
@@ -133,16 +150,35 @@
                        success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                        success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                        failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
                        failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
 {
 {
-    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure];
+    return [self POST:URLString parameters:parameters progress:nil success:success failure:failure];
+}
+
+- (NSURLSessionDataTask *)POST:(NSString *)URLString
+                    parameters:(id)parameters
+                      progress:(void (^)(NSProgress * _Nonnull))uploadProgress
+                       success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
+                       failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
+{
+    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
 
 
     [dataTask resume];
     [dataTask resume];
 
 
     return dataTask;
     return dataTask;
 }
 }
 
 
+- (NSURLSessionDataTask *)POST:(NSString *)URLString
+                    parameters:(nullable id)parameters
+     constructingBodyWithBlock:(nullable void (^)(id<AFMultipartFormData> _Nonnull))block
+                       success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
+                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
+{
+    return [self POST:URLString parameters:parameters constructingBodyWithBlock:block success:success failure:failure];
+}
+
 - (NSURLSessionDataTask *)POST:(NSString *)URLString
 - (NSURLSessionDataTask *)POST:(NSString *)URLString
                     parameters:(id)parameters
                     parameters:(id)parameters
      constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
      constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
+                      progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
                        success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                        success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                        failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
                        failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
 {
 {
@@ -161,7 +197,7 @@
         return nil;
         return nil;
     }
     }
 
 
-    __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
+    __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
         if (error) {
         if (error) {
             if (failure) {
             if (failure) {
                 failure(task, error);
                 failure(task, error);
@@ -183,7 +219,7 @@
                       success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                       success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                       failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
                       failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
 {
 {
-    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters success:success failure:failure];
+    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure];
 
 
     [dataTask resume];
     [dataTask resume];
 
 
@@ -195,7 +231,7 @@
                         success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                         success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                         failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
                         failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
 {
 {
-    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters success:success failure:failure];
+    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure];
 
 
     [dataTask resume];
     [dataTask resume];
 
 
@@ -207,7 +243,7 @@
                          success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                          success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                          failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
                          failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
 {
 {
-    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters success:success failure:failure];
+    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure];
 
 
     [dataTask resume];
     [dataTask resume];
 
 
@@ -217,6 +253,8 @@
 - (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
 - (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                        URLString:(NSString *)URLString
                                        URLString:(NSString *)URLString
                                       parameters:(id)parameters
                                       parameters:(id)parameters
+                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
+                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                          success:(void (^)(NSURLSessionDataTask *, id))success
                                          success:(void (^)(NSURLSessionDataTask *, id))success
                                          failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
                                          failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
 {
 {
@@ -236,7 +274,10 @@
     }
     }
 
 
     __block NSURLSessionDataTask *dataTask = nil;
     __block NSURLSessionDataTask *dataTask = nil;
-    dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
+    dataTask = [self dataTaskWithRequest:request
+                          uploadProgress:uploadProgress
+                        downloadProgress:downloadProgress
+                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
         if (error) {
         if (error) {
             if (failure) {
             if (failure) {
                 failure(dataTask, error);
                 failure(dataTask, error);

+ 27 - 23
AFNetworking/AFURLSessionManager.h

@@ -209,6 +209,19 @@ NS_ASSUME_NONNULL_BEGIN
 - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
 - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                             completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler;
                             completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler;
 
 
+/**
+ Creates an `NSURLSessionDataTask` with the specified request.
+
+ @param request The HTTP request for the request.
+ @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
+ @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
+ */
+- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
+                               uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
+                             downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
+                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler;
+
 ///---------------------------
 ///---------------------------
 /// @name Running Upload Tasks
 /// @name Running Upload Tasks
 ///---------------------------
 ///---------------------------
@@ -218,14 +231,14 @@ NS_ASSUME_NONNULL_BEGIN
 
 
  @param request The HTTP request for the request.
  @param request The HTTP request for the request.
  @param fileURL A URL to the local file to be uploaded.
  @param fileURL A URL to the local file to be uploaded.
- @param progress A progress object monitoring the current upload progress.
+ @param progress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
  @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
  @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
 
 
  @see `attemptsToRecreateUploadTasksForBackgroundSessions`
  @see `attemptsToRecreateUploadTasksForBackgroundSessions`
  */
  */
 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                          fromFile:(NSURL *)fileURL
                                          fromFile:(NSURL *)fileURL
-                                         progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
+                                         progress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                 completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError  * _Nullable error))completionHandler;
                                 completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError  * _Nullable error))completionHandler;
 
 
 /**
 /**
@@ -233,23 +246,23 @@ NS_ASSUME_NONNULL_BEGIN
 
 
  @param request The HTTP request for the request.
  @param request The HTTP request for the request.
  @param bodyData A data object containing the HTTP body to be uploaded.
  @param bodyData A data object containing the HTTP body to be uploaded.
- @param progress A progress object monitoring the current upload progress.
+ @param progress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
  @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
  @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
  */
  */
 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                          fromData:(nullable NSData *)bodyData
                                          fromData:(nullable NSData *)bodyData
-                                         progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
+                                         progress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                 completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
                                 completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
 
 
 /**
 /**
  Creates an `NSURLSessionUploadTask` with the specified streaming request.
  Creates an `NSURLSessionUploadTask` with the specified streaming request.
 
 
  @param request The HTTP request for the request.
  @param request The HTTP request for the request.
- @param progress A progress object monitoring the current upload progress.
+ @param progress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
  @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
  @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
  */
  */
 - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
 - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
-                                                 progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
+                                                 progress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                         completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
                                         completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
 
 
 ///-----------------------------
 ///-----------------------------
@@ -260,14 +273,14 @@ NS_ASSUME_NONNULL_BEGIN
  Creates an `NSURLSessionDownloadTask` with the specified request.
  Creates an `NSURLSessionDownloadTask` with the specified request.
 
 
  @param request The HTTP request for the request.
  @param request The HTTP request for the request.
- @param progress A progress object monitoring the current download progress.
+ @param progress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
  @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL.
  @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL.
  @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any.
  @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any.
 
 
  @warning If using a background `NSURLSessionConfiguration` on iOS, these blocks will be lost when the app is terminated. Background sessions may prefer to use `-setDownloadTaskDidFinishDownloadingBlock:` to specify the URL for saving the downloaded file, rather than the destination block of this method.
  @warning If using a background `NSURLSessionConfiguration` on iOS, these blocks will be lost when the app is terminated. Background sessions may prefer to use `-setDownloadTaskDidFinishDownloadingBlock:` to specify the URL for saving the downloaded file, rather than the destination block of this method.
  */
  */
 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
-                                             progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
+                                             progress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                           destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                           destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                     completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
                                     completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
 
 
@@ -275,12 +288,12 @@ NS_ASSUME_NONNULL_BEGIN
  Creates an `NSURLSessionDownloadTask` with the specified resume data.
  Creates an `NSURLSessionDownloadTask` with the specified resume data.
 
 
  @param resumeData The data used to resume downloading.
  @param resumeData The data used to resume downloading.
- @param progress A progress object monitoring the current download progress.
+ @param progress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
  @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL.
  @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL.
  @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any.
  @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any.
  */
  */
 - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
 - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
-                                                progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
+                                                progress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                              destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                              destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                        completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
                                        completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
 
 
@@ -288,32 +301,23 @@ NS_ASSUME_NONNULL_BEGIN
 /// @name Getting Progress for Tasks
 /// @name Getting Progress for Tasks
 ///---------------------------------
 ///---------------------------------
 
 
-/**
- Returns the response download progress of the specified task.
-
- @param dataTask The session data task. Must not be `nil`.
-
- @return An `NSProgress` object reporting the response download progress of a task, or `nil` if the progress is unavailable.
- */
-- (nullable NSProgress *)progressForDataTask:(NSURLSessionDataTask *)dataTask;
-
 /**
 /**
  Returns the upload progress of the specified task.
  Returns the upload progress of the specified task.
 
 
- @param uploadTask The session upload task. Must not be `nil`.
+ @param task The session task. Must not be `nil`.
 
 
  @return An `NSProgress` object reporting the upload progress of a task, or `nil` if the progress is unavailable.
  @return An `NSProgress` object reporting the upload progress of a task, or `nil` if the progress is unavailable.
  */
  */
-- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionUploadTask *)uploadTask;
+- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;
 
 
 /**
 /**
  Returns the download progress of the specified task.
  Returns the download progress of the specified task.
 
 
- @param downloadTask The session download task. Must not be `nil`.
+ @param task The session task. Must not be `nil`.
 
 
  @return An `NSProgress` object reporting the download progress of a task, or `nil` if the progress is unavailable.
  @return An `NSProgress` object reporting the download progress of a task, or `nil` if the progress is unavailable.
  */
  */
-- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionDownloadTask *)downloadTask;
+- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;
 
 
 ///-----------------------------------------
 ///-----------------------------------------
 /// @name Setting Session Delegate Callbacks
 /// @name Setting Session Delegate Callbacks

+ 152 - 119
AFNetworking/AFURLSessionManager.m

@@ -89,17 +89,22 @@ typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSUR
 typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);
 typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);
 typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite);
 typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite);
 typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes);
 typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes);
+typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
 
 
 typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
 typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
 
 
+
 #pragma mark -
 #pragma mark -
 
 
 @interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
 @interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
 @property (nonatomic, weak) AFURLSessionManager *manager;
 @property (nonatomic, weak) AFURLSessionManager *manager;
 @property (nonatomic, strong) NSMutableData *mutableData;
 @property (nonatomic, strong) NSMutableData *mutableData;
-@property (nonatomic, strong) NSProgress *progress;
+@property (nonatomic, strong) NSProgress *uploadProgress;
+@property (nonatomic, strong) NSProgress *downloadProgress;
 @property (nonatomic, copy) NSURL *downloadFileURL;
 @property (nonatomic, copy) NSURL *downloadFileURL;
 @property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
 @property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
+@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
+@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
 @property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
 @property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
 @end
 @end
 
 
@@ -112,24 +117,119 @@ typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id re
     }
     }
 
 
     self.mutableData = [NSMutableData data];
     self.mutableData = [NSMutableData data];
+    self.uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
+    self.uploadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown;
 
 
-    self.progress = [NSProgress progressWithTotalUnitCount:0];
-
+    self.downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
+    self.downloadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown;
     return self;
     return self;
 }
 }
 
 
-#pragma mark - NSURLSessionTaskDelegate
+#pragma mark - NSProgress Tracking
 
 
-- (void)URLSession:(__unused NSURLSession *)session
-              task:(__unused NSURLSessionTask *)task
-   didSendBodyData:(__unused int64_t)bytesSent
-    totalBytesSent:(int64_t)totalBytesSent
-totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
-{
-    self.progress.totalUnitCount = totalBytesExpectedToSend;
-    self.progress.completedUnitCount = totalBytesSent;
+- (void)setupProgressForTask:(NSURLSessionTask *)task {
+    __weak __typeof__(task) weakTask = task;
+
+    self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
+    self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
+    [self.uploadProgress setCancellable:YES];
+    [self.uploadProgress setCancellationHandler:^{
+        __typeof__(weakTask) strongTask = weakTask;
+        [strongTask cancel];
+    }];
+    [self.uploadProgress setPausable:YES];
+    [self.uploadProgress setPausingHandler:^{
+        __typeof__(weakTask) strongTask = weakTask;
+        [strongTask suspend];
+    }];
+    if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
+        [self.uploadProgress setResumingHandler:^{
+            __typeof__(weakTask) strongTask = weakTask;
+            [strongTask resume];
+        }];
+    }
+
+    [self.downloadProgress setCancellable:YES];
+    [self.downloadProgress setCancellationHandler:^{
+        __typeof__(weakTask) strongTask = weakTask;
+        [strongTask cancel];
+    }];
+    [self.downloadProgress setPausable:YES];
+    [self.downloadProgress setPausingHandler:^{
+        __typeof__(weakTask) strongTask = weakTask;
+        [strongTask suspend];
+    }];
+
+    if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
+        [self.downloadProgress setResumingHandler:^{
+            __typeof__(weakTask) strongTask = weakTask;
+            [strongTask resume];
+        }];
+    }
+
+    [task addObserver:self
+           forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
+              options:NSKeyValueObservingOptionNew
+              context:NULL];
+    [task addObserver:self
+           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
+              options:NSKeyValueObservingOptionNew
+              context:NULL];
+
+    [task addObserver:self
+           forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
+              options:NSKeyValueObservingOptionNew
+              context:NULL];
+    [task addObserver:self
+           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
+              options:NSKeyValueObservingOptionNew
+              context:NULL];
+
+    [self.downloadProgress addObserver:self
+                            forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
+                               options:NSKeyValueObservingOptionNew
+                               context:NULL];
+    [self.uploadProgress addObserver:self
+                          forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
+                             options:NSKeyValueObservingOptionNew
+                             context:NULL];
+}
+
+- (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
+    [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
+    [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))];
+    [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
+    [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))];
+    [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
+    [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
+    if ([object isKindOfClass:[NSURLSessionTask class]]) {
+        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
+            self.downloadProgress.completedUnitCount = [change[@"new"] longLongValue];
+        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
+            self.downloadProgress.totalUnitCount = [change[@"new"] longLongValue];
+        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
+            self.uploadProgress.completedUnitCount = [change[@"new"] longLongValue];
+        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
+            self.uploadProgress.totalUnitCount = [change[@"new"] longLongValue];
+        }
+    }
+    else if ([object isEqual:self.downloadProgress]) {
+        if (self.downloadProgressBlock) {
+            self.downloadProgressBlock(object);
+        }
+    }
+    else if ([object isEqual:self.uploadProgress]) {
+        if (self.uploadProgressBlock) {
+            self.uploadProgressBlock(object);
+        }
+    }
 }
 }
 
 
+#pragma mark - NSURLSessionTaskDelegate
+
 - (void)URLSession:(__unused NSURLSession *)session
 - (void)URLSession:(__unused NSURLSession *)session
               task:(NSURLSessionTask *)task
               task:(NSURLSessionTask *)task
 didCompleteWithError:(NSError *)error
 didCompleteWithError:(NSError *)error
@@ -206,12 +306,6 @@ didCompleteWithError:(NSError *)error
           dataTask:(__unused NSURLSessionDataTask *)dataTask
           dataTask:(__unused NSURLSessionDataTask *)dataTask
     didReceiveData:(NSData *)data
     didReceiveData:(NSData *)data
 {
 {
-    NSUInteger length = data.length;
-    long long expectedLength = dataTask.response.expectedContentLength;
-    if(expectedLength != -1) {
-        self.progress.totalUnitCount = expectedLength;
-        self.progress.completedUnitCount += length;
-    }
     [self.mutableData appendData:data];
     [self.mutableData appendData:data];
 }
 }
 
 
@@ -236,24 +330,6 @@ didFinishDownloadingToURL:(NSURL *)location
     }
     }
 }
 }
 
 
-- (void)URLSession:(__unused NSURLSession *)session
-      downloadTask:(__unused NSURLSessionDownloadTask *)downloadTask
-      didWriteData:(__unused int64_t)bytesWritten
- totalBytesWritten:(int64_t)totalBytesWritten
-totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
-{
-    self.progress.totalUnitCount = totalBytesExpectedToWrite;
-    self.progress.completedUnitCount = totalBytesWritten;
-}
-
-- (void)URLSession:(__unused NSURLSession *)session
-      downloadTask:(__unused NSURLSessionDownloadTask *)downloadTask
- didResumeAtOffset:(int64_t)fileOffset
-expectedTotalBytes:(int64_t)expectedTotalBytes {
-    self.progress.totalUnitCount = expectedTotalBytes;
-    self.progress.completedUnitCount = fileOffset;
-}
-
 @end
 @end
 
 
 #pragma mark -
 #pragma mark -
@@ -447,7 +523,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
 
 
     [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
     [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
         for (NSURLSessionDataTask *task in dataTasks) {
         for (NSURLSessionDataTask *task in dataTasks) {
-            [self addDelegateForDataTask:task completionHandler:nil];
+            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
         }
         }
 
 
         for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
         for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
@@ -515,10 +591,14 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
 
 
     [self.lock lock];
     [self.lock lock];
     self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
     self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
+    [delegate setupProgressForTask:task];
+    [self addNotificationObserverForTask:task];
     [self.lock unlock];
     [self.lock unlock];
 }
 }
 
 
 - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
 - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
+                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
+              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
              completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
              completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
 {
 {
     AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
     AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
@@ -527,48 +607,28 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
 
 
     dataTask.taskDescription = self.taskDescriptionForSessionTasks;
     dataTask.taskDescription = self.taskDescriptionForSessionTasks;
     [self setDelegate:delegate forTask:dataTask];
     [self setDelegate:delegate forTask:dataTask];
+
+    delegate.uploadProgressBlock = uploadProgressBlock;
+    delegate.downloadProgressBlock = downloadProgressBlock;
 }
 }
 
 
 - (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
 - (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
-                        progress:(NSProgress * __autoreleasing *)progress
+                        progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
 {
 {
     AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
     AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
     delegate.manager = self;
     delegate.manager = self;
     delegate.completionHandler = completionHandler;
     delegate.completionHandler = completionHandler;
 
 
-    int64_t totalUnitCount = uploadTask.countOfBytesExpectedToSend;
-    if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
-        NSString *contentLength = [uploadTask.originalRequest valueForHTTPHeaderField:@"Content-Length"];
-        if(contentLength) {
-            totalUnitCount = (int64_t)[contentLength longLongValue];
-        }
-    }
-
-    if (delegate.progress) {
-        delegate.progress.totalUnitCount = totalUnitCount;
-    } else {
-        delegate.progress = [NSProgress progressWithTotalUnitCount:totalUnitCount];
-    }
-
-    delegate.progress.pausingHandler = ^{
-        [uploadTask suspend];
-    };
-    delegate.progress.cancellationHandler = ^{
-        [uploadTask cancel];
-    };
-
-    if (progress) {
-        *progress = delegate.progress;
-    }
-
     uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
     uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
 
 
     [self setDelegate:delegate forTask:uploadTask];
     [self setDelegate:delegate forTask:uploadTask];
+
+    delegate.uploadProgressBlock = uploadProgressBlock;
 }
 }
 
 
 - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
 - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
-                          progress:(NSProgress * __autoreleasing *)progress
+                          progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                        destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                        destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                  completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
                  completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
 {
 {
@@ -582,29 +642,24 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
         };
         };
     }
     }
 
 
-    if (progress) {
-        *progress = delegate.progress;
-    }
-
     downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
     downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
 
 
     [self setDelegate:delegate forTask:downloadTask];
     [self setDelegate:delegate forTask:downloadTask];
+
+    delegate.downloadProgressBlock = downloadProgressBlock;
 }
 }
 
 
 - (void)removeDelegateForTask:(NSURLSessionTask *)task {
 - (void)removeDelegateForTask:(NSURLSessionTask *)task {
     NSParameterAssert(task);
     NSParameterAssert(task);
 
 
+    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
     [self.lock lock];
     [self.lock lock];
+    [delegate cleanUpProgressForTask:task];
+    [self removeNotificationObserverForTask:task];
     [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
     [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
     [self.lock unlock];
     [self.lock unlock];
 }
 }
 
 
-- (void)removeAllDelegates {
-    [self.lock lock];
-    [self.mutableTaskDelegatesKeyedByTaskIdentifier removeAllObjects];
-    [self.lock unlock];
-}
-
 #pragma mark -
 #pragma mark -
 
 
 - (NSArray *)tasksForKeyPath:(NSString *)keyPath {
 - (NSArray *)tasksForKeyPath:(NSString *)keyPath {
@@ -681,14 +736,20 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
 - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
 - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
                             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
 {
 {
+    return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
+}
+
+- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
+                               uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
+                             downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
+                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler {
+
     __block NSURLSessionDataTask *dataTask = nil;
     __block NSURLSessionDataTask *dataTask = nil;
     dispatch_sync(url_session_manager_creation_queue(), ^{
     dispatch_sync(url_session_manager_creation_queue(), ^{
         dataTask = [self.session dataTaskWithRequest:request];
         dataTask = [self.session dataTaskWithRequest:request];
     });
     });
 
 
-    [self addNotificationObserverForTask:dataTask];
-
-    [self addDelegateForDataTask:dataTask completionHandler:completionHandler];
+    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
 
 
     return dataTask;
     return dataTask;
 }
 }
@@ -697,7 +758,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
 
 
 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                          fromFile:(NSURL *)fileURL
                                          fromFile:(NSURL *)fileURL
-                                         progress:(NSProgress * __autoreleasing *)progress
+                                         progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                 completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
                                 completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
 {
 {
     __block NSURLSessionUploadTask *uploadTask = nil;
     __block NSURLSessionUploadTask *uploadTask = nil;
@@ -705,22 +766,20 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
         uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
         uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
     });
     });
 
 
-    [self addNotificationObserverForTask:uploadTask];
-
     if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
     if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
         for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
         for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
             uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
             uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
         }
         }
     }
     }
 
 
-    [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler];
+    [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
 
 
     return uploadTask;
     return uploadTask;
 }
 }
 
 
 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                          fromData:(NSData *)bodyData
                                          fromData:(NSData *)bodyData
-                                         progress:(NSProgress * __autoreleasing *)progress
+                                         progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                 completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
                                 completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
 {
 {
     __block NSURLSessionUploadTask *uploadTask = nil;
     __block NSURLSessionUploadTask *uploadTask = nil;
@@ -728,15 +787,13 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
         uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
         uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
     });
     });
 
 
-    [self addNotificationObserverForTask:uploadTask];
-
-    [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler];
+    [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
 
 
     return uploadTask;
     return uploadTask;
 }
 }
 
 
 - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
 - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
-                                                 progress:(NSProgress * __autoreleasing *)progress
+                                                 progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                         completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
                                         completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
 {
 {
     __block NSURLSessionUploadTask *uploadTask = nil;
     __block NSURLSessionUploadTask *uploadTask = nil;
@@ -744,9 +801,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
         uploadTask = [self.session uploadTaskWithStreamedRequest:request];
         uploadTask = [self.session uploadTaskWithStreamedRequest:request];
     });
     });
 
 
-    [self addNotificationObserverForTask:uploadTask];
-
-    [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler];
+    [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
 
 
     return uploadTask;
     return uploadTask;
 }
 }
@@ -754,7 +809,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
 #pragma mark -
 #pragma mark -
 
 
 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
-                                             progress:(NSProgress * __autoreleasing *)progress
+                                             progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                           destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                           destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                     completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
                                     completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
 {
 {
@@ -763,15 +818,13 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
         downloadTask = [self.session downloadTaskWithRequest:request];
         downloadTask = [self.session downloadTaskWithRequest:request];
     });
     });
 
 
-    [self addNotificationObserverForTask:downloadTask];
-
-    [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler];
+    [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
 
 
     return downloadTask;
     return downloadTask;
 }
 }
 
 
 - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
 - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
-                                                progress:(NSProgress * __autoreleasing *)progress
+                                                progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                              destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                              destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                        completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
                                        completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
 {
 {
@@ -780,24 +833,18 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
         downloadTask = [self.session downloadTaskWithResumeData:resumeData];
         downloadTask = [self.session downloadTaskWithResumeData:resumeData];
     });
     });
 
 
-    [self addNotificationObserverForTask:downloadTask];
-
-    [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler];
+    [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
 
 
     return downloadTask;
     return downloadTask;
 }
 }
 
 
 #pragma mark -
 #pragma mark -
-- (NSProgress *)progressForDataTask:(NSURLSessionDataTask *)dataTask {
-    return [[self delegateForTask:dataTask] progress];
+- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task {
+    return [[self delegateForTask:task] uploadProgress];
 }
 }
 
 
-- (NSProgress *)uploadProgressForTask:(NSURLSessionUploadTask *)uploadTask {
-    return [[self delegateForTask:uploadTask] progress];
-}
-
-- (NSProgress *)downloadProgressForTask:(NSURLSessionDownloadTask *)downloadTask {
-    return [[self delegateForTask:downloadTask] progress];
+- (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task {
+    return [[self delegateForTask:task] downloadProgress];
 }
 }
 
 
 #pragma mark -
 #pragma mark -
@@ -897,7 +944,6 @@ didBecomeInvalidWithError:(NSError *)error
         self.sessionDidBecomeInvalid(session, error);
         self.sessionDidBecomeInvalid(session, error);
     }
     }
 
 
-    [self removeAllDelegates];
     [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
     [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
 }
 }
 
 
@@ -1011,9 +1057,6 @@ totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
         }
         }
     }
     }
 
 
-    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
-    [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalUnitCount];
-
     if (self.taskDidSendBodyData) {
     if (self.taskDidSendBodyData) {
         self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
         self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
     }
     }
@@ -1035,8 +1078,6 @@ didCompleteWithError:(NSError *)error
     if (self.taskDidComplete) {
     if (self.taskDidComplete) {
         self.taskDidComplete(session, task, error);
         self.taskDidComplete(session, task, error);
     }
     }
-
-    [self removeNotificationObserverForTask:task];
 }
 }
 
 
 #pragma mark - NSURLSessionDataDelegate
 #pragma mark - NSURLSessionDataDelegate
@@ -1067,9 +1108,6 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
         [self setDelegate:delegate forTask:downloadTask];
         [self setDelegate:delegate forTask:downloadTask];
     }
     }
 
 
-    [self removeNotificationObserverForTask:dataTask];
-    [self addNotificationObserverForTask:downloadTask];
-
     if (self.dataTaskDidBecomeDownloadTask) {
     if (self.dataTaskDidBecomeDownloadTask) {
         self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
         self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
     }
     }
@@ -1136,8 +1174,6 @@ didFinishDownloadingToURL:(NSURL *)location
     if (delegate) {
     if (delegate) {
         [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
         [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
     }
     }
-
-    [self removeNotificationObserverForTask:downloadTask];
 }
 }
 
 
 - (void)URLSession:(NSURLSession *)session
 - (void)URLSession:(NSURLSession *)session
@@ -1146,9 +1182,6 @@ didFinishDownloadingToURL:(NSURL *)location
  totalBytesWritten:(int64_t)totalBytesWritten
  totalBytesWritten:(int64_t)totalBytesWritten
 totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
 totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
 {
 {
-    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
-    [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
-
     if (self.downloadTaskDidWriteData) {
     if (self.downloadTaskDidWriteData) {
         self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
         self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
     }
     }

+ 1 - 1
Example/Classes/Models/Post.m

@@ -44,7 +44,7 @@
 #pragma mark -
 #pragma mark -
 
 
 + (NSURLSessionDataTask *)globalTimelinePostsWithBlock:(void (^)(NSArray *posts, NSError *error))block {
 + (NSURLSessionDataTask *)globalTimelinePostsWithBlock:(void (^)(NSArray *posts, NSError *error))block {
-    return [[AFAppDotNetAPIClient sharedClient] GET:@"stream/0/posts/stream/global" parameters:nil success:^(NSURLSessionDataTask * __unused task, id JSON) {
+    return [[AFAppDotNetAPIClient sharedClient] GET:@"stream/0/posts/stream/global" parameters:nil progress:nil success:^(NSURLSessionDataTask * __unused task, id JSON) {
         NSArray *postsFromResponse = [JSON valueForKeyPath:@"data"];
         NSArray *postsFromResponse = [JSON valueForKeyPath:@"data"];
         NSMutableArray *mutablePosts = [NSMutableArray arrayWithCapacity:[postsFromResponse count]];
         NSMutableArray *mutablePosts = [NSMutableArray arrayWithCapacity:[postsFromResponse count]];
         for (NSDictionary *attributes in postsFromResponse) {
         for (NSDictionary *attributes in postsFromResponse) {

+ 68 - 0
Tests/Tests/AFHTTPSessionManagerTests.m

@@ -176,6 +176,7 @@
     nilTask = [self.manager
     nilTask = [self.manager
                GET:@"test"
                GET:@"test"
                parameters:@{@"key":@"value"}
                parameters:@{@"key":@"value"}
+               progress:nil
                success:nil
                success:nil
                failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                    XCTAssertNil(task);
                    XCTAssertNil(task);
@@ -186,6 +187,70 @@
 }
 }
 
 
 
 
+#pragma mark - Progress
+
+- (void)testDownloadProgressIsReportedForGET {
+    XCTestExpectation *expectation = [self expectationWithDescription:@"Progress Should equal 1.0"];
+    [self.manager
+     GET:@"image"
+     parameters:nil
+     progress:^(NSProgress * _Nonnull downloadProgress) {
+         if (downloadProgress.fractionCompleted == 1.0) {
+             [expectation fulfill];
+         }
+     }
+     success:nil
+     failure:nil];
+    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
+}
+
+- (void)testUploadProgressIsReportedForPOST {
+    NSMutableString *payload = [NSMutableString stringWithString:@"AFNetworking"];
+    while ([payload lengthOfBytesUsingEncoding:NSUTF8StringEncoding] < 20000) {
+        [payload appendString:@"AFNetworking"];
+    }
+
+    __weak __block XCTestExpectation *expectation = [self expectationWithDescription:@"Progress Should equal 1.0"];
+
+    [self.manager
+     POST:@"post"
+     parameters:payload
+     progress:^(NSProgress * _Nonnull uploadProgress) {
+         if (uploadProgress.fractionCompleted == 1.0) {
+             [expectation fulfill];
+             expectation = nil;
+         }
+     }
+     success:nil
+     failure:nil];
+    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
+}
+
+- (void)testUploadProgressIsReportedForStreamingPost {
+    NSMutableString *payload = [NSMutableString stringWithString:@"AFNetworking"];
+    while ([payload lengthOfBytesUsingEncoding:NSUTF8StringEncoding] < 20000) {
+        [payload appendString:@"AFNetworking"];
+    }
+
+    __block __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Progress Should equal 1.0"];
+
+    [self.manager
+     POST:@"post"
+     parameters:nil
+     constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
+         [formData appendPartWithFileData:[payload dataUsingEncoding:NSUTF8StringEncoding] name:@"AFNetworking" fileName:@"AFNetworking" mimeType:@"text/html"];
+     }
+     progress:^(NSProgress * _Nonnull uploadProgress) {
+         if (uploadProgress.fractionCompleted == 1.0) {
+             [expectation fulfill];
+             expectation = nil;
+         }
+     }
+     success:nil
+     failure:nil];
+    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
+}
+
 # pragma mark - Rest Interface
 # pragma mark - Rest Interface
 
 
 - (void)testThatSuccessBlockIsCalledFor200 {
 - (void)testThatSuccessBlockIsCalledFor200 {
@@ -193,6 +258,7 @@
     [self.manager
     [self.manager
      GET:@"status/200"
      GET:@"status/200"
      parameters:nil
      parameters:nil
+     progress:nil
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
          [expectation fulfill];
          [expectation fulfill];
      }
      }
@@ -205,6 +271,7 @@
     [self.manager
     [self.manager
      GET:@"status/404"
      GET:@"status/404"
      parameters:nil
      parameters:nil
+     progress:nil
      success:nil
      success:nil
      failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nullable error) {
      failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nullable error) {
          [expectation fulfill];
          [expectation fulfill];
@@ -218,6 +285,7 @@
     [self.manager
     [self.manager
      GET:@"status/204"
      GET:@"status/204"
      parameters:nil
      parameters:nil
+     progress:nil
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
          urlResponseObject = responseObject;
          urlResponseObject = responseObject;
          [expectation fulfill];
          [expectation fulfill];

+ 5 - 0
Tests/Tests/AFNetworkActivityManagerTests.m

@@ -69,6 +69,7 @@
     [self.sessionManager
     [self.sessionManager
      GET:@"/delay/1"
      GET:@"/delay/1"
      parameters:nil
      parameters:nil
+     progress:nil
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
          [requestExpectation fulfill];
          [requestExpectation fulfill];
      }
      }
@@ -94,6 +95,7 @@
     [self.sessionManager
     [self.sessionManager
      GET:@"/status/404"
      GET:@"/status/404"
      parameters:nil
      parameters:nil
+     progress:nil
      success:nil
      success:nil
      failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
      failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
          [requestExpectation fulfill];
          [requestExpectation fulfill];
@@ -126,6 +128,7 @@
     [self.sessionManager
     [self.sessionManager
      GET:@"/delay/2"
      GET:@"/delay/2"
      parameters:nil
      parameters:nil
+     progress:nil
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
          requestEndTime = CACurrentMediaTime();
          requestEndTime = CACurrentMediaTime();
          [requestExpectation fulfill];
          [requestExpectation fulfill];
@@ -154,6 +157,7 @@
     [self.sessionManager
     [self.sessionManager
      GET:@"/delay/4"
      GET:@"/delay/4"
      parameters:nil
      parameters:nil
+     progress:nil
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
          [requestExpectation fulfill];
          [requestExpectation fulfill];
      }
      }
@@ -163,6 +167,7 @@
     [self.sessionManager
     [self.sessionManager
      GET:@"/delay/2"
      GET:@"/delay/2"
      parameters:nil
      parameters:nil
+     progress:nil
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
      success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
 
 
          [secondRequestExpectation fulfill];
          [secondRequestExpectation fulfill];

+ 1 - 1
Tests/Tests/AFNetworkReachabilityManagerTests.m

@@ -93,7 +93,7 @@
 
 
 - (void)verifyReachabilityStatusBlockGetsCalledWithManager:(AFNetworkReachabilityManager *)manager
 - (void)verifyReachabilityStatusBlockGetsCalledWithManager:(AFNetworkReachabilityManager *)manager
 {
 {
-    XCTestExpectation *expectation = [self expectationWithDescription:@"reachability status change block gets called"];
+    __weak XCTestExpectation *expectation = [self expectationWithDescription:@"reachability status change block gets called"];
 
 
     typeof(manager) __weak weakManager = manager;
     typeof(manager) __weak weakManager = manager;
     [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
     [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {

+ 91 - 43
Tests/Tests/AFURLSessionManagerTests.m

@@ -33,6 +33,12 @@
 
 
 @implementation AFURLSessionManagerTests
 @implementation AFURLSessionManagerTests
 
 
+- (NSURLRequest *)bigImageURLRequest {
+    NSURL *url = [NSURL URLWithString:@"http://scitechdaily.com/images/New-Image-of-the-Galaxy-Messier-94-also-Known-as-NGC-4736.jpg"];
+    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
+    return request;
+}
+
 - (void)setUp {
 - (void)setUp {
     [super setUp];
     [super setUp];
     self.localManager = [[AFURLSessionManager alloc] init];
     self.localManager = [[AFURLSessionManager alloc] init];
@@ -62,67 +68,109 @@
 
 
 #pragma mark Progress -
 #pragma mark Progress -
 
 
-- (void)testDataTaskDoesReportProgress {
-    NSURL *url = [NSURL URLWithString:@"http://i.space.com/images/i/8123/original/saturn-moon-mimas-death-star.jpg"];
-    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
-    XCTestExpectation *expectation = [self expectationWithDescription:@"Request should succeed"];
+- (void)testDataTaskDoesReportDownloadProgress {
     NSURLSessionDataTask *task;
     NSURLSessionDataTask *task;
 
 
+    __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Progress should equal 1.0"];
     task = [self.localManager
     task = [self.localManager
-            dataTaskWithRequest:request
-            completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
-                [expectation fulfill];
-            }];
-
-    NSProgress *progress = [self.localManager progressForDataTask:task];
-    [self keyValueObservingExpectationForObject:progress keyPath:@"fractionCompleted"
-                                        handler:^BOOL(NSProgress  *observedProgress, NSDictionary * _Nonnull change) {
-                                            double new = [change[@"new"] doubleValue];
-                                            double old = [change[@"old"] doubleValue];
-                                            return new == 1.0 && old != 0.0;
-                                        }];
+            dataTaskWithRequest:[self bigImageURLRequest]
+            uploadProgress:nil
+            downloadProgress:^(NSProgress * _Nonnull downloadProgress) {
+                if (downloadProgress.fractionCompleted == 1.0) {
+                    [expectation fulfill];
+                }
+            }
+            completionHandler:nil];
+    
     [task resume];
     [task resume];
     [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
     [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
 }
 }
 
 
-- (void)testUploadTasksProgressBecomesPartOfCurrentProgress {
-    NSProgress *overallProgress = [NSProgress progressWithTotalUnitCount:100];
+- (void)testDataTaskDownloadProgressCanBeKVOd {
+    NSURLSessionDataTask *task;
 
 
-    [overallProgress becomeCurrentWithPendingUnitCount:80];
-    NSProgress *uploadProgress = nil;
+    task = [self.localManager
+            dataTaskWithRequest:[self bigImageURLRequest]
+            uploadProgress:nil
+            downloadProgress:nil
+            completionHandler:nil];
+
+        NSProgress *progress = [self.localManager downloadProgressForTask:task];
+        [self keyValueObservingExpectationForObject:progress keyPath:@"fractionCompleted"
+                                            handler:^BOOL(NSProgress  *observedProgress, NSDictionary * _Nonnull change) {
+                                                double new = [change[@"new"] doubleValue];
+                                                double old = [change[@"old"] doubleValue];
+                                                return new == 1.0 && old != 0.0;
+                                            }];
+    [task resume];
+    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
+}
+
+- (void)testDownloadTaskDoesReportProgress {
+    __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Progress should equal 1.0"];
+    NSURLSessionTask *task;
+    task = [self.localManager
+            downloadTaskWithRequest:[self bigImageURLRequest]
+            progress:^(NSProgress * _Nonnull downloadProgress) {
+                if (downloadProgress.fractionCompleted == 1.0) {
+                    [expectation fulfill];
+                }
+            }
+            destination:nil
+            completionHandler:nil];
+    [task resume];
+    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
+}
 
 
-    [self.localManager uploadTaskWithRequest:[NSURLRequest requestWithURL:self.baseURL]
-                               fromData:[NSData data]
-                                 progress:&uploadProgress
-                        completionHandler:nil];
-    [overallProgress resignCurrent];
+- (void)testUploadTaskDoesReportProgress {
+    NSMutableString *payload = [NSMutableString stringWithString:@"AFNetworking"];
+    while ([payload lengthOfBytesUsingEncoding:NSUTF8StringEncoding] < 20000) {
+        [payload appendString:@"AFNetworking"];
+    }
 
 
-    XCTAssertTrue(overallProgress.fractionCompleted == 0);
+    NSURL *url = [NSURL URLWithString:[[self.baseURL absoluteString] stringByAppendingString:@"/post"]];
+    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
+    [request setHTTPMethod:@"POST"];
 
 
-    uploadProgress.totalUnitCount = 1;
-    uploadProgress.completedUnitCount = 1;
+    __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Progress should equal 1.0"];
 
 
-    XCTAssertTrue(overallProgress.fractionCompleted == 0.8);
+    NSURLSessionTask *task;
+    task = [self.localManager
+            uploadTaskWithRequest:request
+            fromData:[payload dataUsingEncoding:NSUTF8StringEncoding]
+            progress:^(NSProgress * _Nonnull uploadProgress) {
+                NSLog(@"%@", uploadProgress.localizedDescription);
+                if ([uploadProgress fractionCompleted] == 1.0) {
+                    [expectation fulfill];
+                }
+            }
+            completionHandler:nil];
+    [task resume];
+    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
 }
 }
 
 
-- (void)testDownloadTasksProgressBecomesPartOfCurrentProgress {
-    NSProgress *overallProgress = [NSProgress progressWithTotalUnitCount:100];
-
-    [overallProgress becomeCurrentWithPendingUnitCount:80];
-    NSProgress *downloadProgress = nil;
+- (void)testUploadProgressCanBeKVOd {
+    NSMutableString *payload = [NSMutableString stringWithString:@"AFNetworking"];
+    while ([payload lengthOfBytesUsingEncoding:NSUTF8StringEncoding] < 20000) {
+        [payload appendString:@"AFNetworking"];
+    }
 
 
-    [self.localManager downloadTaskWithRequest:[NSURLRequest requestWithURL:self.baseURL]
-                                 progress:&downloadProgress
-                              destination:nil
-                        completionHandler:nil];
-    [overallProgress resignCurrent];
+    NSURL *url = [NSURL URLWithString:[[self.baseURL absoluteString] stringByAppendingString:@"/post"]];
+    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
+    [request setHTTPMethod:@"POST"];
 
 
-    XCTAssertTrue(overallProgress.fractionCompleted == 0.0);
+    NSURLSessionTask *task;
+    task = [self.localManager
+            uploadTaskWithRequest:request
+            fromData:[payload dataUsingEncoding:NSUTF8StringEncoding]
+            progress:nil
+            completionHandler:nil];
 
 
-    downloadProgress.totalUnitCount = 1;
-    downloadProgress.completedUnitCount = 1;
+    NSProgress *uploadProgress = [self.localManager uploadProgressForTask:task];
+    [self keyValueObservingExpectationForObject:uploadProgress keyPath:NSStringFromSelector(@selector(fractionCompleted)) expectedValue:@(1.0)];
 
 
-    XCTAssertTrue(overallProgress.fractionCompleted == 0.8);
+    [task resume];
+    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
 }
 }
 
 
 #pragma mark - Issue #2702 Tests
 #pragma mark - Issue #2702 Tests

+ 2 - 1
UIKit+AFNetworking/UIWebView+AFNetworking.m

@@ -129,6 +129,7 @@
     dataTask = [self.sessionManager
     dataTask = [self.sessionManager
             GET:request.URL.absoluteString
             GET:request.URL.absoluteString
             parameters:nil
             parameters:nil
+            progress:nil
             success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
             success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
                 __strong __typeof(weakSelf) strongSelf = weakSelf;
                 __strong __typeof(weakSelf) strongSelf = weakSelf;
                 if (success) {
                 if (success) {
@@ -146,7 +147,7 @@
                 }
                 }
             }];
             }];
     self.af_URLSessionTask = dataTask;
     self.af_URLSessionTask = dataTask;
-    *progress = [self.sessionManager progressForDataTask:dataTask];
+    *progress = [self.sessionManager downloadProgressForTask:dataTask];
     [self.af_URLSessionTask resume];
     [self.af_URLSessionTask resume];
 
 
     if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
     if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {