diff --git a/AFNetworking/AFDownloadRequestOperation/AFDownloadRequestOperation.h b/AFNetworking/AFDownloadRequestOperation/AFDownloadRequestOperation.h index a927cce..99f7485 100644 --- a/AFNetworking/AFDownloadRequestOperation/AFDownloadRequestOperation.h +++ b/AFNetworking/AFDownloadRequestOperation/AFDownloadRequestOperation.h @@ -1,17 +1,17 @@ // AFDownloadRequestOperation.h // // Copyright (c) 2012 Peter Steinberger (http://petersteinberger.com) -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,12 +29,12 @@ */ @interface AFDownloadRequestOperation : AFHTTPRequestOperation -/** +/** A String value that defines the target path or directory. - + We try to be clever here and understand both a directory or a filename. The target directory should already be create, or the download fill fail. - + If the target is a directory, we use the last part of the URL as a default file name. targetPath is the responseObject if operation succeeds */ @@ -46,26 +46,26 @@ */ @property (assign) BOOL shouldOverwrite; -/** +/** A Boolean value that indicates if we should try to resume the download. Defaults is `YES`. Can only be set while creating the request. */ @property (assign, readonly) BOOL shouldResume; -/** +/** Deletes the temporary file if operations is cancelled. Defaults to `NO`. */ @property (assign, getter=isDeletingTempFileOnCancel) BOOL deleteTempFileOnCancel; -/** +/** Expected total length. This is different than expectedContentLength if the file is resumed. - + Note: this can also be zero if the file size is not sent (*) */ @property (assign, readonly) long long totalContentLength; -/** +/** Indicator for the file offset on partial downloads. This is greater than zero if the file download is resumed. */ @property (assign, readonly) long long offsetContentLength; @@ -88,23 +88,23 @@ */ - (id)initWithRequest:(NSURLRequest *)urlRequest targetPath:(NSString *)targetPath shouldResume:(BOOL)shouldResume; -/** +/** Deletes the temporary file. - + Returns `NO` if an error happened, `YES` if the file is removed or did not exist in the first place. */ - (BOOL)deleteTempFileWithError:(NSError **)error; -/** +/** Returns the path used for the temporary file. Returns `nil` if the targetPath has not been set. */ - (NSString *)tempPath; /** - Sets a callback to be called when an undetermined number of bytes have been downloaded from the server. This is a variant of setDownloadProgressBlock that adds support for progressive downloads and adds the - + Sets a callback to be called when an undetermined number of bytes have been downloaded from the server. This is a variant of setDownloadProgressBlock that adds support for progressive downloads and adds the + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the number of bytes read since the last time the download progress block was called, the bytes expected to be read during the request, the bytes already read during this request, the total bytes read (including from previous partial downloads), and the total bytes expected to be read for the file. This block may be called multiple times. - + @see setDownloadProgressBlock */ - (void)setProgressiveDownloadProgressBlock:(void (^)(AFDownloadRequestOperation *operation, NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpected, long long totalBytesReadForFile, long long totalBytesExpectedToReadForFile))block; diff --git a/AFNetworking/AFDownloadRequestOperation/AFDownloadRequestOperation.m b/AFNetworking/AFDownloadRequestOperation/AFDownloadRequestOperation.m index 7a5bdd0..d9fa71a 100644 --- a/AFNetworking/AFDownloadRequestOperation/AFDownloadRequestOperation.m +++ b/AFNetworking/AFDownloadRequestOperation/AFDownloadRequestOperation.m @@ -257,8 +257,6 @@ - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLRespon totalContentLength = [bytes[3] longLongValue]; // if this is *, it's converted to 0 } } - }else if (httpResponse.statusCode != 200){ - return; } self.totalBytesReadPerDownload = 0; diff --git a/AFNetworking/AFHTTPRequestOperation.h b/AFNetworking/AFHTTPRequestOperation.h index dfa82f6..321d4e7 100644 --- a/AFNetworking/AFHTTPRequestOperation.h +++ b/AFNetworking/AFHTTPRequestOperation.h @@ -1,6 +1,5 @@ // AFHTTPRequestOperation.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -40,7 +39,7 @@ /** Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an AFHTTPResponse serializer, which uses the raw data as its response object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed. - @warning `responseSerializer` must not be `nil`. Setting a response serializer will clear out any cached value + @warning `responseSerializer` must not be `nil`. Setting a response serializer will clear out any cached value */ @property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; @@ -57,7 +56,7 @@ Sets the `completionBlock` property with a block that executes either the specified success or failure block, depending on the state of the request on completion. If `error` returns a value, which can be caused by an unacceptable status code or content type, then `failure` is executed. Otherwise, `success` is executed. This method should be overridden in subclasses in order to specify the response object passed into the success block. - + @param success The block to be executed on the completion of a successful request. This block has no return value and takes two arguments: the receiver operation and the object constructed from the response data of the request. @param failure The block to be executed on the completion of an unsuccessful request. This block has no return value and takes two arguments: the receiver operation and the error that occurred during the request. */ diff --git a/AFNetworking/AFHTTPRequestOperation.m b/AFNetworking/AFHTTPRequestOperation.m index 723a3b3..b8deda8 100644 --- a/AFNetworking/AFHTTPRequestOperation.m +++ b/AFNetworking/AFHTTPRequestOperation.m @@ -1,6 +1,5 @@ // AFHTTPRequestOperation.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -57,6 +56,7 @@ @interface AFHTTPRequestOperation () @end @implementation AFHTTPRequestOperation +@dynamic response; @dynamic lock; - (instancetype)initWithRequest:(NSURLRequest *)urlRequest { @@ -168,7 +168,7 @@ - (void)pause { self.request = mutableURLRequest; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding + (BOOL)supportsSecureCoding { return YES; @@ -194,7 +194,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone { - AFHTTPRequestOperation *operation = [[[self class] allocWithZone:zone] initWithRequest:self.request]; + AFHTTPRequestOperation *operation = [super copyWithZone:zone]; operation.responseSerializer = [self.responseSerializer copyWithZone:zone]; operation.completionQueue = self.completionQueue; diff --git a/AFNetworking/AFHTTPRequestOperationManager.h b/AFNetworking/AFHTTPRequestOperationManager.h index d7f6d42..17caa78 100644 --- a/AFNetworking/AFHTTPRequestOperationManager.h +++ b/AFNetworking/AFHTTPRequestOperationManager.h @@ -1,6 +1,5 @@ // AFHTTPRequestOperationManager.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -36,23 +35,31 @@ #import "AFSecurityPolicy.h" #import "AFNetworkReachabilityManager.h" +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + /** `AFHTTPRequestOperationManager` encapsulates the common patterns of communicating with a web application over HTTP, including request creation, response serialization, network reachability monitoring, and security, as well as request operation management. ## Subclassing Notes - + Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. - + For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect. - + ## Methods to Override To change the behavior of all request operation construction for an `AFHTTPRequestOperationManager` subclass, override `HTTPRequestOperationWithRequest:success:failure`. ## Serialization - - Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. - + + Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. + Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` ## URL Construction Using Relative Paths @@ -75,12 +82,12 @@ Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. - ## NSecureCoding & NSCopying Caveats + ## NSSecureCoding & NSCopying Caveats - `AFHTTPRequestOperationManager` conforms to the `NSecureCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. There are a few minor caveats to keep in mind, however: + `AFHTTPRequestOperationManager` conforms to the `NSSecureCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. There are a few minor caveats to keep in mind, however: - Archives and copies of HTTP clients will be initialized with an empty operation queue. - - NSecureCoding cannot serialize / deserialize block properties, so an archive of an HTTP client will not include any reachability callback block that may be set. + - NSSecureCoding cannot serialize / deserialize block properties, so an archive of an HTTP client will not include any reachability callback block that may be set. */ @interface AFHTTPRequestOperationManager : NSObject @@ -91,7 +98,7 @@ /** Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. - + @warning `requestSerializer` must not be `nil`. */ @property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; @@ -151,12 +158,20 @@ /** The dispatch queue for the `completionBlock` of request operations. If `NULL` (default), the main queue is used. */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT @property (nonatomic, strong) dispatch_queue_t completionQueue; +#else +@property (nonatomic, assign) dispatch_queue_t completionQueue; +#endif /** The dispatch group for the `completionBlock` of request operations. If `NULL` (default), a private dispatch group is used. */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT @property (nonatomic, strong) dispatch_group_t completionGroup; +#else +@property (nonatomic, assign) dispatch_group_t completionGroup; +#endif ///--------------------------------------------- /// @name Creating and Initializing HTTP Clients @@ -169,14 +184,14 @@ /** Initializes an `AFHTTPRequestOperationManager` object with the specified base URL. - + This is the designated initializer. - + @param url The base URL for the HTTP client. @return The newly-initialized HTTP client */ -- (instancetype)initWithBaseURL:(NSURL *)url; +- (instancetype)initWithBaseURL:(NSURL *)url NS_DESIGNATED_INITIALIZER; ///--------------------------------------- /// @name Managing HTTP Request Operations diff --git a/AFNetworking/AFHTTPRequestOperationManager.m b/AFNetworking/AFHTTPRequestOperationManager.m index 03116cc..60739e5 100644 --- a/AFNetworking/AFHTTPRequestOperationManager.m +++ b/AFNetworking/AFHTTPRequestOperationManager.m @@ -1,6 +1,5 @@ // AFHTTPRequestOperationManager.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -43,7 +42,7 @@ + (instancetype)manager { } - (instancetype)init { - return [self initWithBaseURL:nil]; + return [self initWithBaseURL:nil]; } - (instancetype)initWithBaseURL:(NSURL *)url { @@ -92,6 +91,30 @@ - (void)setResponseSerializer:(AFHTTPResponseSerializer ", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.operationQueue]; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding + (BOOL)supportsSecureCoding { return YES; @@ -246,7 +277,7 @@ - (id)copyWithZone:(NSZone *)zone { HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; - + return HTTPClient; } diff --git a/AFNetworking/AFHTTPSessionManager.h b/AFNetworking/AFHTTPSessionManager.h index a84fcc5..cc98bab 100644 --- a/AFNetworking/AFHTTPSessionManager.h +++ b/AFNetworking/AFHTTPSessionManager.h @@ -1,6 +1,5 @@ // AFHTTPSessionManager.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,29 +31,37 @@ #import "AFURLSessionManager.h" +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + /** `AFHTTPSessionManager` is a subclass of `AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths. - + ## Subclassing Notes - + Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. - + For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect. ## Methods to Override To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:completionHandler:`. - + ## Serialization - + Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. - + Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` ## URL Construction Using Relative Paths For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`. - + Below are a few examples of how `baseURL` and relative paths interact: NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; @@ -66,6 +73,8 @@ [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash. + + @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance. */ #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) @@ -79,7 +88,7 @@ /** Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. - + @warning `requestSerializer` must not be `nil`. */ @property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; @@ -102,7 +111,7 @@ /** Initializes an `AFHTTPSessionManager` object with the specified base URL. - + @param url The base URL for the HTTP client. @return The newly-initialized HTTP client @@ -120,7 +129,7 @@ @return The newly-initialized HTTP client */ - (instancetype)initWithBaseURL:(NSURL *)url - sessionConfiguration:(NSURLSessionConfiguration *)configuration; + sessionConfiguration:(NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; ///--------------------------- /// @name Making HTTP Requests diff --git a/AFNetworking/AFHTTPSessionManager.m b/AFNetworking/AFHTTPSessionManager.m index 5abfe7e..2b8352e 100644 --- a/AFNetworking/AFHTTPSessionManager.m +++ b/AFNetworking/AFHTTPSessionManager.m @@ -1,6 +1,5 @@ // AFHTTPSessionManager.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -47,6 +46,7 @@ @interface AFHTTPSessionManager () @end @implementation AFHTTPSessionManager +@dynamic responseSerializer; + (instancetype)manager { return [[[self class] alloc] initWithBaseURL:nil]; @@ -109,23 +109,11 @@ - (NSURLSessionDataTask *)GET:(NSString *)URLString success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure]; - __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { - if (error) { - if (failure) { - failure(task, error); - } - } else { - if (success) { - success(task, responseObject); - } - } - }]; + [dataTask resume]; - [task resume]; - - return task; + return dataTask; } - (NSURLSessionDataTask *)HEAD:(NSString *)URLString @@ -133,23 +121,15 @@ - (NSURLSessionDataTask *)HEAD:(NSString *)URLString success:(void (^)(NSURLSessionDataTask *task))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"HEAD" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; - - __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id __unused responseObject, NSError *error) { - if (error) { - if (failure) { - failure(task, error); - } - } else { - if (success) { - success(task); - } + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters success:^(NSURLSessionDataTask *task, __unused id responseObject) { + if (success) { + success(task); } - }]; + } failure:failure]; - [task resume]; + [dataTask resume]; - return task; + return dataTask; } - (NSURLSessionDataTask *)POST:(NSString *)URLString @@ -157,23 +137,11 @@ - (NSURLSessionDataTask *)POST:(NSString *)URLString success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure]; - __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { - if (error) { - if (failure) { - failure(task, error); - } - } else { - if (success) { - success(task, responseObject); - } - } - }]; - - [task resume]; + [dataTask resume]; - return task; + return dataTask; } - (NSURLSessionDataTask *)POST:(NSString *)URLString @@ -182,7 +150,20 @@ - (NSURLSessionDataTask *)POST:(NSString *)URLString success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { - NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:nil]; + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; + if (serializationError) { + if (failure) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); +#pragma clang diagnostic pop + } + + return nil; + } __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { if (error) { @@ -206,23 +187,11 @@ - (NSURLSessionDataTask *)PUT:(NSString *)URLString success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"PUT" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters success:success failure:failure]; - __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { - if (error) { - if (failure) { - failure(task, error); - } - } else { - if (success) { - success(task, responseObject); - } - } - }]; + [dataTask resume]; - [task resume]; - - return task; + return dataTask; } - (NSURLSessionDataTask *)PATCH:(NSString *)URLString @@ -230,23 +199,11 @@ - (NSURLSessionDataTask *)PATCH:(NSString *)URLString success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"PATCH" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters success:success failure:failure]; - __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { - if (error) { - if (failure) { - failure(task, error); - } - } else { - if (success) { - success(task, responseObject); - } - } - }]; + [dataTask resume]; - [task resume]; - - return task; + return dataTask; } - (NSURLSessionDataTask *)DELETE:(NSString *)URLString @@ -254,23 +211,48 @@ - (NSURLSessionDataTask *)DELETE:(NSString *)URLString success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"DELETE" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *, id))success + failure:(void (^)(NSURLSessionDataTask *, NSError *))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; + if (serializationError) { + if (failure) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); +#pragma clang diagnostic pop + } - __block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { + return nil; + } + + __block NSURLSessionDataTask *dataTask = nil; + dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { if (error) { if (failure) { - failure(task, error); + failure(dataTask, error); } } else { if (success) { - success(task, responseObject); + success(dataTask, responseObject); } } }]; - [task resume]; - - return task; + return dataTask; } #pragma mark - NSObject @@ -279,7 +261,7 @@ - (NSString *)description { return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue]; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding + (BOOL)supportsSecureCoding { return YES; @@ -291,7 +273,7 @@ - (id)initWithCoder:(NSCoder *)decoder { if (!configuration) { NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"]; if (configurationIdentifier) { -#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100) +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100) configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier]; #else configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier]; @@ -330,7 +312,7 @@ - (id)copyWithZone:(NSZone *)zone { HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; - + return HTTPClient; } diff --git a/AFNetworking/AFNetworkReachabilityManager.h b/AFNetworking/AFNetworkReachabilityManager.h index 6320783..4ce2a36 100644 --- a/AFNetworking/AFNetworkReachabilityManager.h +++ b/AFNetworking/AFNetworkReachabilityManager.h @@ -1,6 +1,5 @@ // AFNetworkReachabilityManager.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -8,10 +7,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,11 +22,13 @@ #import #import -#import -#import -#import -#import -#import +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { AFNetworkReachabilityStatusUnknown = -1, @@ -38,11 +39,11 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { /** `AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. - + Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability. See Apple's Reachability Sample Code (https://developer.apple.com/library/ios/samplecode/reachability/) - + @warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined. */ @interface AFNetworkReachabilityManager : NSObject @@ -78,9 +79,9 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { /** Creates and returns a network reachability manager for the specified domain. - + @param domain The domain used to evaluate network reachability. - + @return An initialized network reachability manager, actively monitoring the specified domain. */ + (instancetype)managerForDomain:(NSString *)domain; @@ -88,20 +89,20 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { /** Creates and returns a network reachability manager for the socket address. - @param address The socket address used to evaluate network reachability. + @param address The socket address (`sockaddr_in`) used to evaluate network reachability. @return An initialized network reachability manager, actively monitoring the specified socket address. */ -+ (instancetype)managerForAddress:(const struct sockaddr_in *)address; ++ (instancetype)managerForAddress:(const void *)address; /** Initializes an instance of a network reachability manager from the specified reachability object. - + @param reachability The reachability object to monitor. - + @return An initialized network reachability manager, actively monitoring the specified reachability. */ -- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability; +- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER; ///-------------------------------------------------- /// @name Starting & Stopping Reachability Monitoring diff --git a/AFNetworking/AFNetworkReachabilityManager.m b/AFNetworking/AFNetworkReachabilityManager.m index 71adaef..25e9581 100644 --- a/AFNetworking/AFNetworkReachabilityManager.m +++ b/AFNetworking/AFNetworkReachabilityManager.m @@ -1,6 +1,5 @@ // AFNetworkReachabilityManager.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -8,10 +7,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -22,6 +21,12 @@ #import "AFNetworkReachabilityManager.h" +#import +#import +#import +#import +#import + NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change"; NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem"; @@ -80,9 +85,10 @@ static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused targ dispatch_async(dispatch_get_main_queue(), ^{ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:@{ AFNetworkingReachabilityNotificationStatusItem: @(status) }]; + NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; + [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; }); - + } static const void * AFNetworkReachabilityRetainCallback(const void *info) { @@ -128,7 +134,7 @@ + (instancetype)managerForDomain:(NSString *)domain { return manager; } -+ (instancetype)managerForAddress:(const struct sockaddr_in *)address { ++ (instancetype)managerForAddress:(const void *)address { SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; @@ -208,11 +214,11 @@ - (void)startMonitoring { AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); dispatch_async(dispatch_get_main_queue(), ^{ callback(status); - + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:@{ AFNetworkingReachabilityNotificationStatusItem: @(status) }]; - + }); }); } diff --git a/AFNetworking/AFSecurityPolicy.h b/AFNetworking/AFSecurityPolicy.h index b86e76b..c6cec83 100644 --- a/AFNetworking/AFSecurityPolicy.h +++ b/AFNetworking/AFSecurityPolicy.h @@ -1,6 +1,5 @@ -// AFSecurity.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// AFSecurityPolicy.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -31,7 +30,7 @@ typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { /** `AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. - + Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. */ @interface AFSecurityPolicy : NSObject @@ -39,7 +38,7 @@ typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { /** The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`. */ -@property (nonatomic, assign) AFSSLPinningMode SSLPinningMode; +@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode; /** Whether to evaluate an entire SSL certificate chain, or just the leaf certificate. Defaults to `YES`. @@ -57,7 +56,7 @@ typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { @property (nonatomic, assign) BOOL allowInvalidCertificates; /** - Whether or not to validate the domain name in the certificates CN field. Defaults to `YES` for `AFSSLPinningModePublicKey` or `AFSSLPinningModeCertificate`, otherwise `NO`. + Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`. */ @property (nonatomic, assign) BOOL validatesDomainName; @@ -66,8 +65,8 @@ typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { ///----------------------------------------- /** - Returns the shared default security policy, which does not accept invalid certificates, and does not validate against pinned certificates or public keys. - + Returns the shared default security policy, which does not allow invalid certificates, validates domain name, and does not validate against pinned certificates or public keys. + @return The default security policy. */ + (instancetype)defaultPolicy; @@ -78,9 +77,9 @@ typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { /** Creates and returns a security policy with the specified pinning mode. - + @param pinningMode The SSL pinning mode. - + @return A new security policy. */ + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode; @@ -97,19 +96,19 @@ typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { @param serverTrust The X.509 certificate trust of the server. @return Whether or not to trust the server. - + @warning This method has been deprecated in favor of `-evaluateServerTrust:forDomain:`. */ - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust DEPRECATED_ATTRIBUTE; /** - Whether or not the specified server trust should be accepted, based on the security policy. - + Whether or not the specified server trust should be accepted, based on the security policy. + This method should be used when responding to an authentication challenge from a server. - + @param serverTrust The X.509 certificate trust of the server. @param domain The domain of serverTrust. If `nil`, the domain will not be validated. - + @return Whether or not to trust the server. */ - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust diff --git a/AFNetworking/AFSecurityPolicy.m b/AFNetworking/AFSecurityPolicy.m index f11caaa..2bbff62 100644 --- a/AFNetworking/AFSecurityPolicy.m +++ b/AFNetworking/AFSecurityPolicy.m @@ -1,6 +1,5 @@ -// AFSecurity.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// AFSecurityPolicy.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -22,31 +21,13 @@ #import "AFSecurityPolicy.h" -// Equivalent of macro in , without causing compiler warning: -// "'DebugAssert' is deprecated: first deprecated in OS X 10.8" -#ifndef AF_Require - #define AF_Require(assertion, exceptionLabel) \ - do { \ - if (__builtin_expect(!(assertion), 0)) { \ - goto exceptionLabel; \ - } \ - } while (0) -#endif - -#ifndef AF_Require_noErr - #define AF_Require_noErr(errorCode, exceptionLabel) \ - do { \ - if (__builtin_expect(0 != (errorCode), 0)) { \ - goto exceptionLabel; \ - } \ - } while (0) -#endif +#import #if !defined(__IPHONE_OS_VERSION_MIN_REQUIRED) static NSData * AFSecKeyGetData(SecKeyRef key) { CFDataRef data = NULL; - AF_Require_noErr(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out); + __Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out); return (__bridge_transfer NSData *)data; @@ -77,14 +58,14 @@ static id AFPublicKeyForCertificate(NSData *certificate) { SecTrustResultType result; allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); - AF_Require(allowedCertificate != NULL, _out); + __Require_Quiet(allowedCertificate != NULL, _out); allowedCertificates[0] = allowedCertificate; tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL); policy = SecPolicyCreateBasicX509(); - AF_Require_noErr(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out); - AF_Require_noErr(SecTrustEvaluate(allowedTrust, &result), _out); + __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out); + __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); @@ -111,7 +92,7 @@ static id AFPublicKeyForCertificate(NSData *certificate) { static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { BOOL isValid = NO; SecTrustResultType result; - AF_Require_noErr(SecTrustEvaluate(serverTrust, &result), _out); + __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out); isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); @@ -142,10 +123,10 @@ static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL); SecTrustRef trust; - AF_Require_noErr(SecTrustCreateWithCertificates(certificates, policy, &trust), _out); - + __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out); + SecTrustResultType result; - AF_Require_noErr(SecTrustEvaluate(trust, &result), _out); + __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out); [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; @@ -168,6 +149,7 @@ static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { #pragma mark - @interface AFSecurityPolicy() +@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode; @property (readwrite, nonatomic, strong) NSArray *pinnedPublicKeys; @end @@ -202,7 +184,7 @@ + (instancetype)defaultPolicy { + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode { AFSecurityPolicy *securityPolicy = [[self alloc] init]; securityPolicy.SSLPinningMode = pinningMode; - securityPolicy.validatesDomainName = YES; + [securityPolicy setPinnedCertificates:[self defaultPinnedCertificates]]; return securityPolicy; @@ -215,12 +197,11 @@ - (id)init { } self.validatesCertificateChain = YES; + self.validatesDomainName = YES; return self; } -#pragma mark - - - (void)setPinnedCertificates:(NSArray *)pinnedCertificates { _pinnedCertificates = pinnedCertificates; @@ -257,14 +238,21 @@ - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); - if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { + if (self.SSLPinningMode == AFSSLPinningModeNone) { + if (self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust)){ + return YES; + } else { + return NO; + } + } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { return NO; } NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); switch (self.SSLPinningMode) { case AFSSLPinningModeNone: - return YES; + default: + return NO; case AFSSLPinningModeCertificate: { NSMutableArray *pinnedCertificates = [NSMutableArray array]; for (NSData *certificateData in self.pinnedCertificates) { @@ -307,7 +295,7 @@ - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust return trustedPublicKeyCount > 0 && ((self.validatesCertificateChain && trustedPublicKeyCount == [serverCertificates count]) || (!self.validatesCertificateChain && trustedPublicKeyCount >= 1)); } } - + return NO; } diff --git a/AFNetworking/AFURLConnectionOperation.h b/AFNetworking/AFURLConnectionOperation.h index 8543556..0248e67 100644 --- a/AFNetworking/AFURLConnectionOperation.h +++ b/AFNetworking/AFURLConnectionOperation.h @@ -1,6 +1,5 @@ // AFURLConnectionOperation.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,6 +26,14 @@ #import "AFURLResponseSerialization.h" #import "AFSecurityPolicy.h" +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + /** `AFURLConnectionOperation` is a subclass of `NSOperation` that implements `NSURLConnection` delegate methods. @@ -47,7 +54,7 @@ - `connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:` - `connection:willCacheResponse:` - `connectionShouldUseCredentialStorage:` - - `connection:needNewBodyStream:` + - `connection:needNewBodyStream:` - `connection:willSendRequestForAuthenticationChallenge:` If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first. @@ -57,17 +64,17 @@ The built-in `completionBlock` provided by `NSOperation` allows for custom behavior to be executed after the request finishes. It is a common pattern for class constructors in subclasses to take callback block parameters, and execute them conditionally in the body of its `completionBlock`. Make sure to handle cancelled operations appropriately when setting a `completionBlock` (i.e. returning early before parsing response data). See the implementation of any of the `AFHTTPRequestOperation` subclasses for an example of this. Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as ["The Deallocation Problem"](http://developer.apple.com/library/ios/#technotes/tn2109/). - + ## SSL Pinning - + Relying on the CA trust model to validate SSL certificates exposes your app to security vulnerabilities, such as man-in-the-middle attacks. For applications that connect to known servers, SSL certificate pinning provides an increased level of security, by checking server certificate validity against those specified in the app bundle. - + SSL with certificate pinning is strongly recommended for any application that transmits sensitive information to an external webservice. Connections will be validated on all matching certificates with a `.cer` extension in the bundle root. - + ## App Extensions - + When using AFNetworking in an App Extension, `#define AF_APP_EXTENSIONS` to avoid using unavailable APIs. ## NSCoding & NSCopying Conformance @@ -189,12 +196,20 @@ /** The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used. */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT @property (nonatomic, strong) dispatch_queue_t completionQueue; +#else +@property (nonatomic, assign) dispatch_queue_t completionQueue; +#endif /** The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT @property (nonatomic, strong) dispatch_group_t completionGroup; +#else +@property (nonatomic, assign) dispatch_group_t completionGroup; +#endif ///--------------------------------------------- /// @name Managing Request Operation Information @@ -211,12 +226,12 @@ /** Initializes and returns a newly allocated operation object with a url connection configured with the specified url request. - + This is the designated initializer. - + @param urlRequest The request object to be used by the operation connection. */ -- (instancetype)initWithRequest:(NSURLRequest *)urlRequest; +- (instancetype)initWithRequest:(NSURLRequest *)urlRequest NS_DESIGNATED_INITIALIZER; ///---------------------------------- /// @name Pausing / Resuming Requests @@ -280,9 +295,9 @@ /** Sets a block to be executed when the connection will authenticate a challenge in order to download its request, as handled by the `NSURLConnectionDelegate` method `connection:willSendRequestForAuthenticationChallenge:`. - + @param block A block object to be executed when the connection will authenticate a challenge in order to download its request. The block has no return type and takes two arguments: the URL connection object, and the challenge that must be authenticated. This block must invoke one of the challenge-responder methods (NSURLAuthenticationChallengeSender protocol). - + If `allowsInvalidSSLCertificate` is set to YES, `connection:willSendRequestForAuthenticationChallenge:` will attempt to have the challenge sender use credentials with invalid SSL certificates. */ - (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block; diff --git a/AFNetworking/AFURLConnectionOperation.m b/AFNetworking/AFURLConnectionOperation.m index e5e6839..8d32092 100644 --- a/AFNetworking/AFURLConnectionOperation.m +++ b/AFNetworking/AFURLConnectionOperation.m @@ -1,6 +1,5 @@ // AFURLConnectionOperation.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -177,7 +176,7 @@ + (NSThread *)networkRequestThread { _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; [_networkRequestThread start]; }); - + return _networkRequestThread; } @@ -193,11 +192,11 @@ - (instancetype)initWithRequest:(NSURLRequest *)urlRequest { self.lock = [[NSRecursiveLock alloc] init]; self.lock.name = kAFNetworkingLockName; - + self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes]; - + self.request = urlRequest; - + self.shouldUseCredentialStorage = YES; self.securityPolicy = [AFSecurityPolicy defaultPolicy]; @@ -210,7 +209,7 @@ - (void)dealloc { [_outputStream close]; _outputStream = nil; } - + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS) if (_backgroundTaskIdentifier) { [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier]; @@ -297,14 +296,14 @@ - (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))ha __weak __typeof(self)weakSelf = self; self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{ __strong __typeof(weakSelf)strongSelf = weakSelf; - + if (handler) { handler(); } - + if (strongSelf) { [strongSelf cancel]; - + [application endBackgroundTask:strongSelf.backgroundTaskIdentifier]; strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid; } @@ -320,11 +319,11 @@ - (void)setState:(AFOperationState)state { if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) { return; } - + [self.lock lock]; NSString *oldStateKey = AFKeyPathFromOperationState(self.state); NSString *newStateKey = AFKeyPathFromOperationState(state); - + [self willChangeValueForKey:newStateKey]; [self willChangeValueForKey:oldStateKey]; _state = state; @@ -337,20 +336,18 @@ - (void)pause { if ([self isPaused] || [self isFinished] || [self isCancelled]) { return; } - + [self.lock lock]; - if ([self isExecuting]) { [self performSelector:@selector(operationDidPause) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; - + dispatch_async(dispatch_get_main_queue(), ^{ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self]; }); } - + self.state = AFOperationPausedState; - [self.lock unlock]; } @@ -368,10 +365,10 @@ - (void)resume { if (![self isPaused]) { return; } - + [self.lock lock]; self.state = AFOperationReadyState; - + [self start]; [self.lock unlock]; } @@ -449,7 +446,7 @@ - (void)start { [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } else if ([self isReady]) { self.state = AFOperationExecutingState; - + [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } [self.lock unlock]; @@ -459,17 +456,18 @@ - (void)operationDidStart { [self.lock lock]; if (![self isCancelled]) { self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; - + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; for (NSString *runLoopMode in self.runLoopModes) { [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode]; [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode]; } - + + [self.outputStream open]; [self.connection start]; } [self.lock unlock]; - + dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self]; }); @@ -578,7 +576,10 @@ + (NSArray *)batchOfRequestOperations:(NSArray *)operations #pragma mark - NSObject - (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p, state: %@, cancelled: %@ request: %@, response: %@>", NSStringFromClass([self class]), self, AFKeyPathFromOperationState(self.state), ([self isCancelled] ? @"YES" : @"NO"), self.request, self.response]; + [self.lock lock]; + NSString *description = [NSString stringWithFormat:@"<%@: %p, state: %@, cancelled: %@ request: %@, response: %@>", NSStringFromClass([self class]), self, AFKeyPathFromOperationState(self.state), ([self isCancelled] ? @"YES" : @"NO"), self.request, self.response]; + [self.lock unlock]; + return description; } #pragma mark - NSURLConnectionDelegate @@ -642,8 +643,6 @@ - (void)connection:(NSURLConnection __unused *)connection didReceiveResponse:(NSURLResponse *)response { self.response = response; - - [self.outputStream open]; } - (void)connection:(NSURLConnection __unused *)connection @@ -661,13 +660,13 @@ - (void)connection:(NSURLConnection __unused *)connection if (numberOfBytesWritten == -1) { break; } - + totalNumberOfBytesWritten += numberOfBytesWritten; } break; } - + if (self.outputStream.streamError) { [self.connection cancel]; [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError]; @@ -721,12 +720,12 @@ - (NSCachedURLResponse *)connection:(NSURLConnection *)connection if ([self isCancelled]) { return nil; } - + return cachedResponse; } } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding + (BOOL)supportsSecureCoding { return YES; @@ -734,7 +733,7 @@ + (BOOL)supportsSecureCoding { - (id)initWithCoder:(NSCoder *)decoder { NSURLRequest *request = [decoder decodeObjectOfClass:[NSURLRequest class] forKey:NSStringFromSelector(@selector(request))]; - + self = [self initWithRequest:request]; if (!self) { return nil; @@ -751,9 +750,9 @@ - (id)initWithCoder:(NSCoder *)decoder { - (void)encodeWithCoder:(NSCoder *)coder { [self pause]; - + [coder encodeObject:self.request forKey:NSStringFromSelector(@selector(request))]; - + switch (self.state) { case AFOperationExecutingState: case AFOperationPausedState: @@ -763,7 +762,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeInteger:self.state forKey:NSStringFromSelector(@selector(state))]; break; } - + [coder encodeObject:self.response forKey:NSStringFromSelector(@selector(response))]; [coder encodeObject:self.error forKey:NSStringFromSelector(@selector(error))]; [coder encodeObject:self.responseData forKey:NSStringFromSelector(@selector(responseData))]; @@ -774,7 +773,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { - (id)copyWithZone:(NSZone *)zone { AFURLConnectionOperation *operation = [(AFURLConnectionOperation *)[[self class] allocWithZone:zone] initWithRequest:self.request]; - + operation.uploadProgress = self.uploadProgress; operation.downloadProgress = self.downloadProgress; operation.authenticationChallenge = self.authenticationChallenge; @@ -782,7 +781,7 @@ - (id)copyWithZone:(NSZone *)zone { operation.redirectResponse = self.redirectResponse; operation.completionQueue = self.completionQueue; operation.completionGroup = self.completionGroup; - + return operation; } diff --git a/AFNetworking/AFURLRequestSerialization.h b/AFNetworking/AFURLRequestSerialization.h index 161b551..bb8d444 100644 --- a/AFNetworking/AFURLRequestSerialization.h +++ b/AFNetworking/AFURLRequestSerialization.h @@ -1,6 +1,5 @@ -// AFSerialization.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// AFURLRequestSerialization.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -8,10 +7,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -72,42 +71,42 @@ typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) { /** Whether created requests can use the device’s cellular radio (if present). `YES` by default. - + @see NSMutableURLRequest -setAllowsCellularAccess: */ @property (nonatomic, assign) BOOL allowsCellularAccess; /** The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default. - + @see NSMutableURLRequest -setCachePolicy: */ @property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy; /** Whether created requests should use the default cookie handling. `YES` by default. - + @see NSMutableURLRequest -setHTTPShouldHandleCookies: */ @property (nonatomic, assign) BOOL HTTPShouldHandleCookies; /** Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default - + @see NSMutableURLRequest -setHTTPShouldUsePipelining: */ @property (nonatomic, assign) BOOL HTTPShouldUsePipelining; /** The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default. - + @see NSMutableURLRequest -setNetworkServiceType: */ @property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType; /** The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds. - + @see NSMutableURLRequest -setTimeoutInterval: */ @property (nonatomic, assign) NSTimeInterval timeoutInterval; @@ -117,7 +116,12 @@ typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) { ///--------------------------------------- /** - Default HTTP header field values to be applied to serialized requests. + Default HTTP header field values to be applied to serialized requests. By default, these include the following: + + - `Accept-Language` with the contents of `NSLocale +preferredLanguages` + - `User-Agent` with the contents of various bundle identifiers and OS designations + + @discussion To add or remove default request headers, use `setValue:forHTTPHeaderField:`. */ @property (readonly, nonatomic, strong) NSDictionary *HTTPRequestHeaders; @@ -135,6 +139,15 @@ typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) { - (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field; +/** + Returns the value for the HTTP headers set in the request serializer. + + @param field The HTTP header to retrieve the default value for + + @return The value set as default for the specified header, or `nil` + */ +- (NSString *)valueForHTTPHeaderField:(NSString *)field; + /** Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a basic authentication value with Base64-encoded username and password. This overwrites any existing value for this header. @@ -178,7 +191,7 @@ forHTTPHeaderField:(NSString *)field; @param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request. */ -- (void)setQueryStringSerializationWithBlock:(NSString * (^)(NSURLRequest *request, NSDictionary *parameters, NSError * __autoreleasing *error))block; +- (void)setQueryStringSerializationWithBlock:(NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block; ///------------------------------- /// @name Creating Request Objects @@ -237,11 +250,11 @@ forHTTPHeaderField:(NSString *)field; /** Creates an `NSMutableURLRequest` by removing the `HTTPBodyStream` from a request, and asynchronously writing its contents into the specified file, invoking the completion handler when finished. - - @param request The multipart form request. + + @param request The multipart form request. The `HTTPBodyStream` property of `request` must not be `nil`. @param fileURL The file URL to write multipart form contents to. @param handler A handler block to execute. - + @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request. @see https://github.com/AFNetworking/AFNetworking/issues/1398 @@ -334,7 +347,7 @@ forHTTPHeaderField:(NSString *)field; Appends HTTP headers, followed by the encoded data and the multipart form boundary. @param headers The HTTP headers to be appended to the form data. - @param body The data to be encoded and appended to the form data. + @param body The data to be encoded and appended to the form data. This parameter must not be `nil`. */ - (void)appendPartWithHeaders:(NSDictionary *)headers body:(NSData *)body; @@ -354,6 +367,9 @@ forHTTPHeaderField:(NSString *)field; #pragma mark - +/** + `AFJSONRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`. + */ @interface AFJSONRequestSerializer : AFHTTPRequestSerializer /** @@ -370,6 +386,11 @@ forHTTPHeaderField:(NSString *)field; @end +#pragma mark - + +/** + `AFPropertyListRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`. + */ @interface AFPropertyListRequestSerializer : AFHTTPRequestSerializer /** @@ -387,7 +408,7 @@ forHTTPHeaderField:(NSString *)field; @param format The property list format. @param writeOptions The property list write options. - + @warning The `writeOptions` property is currently unused. */ + (instancetype)serializerWithFormat:(NSPropertyListFormat)format @@ -395,6 +416,8 @@ forHTTPHeaderField:(NSString *)field; @end +#pragma mark - + ///---------------- /// @name Constants ///---------------- @@ -418,7 +441,7 @@ extern NSString * const AFURLRequestSerializationErrorDomain; These keys may exist in the user info dictionary, in addition to those defined for NSError. - - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey` + - `NSString * const AFNetworkingOperationFailingURLRequestErrorKey` ### Constants diff --git a/AFNetworking/AFURLRequestSerialization.m b/AFNetworking/AFURLRequestSerialization.m index 45df7a0..5b55b5f 100644 --- a/AFNetworking/AFURLRequestSerialization.m +++ b/AFNetworking/AFURLRequestSerialization.m @@ -1,6 +1,5 @@ -// AFSerialization.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// AFURLRequestSerialization.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -8,10 +7,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -31,7 +30,7 @@ NSString * const AFURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request"; NSString * const AFNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response"; -typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, NSDictionary *parameters, NSError *__autoreleasing *error); +typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error); static NSString * AFBase64EncodedStringFromString(NSString *string) { NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; @@ -154,7 +153,7 @@ - (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding } else { [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]]; } - + return mutableQueryStringComponents; } @@ -169,7 +168,20 @@ - (NSMutableURLRequest *)requestByFinalizingMultipartFormData; #pragma mark - +static NSArray * AFHTTPRequestSerializerObservedKeyPaths() { + static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))]; + }); + + return _AFHTTPRequestSerializerObservedKeyPaths; +} + +static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext; + @interface AFHTTPRequestSerializer () +@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths; @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders; @property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle; @property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization; @@ -188,12 +200,6 @@ - (instancetype)init { } self.stringEncoding = NSUTF8StringEncoding; - self.allowsCellularAccess = YES; - self.cachePolicy = NSURLRequestUseProtocolCachePolicy; - self.HTTPShouldHandleCookies = YES; - self.HTTPShouldUsePipelining = NO; - self.networkServiceType = NSURLNetworkServiceTypeDefault; - self.timeoutInterval = 60; self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; @@ -211,7 +217,7 @@ - (instancetype)init { #pragma clang diagnostic ignored "-Wgnu" #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 - userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleIdentifierKey], (__bridge id)CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleVersionKey) ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]]; + userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleIdentifierKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]]; #elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleIdentifierKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]]; #endif @@ -229,20 +235,84 @@ - (instancetype)init { // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil]; + self.mutableObservedChangedKeyPaths = [NSMutableSet set]; + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { + [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext]; + } + } + return self; } +- (void)dealloc { + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { + [self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext]; + } + } +} + +#pragma mark - + +// Workarounds for crashing behavior using Key-Value Observing with XCTest +// See https://github.com/AFNetworking/AFNetworking/issues/2523 + +- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess { + [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))]; + _allowsCellularAccess = allowsCellularAccess; + [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))]; +} + +- (void)setCachePolicy:(NSURLRequestCachePolicy)cachePolicy { + [self willChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))]; + _cachePolicy = cachePolicy; + [self didChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))]; +} + +- (void)setHTTPShouldHandleCookies:(BOOL)HTTPShouldHandleCookies { + [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; + _HTTPShouldHandleCookies = HTTPShouldHandleCookies; + [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; +} + +- (void)setHTTPShouldUsePipelining:(BOOL)HTTPShouldUsePipelining { + [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))]; + _HTTPShouldUsePipelining = HTTPShouldUsePipelining; + [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))]; +} + +- (void)setNetworkServiceType:(NSURLRequestNetworkServiceType)networkServiceType { + [self willChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))]; + _networkServiceType = networkServiceType; + [self didChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))]; +} + +- (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval { + [self willChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))]; + _timeoutInterval = timeoutInterval; + [self didChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))]; +} + #pragma mark - - (NSDictionary *)HTTPRequestHeaders { return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; } -- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field { +- (void)setValue:(NSString *)value +forHTTPHeaderField:(NSString *)field +{ [self.mutableHTTPRequestHeaders setValue:value forKey:field]; } -- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username password:(NSString *)password { +- (NSString *)valueForHTTPHeaderField:(NSString *)field { + return [self.mutableHTTPRequestHeaders valueForKey:field]; +} + +- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username + password:(NSString *)password +{ NSString *basicAuthCredentials = [NSString stringWithFormat:@"%@:%@", username, password]; [self setValue:[NSString stringWithFormat:@"Basic %@", AFBase64EncodedStringFromString(basicAuthCredentials)] forHTTPHeaderField:@"Authorization"]; } @@ -262,7 +332,7 @@ - (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializat self.queryStringSerialization = nil; } -- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, NSDictionary *, NSError *__autoreleasing *))block { +- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, NSError *__autoreleasing *))block { self.queryStringSerialization = block; } @@ -289,12 +359,12 @@ - (NSMutableURLRequest *)requestWithMethod:(NSString *)method NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url]; mutableRequest.HTTPMethod = method; - mutableRequest.allowsCellularAccess = self.allowsCellularAccess; - mutableRequest.cachePolicy = self.cachePolicy; - mutableRequest.HTTPShouldHandleCookies = self.HTTPShouldHandleCookies; - mutableRequest.HTTPShouldUsePipelining = self.HTTPShouldUsePipelining; - mutableRequest.networkServiceType = self.networkServiceType; - mutableRequest.timeoutInterval = self.timeoutInterval; + + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) { + [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath]; + } + } mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy]; @@ -350,10 +420,7 @@ - (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request writingStreamContentsToFile:(NSURL *)fileURL completionHandler:(void (^)(NSError *error))handler { - if (!request.HTTPBodyStream) { - return [request mutableCopy]; - } - + NSParameterAssert(request.HTTPBodyStream); NSParameterAssert([fileURL isFileURL]); NSInputStream *inputStream = request.HTTPBodyStream; @@ -422,7 +489,16 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request if (parameters) { NSString *query = nil; if (self.queryStringSerialization) { - query = self.queryStringSerialization(request, parameters, error); + NSError *serializationError; + query = self.queryStringSerialization(request, parameters, &serializationError); + + if (serializationError) { + if (error) { + *error = serializationError; + } + + return nil; + } } else { switch (self.queryStringSerializationStyle) { case AFHTTPRequestQueryStringDefaultStyle: @@ -435,8 +511,7 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]]; } else { if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { - NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(self.stringEncoding)); - [mutableRequest setValue:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; + [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; } [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; } @@ -445,7 +520,31 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request return mutableRequest; } -#pragma mark - NSecureCoding +#pragma mark - NSKeyValueObserving + ++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { + if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) { + return NO; + } + + return [super automaticallyNotifiesObserversForKey:key]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(__unused id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (context == AFHTTPRequestSerializerObserverContext) { + if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) { + [self.mutableObservedChangedKeyPaths removeObject:keyPath]; + } else { + [self.mutableObservedChangedKeyPaths addObject:keyPath]; + } + } +} + +#pragma mark - NSSecureCoding + (BOOL)supportsSecureCoding { return YES; @@ -475,7 +574,7 @@ - (id)copyWithZone:(NSZone *)zone { serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone]; serializer.queryStringSerializationStyle = self.queryStringSerializationStyle; serializer.queryStringSerialization = self.queryStringSerialization; - + return serializer; } @@ -620,11 +719,11 @@ - (BOOL)appendPartWithFileURL:(NSURL *)fileURL if (!fileAttributes) { return NO; } - + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; - + AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; bodyPart.stringEncoding = self.stringEncoding; bodyPart.headers = mutableHeaders; @@ -650,7 +749,6 @@ - (void)appendPartWithInputStream:(NSInputStream *)inputStream [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; - AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; bodyPart.stringEncoding = self.stringEncoding; bodyPart.headers = mutableHeaders; @@ -747,6 +845,9 @@ @interface AFMultipartBodyStream () @implementation AFMultipartBodyStream #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wimplicit-atomic-properties" +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100) +@synthesize delegate; +#endif @synthesize streamStatus; @synthesize streamError; #pragma clang diagnostic pop @@ -957,6 +1058,8 @@ - (NSInputStream *)inputStream { _inputStream = [NSInputStream inputStreamWithURL:self.body]; } else if ([self.body isKindOfClass:[NSInputStream class]]) { _inputStream = self.body; + } else { + _inputStream = [NSInputStream inputStreamWithData:[NSData data]]; } } @@ -1073,7 +1176,9 @@ - (NSInteger)readData:(NSData *)data - (BOOL)transitionToNextPhase { if (![[NSThread currentThread] isMainThread]) { - [self performSelectorOnMainThread:@selector(transitionToNextPhase) withObject:nil waitUntilDone:YES]; + dispatch_sync(dispatch_get_main_queue(), ^{ + [self transitionToNextPhase]; + }); return YES; } @@ -1107,13 +1212,13 @@ - (BOOL)transitionToNextPhase { - (id)copyWithZone:(NSZone *)zone { AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init]; - + bodyPart.stringEncoding = self.stringEncoding; bodyPart.headers = self.headers; bodyPart.bodyContentLength = self.bodyContentLength; bodyPart.body = self.body; bodyPart.boundary = self.boundary; - + return bodyPart; } @@ -1124,7 +1229,7 @@ - (id)copyWithZone:(NSZone *)zone { @implementation AFJSONRequestSerializer + (instancetype)serializer { - return [self serializerWithWritingOptions:0]; + return [self serializerWithWritingOptions:(NSJSONWritingOptions)0]; } + (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions @@ -1154,11 +1259,10 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request [mutableRequest setValue:value forHTTPHeaderField:field]; } }]; - + if (parameters) { if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { - NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); - [mutableRequest setValue:[NSString stringWithFormat:@"application/json; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; + [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; } [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]]; @@ -1167,7 +1271,7 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request return mutableRequest; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; @@ -1237,8 +1341,7 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request if (parameters) { if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { - NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); - [mutableRequest setValue:[NSString stringWithFormat:@"application/x-plist; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; + [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"]; } [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]]; @@ -1247,7 +1350,7 @@ - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request return mutableRequest; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; diff --git a/AFNetworking/AFURLResponseSerialization.h b/AFNetworking/AFURLResponseSerialization.h index 36ed33e..9cd4ad2 100644 --- a/AFNetworking/AFURLResponseSerialization.h +++ b/AFNetworking/AFURLResponseSerialization.h @@ -1,6 +1,5 @@ -// AFSerialization.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// AFURLResponseSerialization.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -8,10 +7,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -54,8 +53,10 @@ */ @interface AFHTTPResponseSerializer : NSObject +- (instancetype) init; + /** - The string encoding used to serialize parameters. + The string encoding used to serialize data received from the server, when no string encoding is specified by the response. `NSUTF8StringEncoding` by default. */ @property (nonatomic, assign) NSStringEncoding stringEncoding; @@ -111,6 +112,8 @@ */ @interface AFJSONResponseSerializer : AFHTTPResponseSerializer +- (instancetype) init; + /** Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. */ @@ -133,9 +136,9 @@ #pragma mark - /** - `AFXMLParserSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. + `AFXMLParserResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. - By default, `AFXMLParserSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: + By default, `AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: - `application/xml` - `text/xml` @@ -149,15 +152,17 @@ #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED /** - `AFXMLDocumentSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. + `AFXMLDocumentResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. - By default, `AFXMLDocumentSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: + By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: - `application/xml` - `text/xml` */ @interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer +- (instancetype) init; + /** Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. */ @@ -177,14 +182,16 @@ #pragma mark - /** - `AFPropertyListSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. + `AFPropertyListResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. - By default, `AFPropertyListSerializer` accepts the following MIME types: + By default, `AFPropertyListResponseSerializer` accepts the following MIME types: - `application/x-plist` */ @interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer +- (instancetype) init; + /** The property list format. Possible values are described in "NSPropertyListFormat". */ @@ -209,9 +216,9 @@ #pragma mark - /** - `AFImageSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses. + `AFImageResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses. - By default, `AFImageSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: + By default, `AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: - `image/tiff` - `image/jpeg` @@ -285,10 +292,18 @@ extern NSString * const AFURLResponseSerializationErrorDomain; These keys may exist in the user info dictionary, in addition to those defined for NSError. - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey` + - `NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey` ### Constants `AFNetworkingOperationFailingURLResponseErrorKey` The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. + + `AFNetworkingOperationFailingURLResponseDataErrorKey` + The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. */ extern NSString * const AFNetworkingOperationFailingURLResponseErrorKey; + +extern NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey; + + diff --git a/AFNetworking/AFURLResponseSerialization.m b/AFNetworking/AFURLResponseSerialization.m index cc1e00b..a672d20 100644 --- a/AFNetworking/AFURLResponseSerialization.m +++ b/AFNetworking/AFURLResponseSerialization.m @@ -1,6 +1,5 @@ -// AFSerialization.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// AFURLResponseSerialization.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -8,10 +7,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,6 +29,7 @@ NSString * const AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response"; NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response"; +NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data"; static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) { if (!error) { @@ -112,27 +112,34 @@ - (BOOL)validateResponse:(NSHTTPURLResponse *)response if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) { if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) { - if ([data length] > 0) { - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]], - NSURLErrorFailingURLErrorKey:[response URL], - AFNetworkingOperationFailingURLResponseErrorKey: response - }; + if ([data length] > 0 && [response URL]) { + NSMutableDictionary *mutableUserInfo = [@{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]], + NSURLErrorFailingURLErrorKey:[response URL], + AFNetworkingOperationFailingURLResponseErrorKey: response, + } mutableCopy]; + if (data) { + mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; + } - validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo], validationError); + validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError); } responseIsValid = NO; } - if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode]) { - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode], - NSURLErrorFailingURLErrorKey:[response URL], - AFNetworkingOperationFailingURLResponseErrorKey: response - }; + if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) { + NSMutableDictionary *mutableUserInfo = [@{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode], + NSURLErrorFailingURLErrorKey:[response URL], + AFNetworkingOperationFailingURLResponseErrorKey: response, + } mutableCopy]; + + if (data) { + mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; + } - validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo], validationError); + validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError); responseIsValid = NO; } @@ -156,7 +163,7 @@ - (id)responseObjectForResponse:(NSURLResponse *)response return data; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding + (BOOL)supportsSecureCoding { return YES; @@ -196,7 +203,7 @@ - (id)copyWithZone:(NSZone *)zone { @implementation AFJSONResponseSerializer + (instancetype)serializer { - return [self serializerWithReadingOptions:0]; + return [self serializerWithReadingOptions:(NSJSONReadingOptions)0]; } + (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions { @@ -256,8 +263,8 @@ - (id)responseObjectForResponse:(NSURLResponse *)response } } else { NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Data failed decoding as a UTF-8 string", nil, @"AFNetworking"), - NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Could not decode string: %@", nil, @"AFNetworking"), responseString] + NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Data failed decoding as a UTF-8 string", @"AFNetworking", nil), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Could not decode string: %@", @"AFNetworking", nil), responseString] }; serializationError = [NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo]; @@ -268,15 +275,15 @@ - (id)responseObjectForResponse:(NSURLResponse *)response if (self.removesKeysWithNullValues && responseObject) { responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); } - + if (error) { - *error = AFErrorWithUnderlyingError(serializationError, *error);; + *error = AFErrorWithUnderlyingError(serializationError, *error); } - + return responseObject; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; @@ -285,6 +292,7 @@ - (id)initWithCoder:(NSCoder *)decoder { } self.readingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readingOptions))] unsignedIntegerValue]; + self.removesKeysWithNullValues = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))] boolValue]; return self; } @@ -293,6 +301,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeObject:@(self.readingOptions) forKey:NSStringFromSelector(@selector(readingOptions))]; + [coder encodeObject:@(self.removesKeysWithNullValues) forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))]; } #pragma mark - NSCopying @@ -300,6 +309,7 @@ - (void)encodeWithCoder:(NSCoder *)coder { - (id)copyWithZone:(NSZone *)zone { AFJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; serializer.readingOptions = self.readingOptions; + serializer.removesKeysWithNullValues = self.removesKeysWithNullValues; return serializer; } @@ -394,7 +404,7 @@ - (id)responseObjectForResponse:(NSURLResponse *)response return document; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; @@ -481,7 +491,7 @@ - (id)responseObjectForResponse:(NSURLResponse *)response return responseObject; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; @@ -521,6 +531,9 @@ - (id)copyWithZone:(NSZone *)zone { static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) { UIImage *image = [[UIImage alloc] initWithData:data]; + if (image.images) { + return image; + } return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation]; } @@ -538,10 +551,11 @@ - (id)copyWithZone:(NSZone *)zone { } else if ([response.MIMEType isEqualToString:@"image/jpeg"]) { imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); - // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so if so, fall back to AFImageWithDataAtScale if (imageRef) { CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef); CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace); + + // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale if (imageColorSpaceModel == kCGColorSpaceModelCMYK) { CGImageRelease(imageRef); imageRef = NULL; @@ -573,13 +587,16 @@ - (id)copyWithZone:(NSZone *)zone { return image; } - size_t bytesPerRow = 0; // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate + // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate + size_t bytesPerRow = 0; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); if (colorSpaceModel == kCGColorSpaceModelRGB) { uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wassign-enum" if (alpha == kCGImageAlphaNone) { bitmapInfo &= ~kCGBitmapAlphaInfoMask; bitmapInfo |= kCGImageAlphaNoneSkipFirst; @@ -587,6 +604,7 @@ - (id)copyWithZone:(NSZone *)zone { bitmapInfo &= ~kCGBitmapAlphaInfoMask; bitmapInfo |= kCGImageAlphaPremultipliedFirst; } +#pragma clang diagnostic pop } CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo); @@ -608,7 +626,7 @@ - (id)copyWithZone:(NSZone *)zone { CGImageRelease(inflatedImageRef); CGImageRelease(imageRef); - + return inflatedImage; } #endif @@ -662,7 +680,7 @@ - (id)responseObjectForResponse:(NSURLResponse *)response return nil; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; @@ -744,11 +762,11 @@ - (id)responseObjectForResponse:(NSURLResponse *)response return responseObject; } } - + return [super responseObjectForResponse:response data:data error:error]; } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding - (id)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; diff --git a/AFNetworking/AFURLSessionManager.h b/AFNetworking/AFURLSessionManager.h index 6432027..21d9d64 100644 --- a/AFNetworking/AFURLSessionManager.h +++ b/AFNetworking/AFURLSessionManager.h @@ -1,6 +1,5 @@ // AFURLSessionManager.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -8,10 +7,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,57 +26,67 @@ #import "AFSecurityPolicy.h" #import "AFNetworkReachabilityManager.h" +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + /** `AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. - + ## Subclassing Notes - + This is the base class for `AFHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `AFURLSessionManager` specifically for HTTP, consider subclassing `AFHTTPSessionManager` instead. - + ## NSURLSession & NSURLSessionTask Delegate Methods - + `AFURLSessionManager` implements the following delegate methods: - + ### `NSURLSessionDelegate` - + - `URLSession:didBecomeInvalidWithError:` - `URLSession:didReceiveChallenge:completionHandler:` + - `URLSessionDidFinishEventsForBackgroundURLSession:` ### `NSURLSessionTaskDelegate` - + - `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:` - `URLSession:task:didReceiveChallenge:completionHandler:` - `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:` - `URLSession:task:didCompleteWithError:` ### `NSURLSessionDataDelegate` - + - `URLSession:dataTask:didReceiveResponse:completionHandler:` - `URLSession:dataTask:didBecomeDownloadTask:` - `URLSession:dataTask:didReceiveData:` - `URLSession:dataTask:willCacheResponse:completionHandler:` - - `URLSessionDidFinishEventsForBackgroundURLSession:` ### `NSURLSessionDownloadDelegate` - `URLSession:downloadTask:didFinishDownloadingToURL:` - `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:` - `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:` - + If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first. - + ## Network Reachability Monitoring Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. - + ## NSCoding Caveats - + - Encoded managers do not include any block properties. Be sure to set delegate callback blocks when using `-initWithCoder:` or `NSKeyedUnarchiver`. ## NSCopying Caveats - `-copy` and `-copyWithZone:` return a new manager with a new `NSURLSession` created from the configuration of the original. - Operation copies do not include any delegate callback blocks, as they often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ session manager when copied. + + @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance. */ #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) @@ -150,12 +159,20 @@ /** The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used. */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT @property (nonatomic, strong) dispatch_queue_t completionQueue; +#else +@property (nonatomic, assign) dispatch_queue_t completionQueue; +#endif /** The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT @property (nonatomic, strong) dispatch_group_t completionGroup; +#else +@property (nonatomic, assign) dispatch_group_t completionGroup; +#endif ///--------------------------------- /// @name Working Around System Bugs @@ -176,16 +193,16 @@ /** Creates and returns a manager for a session created with the specified configuration. This is the designated initializer. - + @param configuration The configuration used to create the managed session. - + @return A manager for a newly-created session. */ -- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration; +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; /** Invalidates the managed session, optionally canceling pending tasks. - + @param cancelPendingTasks Whether or not to cancel pending tasks. */ - (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks; @@ -214,7 +231,7 @@ @param fileURL A URL to the local file to be uploaded. @param progress A progress object monitoring the current upload progress. @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` */ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request @@ -257,7 +274,7 @@ @param progress A progress object monitoring the current download progress. @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. - + @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 @@ -293,9 +310,9 @@ /** Returns the download progress of the specified task. - + @param downloadTask The session download task. Must not be `nil`. - + @return An `NSProgress` object reporting the download progress of a task, or `nil` if the progress is unavailable. */ - (NSProgress *)downloadProgressForTask:(NSURLSessionDownloadTask *)downloadTask; @@ -306,7 +323,7 @@ /** Sets a block to be executed when the managed session becomes invalid, as handled by the `NSURLSessionDelegate` method `URLSession:didBecomeInvalidWithError:`. - + @param block A block object to be executed when the managed session becomes invalid. The block has no return value, and takes two arguments: the session, and the error related to the cause of invalidation. */ - (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block; @@ -331,28 +348,28 @@ /** Sets a block to be executed when an HTTP request is attempting to perform a redirection to a different URL, as handled by the `NSURLSessionTaskDelegate` method `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`. - + @param block A block object to be executed when an HTTP request is attempting to perform a redirection to a different URL. The block returns the request to be made for the redirection, and takes four arguments: the session, the task, the redirection response, and the request corresponding to the redirection response. */ - (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block; /** Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`. - + @param block A block object to be executed when a session task has received a request specific authentication challenge. The block returns the disposition of the authentication challenge, and takes four arguments: the session, the task, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge. */ - (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block; /** Sets a block to be executed periodically to track upload progress, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`. - + @param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes five arguments: the session, the task, the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. */ - (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block; /** Sets a block to be executed as the last message related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didCompleteWithError:`. - + @param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any error that occurred in the process of executing the task. */ - (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block; @@ -370,28 +387,28 @@ /** Sets a block to be executed when a data task has become a download task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didBecomeDownloadTask:`. - + @param block A block object to be executed when a data task has become a download task. The block has no return value, and takes three arguments: the session, the data task, and the download task it has become. */ - (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block; /** Sets a block to be executed when a data task receives data, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveData:`. - + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the session, the data task, and the data received. This block may be called multiple times, and will execute on the session manager operation queue. */ - (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block; /** Sets a block to be executed to determine the caching behavior of a data task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:willCacheResponse:completionHandler:`. - + @param block A block object to be executed to determine the caching behavior of a data task. The block returns the response to cache, and takes three arguments: the session, the data task, and the proposed cached URL response. */ - (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block; /** Sets a block to be executed once all messages enqueued for a session have been delivered, as handled by the `NSURLSessionDataDelegate` method `URLSessionDidFinishEventsForBackgroundURLSession:`. - + @param block A block object to be executed once all messages enqueued for a session have been delivered. The block has no return value and takes a single argument: the session. */ - (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block; @@ -402,21 +419,21 @@ /** Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`. - + @param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error. */ - (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block; /** Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`. - + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the session, the download task, the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the session manager operation queue. */ - (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block; /** Sets a block to be executed when a download task has been resumed, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`. - + @param block A block object to be executed when a download task has been resumed. The block has no return value and takes four arguments: the session, the download task, the file offset of the resumed download, and the total number of bytes expected to be downloaded. */ - (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block; @@ -431,7 +448,7 @@ /** Posted when a task begins executing. - + @deprecated Use `AFNetworkingTaskDidResumeNotification` instead. */ extern NSString * const AFNetworkingTaskDidStartNotification DEPRECATED_ATTRIBUTE; @@ -443,7 +460,7 @@ extern NSString * const AFNetworkingTaskDidResumeNotification; /** Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task. - + @deprecated Use `AFNetworkingTaskDidCompleteNotification` instead. */ extern NSString * const AFNetworkingTaskDidFinishNotification DEPRECATED_ATTRIBUTE; @@ -470,7 +487,7 @@ extern NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification; /** The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if response data exists for the task. - + @deprecated Use `AFNetworkingTaskDidCompleteResponseDataKey` instead. */ extern NSString * const AFNetworkingTaskDidFinishResponseDataKey DEPRECATED_ATTRIBUTE; @@ -482,7 +499,7 @@ extern NSString * const AFNetworkingTaskDidCompleteResponseDataKey; /** The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the response was serialized. - + @deprecated Use `AFNetworkingTaskDidCompleteSerializedResponseKey` instead. */ extern NSString * const AFNetworkingTaskDidFinishSerializedResponseKey DEPRECATED_ATTRIBUTE; @@ -494,7 +511,7 @@ extern NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey; /** The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the task has an associated response serializer. - + @deprecated Use `AFNetworkingTaskDidCompleteResponseSerializerKey` instead. */ extern NSString * const AFNetworkingTaskDidFinishResponseSerializerKey DEPRECATED_ATTRIBUTE; @@ -506,7 +523,7 @@ extern NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey; /** The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an the response data has been stored directly to disk. - + @deprecated Use `AFNetworkingTaskDidCompleteAssetPathKey` instead. */ extern NSString * const AFNetworkingTaskDidFinishAssetPathKey DEPRECATED_ATTRIBUTE; @@ -518,7 +535,7 @@ extern NSString * const AFNetworkingTaskDidCompleteAssetPathKey; /** Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an error exists. - + @deprecated Use `AFNetworkingTaskDidCompleteErrorKey` instead. */ extern NSString * const AFNetworkingTaskDidFinishErrorKey DEPRECATED_ATTRIBUTE; diff --git a/AFNetworking/AFURLSessionManager.m b/AFNetworking/AFURLSessionManager.m index 194cdfa..5cec1b7 100644 --- a/AFNetworking/AFURLSessionManager.m +++ b/AFNetworking/AFURLSessionManager.m @@ -1,6 +1,5 @@ // AFURLSessionManager.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -8,10 +7,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,9 +20,20 @@ // THE SOFTWARE. #import "AFURLSessionManager.h" +#import #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) +static dispatch_queue_t url_session_manager_creation_queue() { + static dispatch_queue_t af_url_session_manager_creation_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL); + }); + + return af_url_session_manager_creation_queue; +} + static dispatch_queue_t url_session_manager_processing_queue() { static dispatch_queue_t af_url_session_manager_processing_queue; static dispatch_once_t onceToken; @@ -67,7 +77,7 @@ static dispatch_group_t url_session_manager_completion_group() { static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock"; -static NSUInteger const AFMaximumNumberOfToRecreateBackgroundSessionUploadTask = 3; +static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3; static void * AFTaskStateChangedContext = &AFTaskStateChangedContext; @@ -76,6 +86,7 @@ static dispatch_group_t url_session_manager_completion_group() { typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request); typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); +typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session); typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task); typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend); @@ -85,7 +96,6 @@ static dispatch_group_t url_session_manager_completion_group() { typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask); typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data); typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse); -typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session); 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); @@ -183,7 +193,7 @@ - (void)URLSession:(__unused NSURLSession *)session if (self.completionHandler) { self.completionHandler(task.response, responseObject, serializationError); } - + dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; }); @@ -245,14 +255,135 @@ - (void)URLSession:(__unused NSURLSession *)session #pragma mark - +/** + * A workaround for issues related to key-value observing the `state` of an `NSURLSessionTask`. + * + * See: + * - https://github.com/AFNetworking/AFNetworking/issues/1477 + * - https://github.com/AFNetworking/AFNetworking/issues/2638 + * - https://github.com/AFNetworking/AFNetworking/pull/2702 + */ + +static inline void af_swizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector) { + Method originalMethod = class_getInstanceMethod(class, originalSelector); + Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); + method_exchangeImplementations(originalMethod, swizzledMethod); +} + +static inline BOOL af_addMethod(Class class, SEL selector, Method method) { + return class_addMethod(class, selector, method_getImplementation(method), method_getTypeEncoding(method)); +} + +static NSString * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume"; +static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend"; + +@interface _AFURLSessionTaskSwizzling : NSObject + +@end + +@implementation _AFURLSessionTaskSwizzling + ++ (void)load { + /** + WARNING: Trouble Ahead + https://github.com/AFNetworking/AFNetworking/pull/2702 + */ + + if (NSClassFromString(@"NSURLSessionTask")) { + /** + iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky. + Many Unit Tests have been built to validate as much of this behavior has possible. + Here is what we know: + - NSURLSessionTasks are implemented with class clusters, meaning the class you request from the API isn't actually the type of class you will get back. + - Simply referencing `[NSURLSessionTask class]` will not work. You need to ask an `NSURLSession` to actually create an object, and grab the class from there. + - On iOS 7, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `__NSCFURLSessionTask`. + - On iOS 8, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `NSURLSessionTask`. + - On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled. + - On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled. + - Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there. + + Some Assumptions: + - No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it. + - No background task classes override `resume` or `suspend` + + The current solution: + 1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task. + 2) Grab a pointer to the original implementation of `af_resume` + 3) Check to see if the current class has an implementation of resume. If so, continue to step 4. + 4) Grab the super class of the current class. + 5) Grab a pointer for the current class to the current implementation of `resume`. + 6) Grab a pointer for the super class to the current implementation of `resume`. + 7) If the current class implementation of `resume` is not equal to the super class implementation of `resume` AND the current implementation of `resume` is not equal to the original implementation of `af_resume`, THEN swizzle the methods + 8) Set the current class to the super class, and repeat steps 3-8 + */ + NSURLSessionDataTask *localDataTask = [[NSURLSession sessionWithConfiguration:nil] dataTaskWithURL:nil]; + IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([_AFURLSessionTaskSwizzling class], @selector(af_resume))); + Class currentClass = [localDataTask class]; + + while (class_getInstanceMethod(currentClass, @selector(resume))) { + Class superClass = [currentClass superclass]; + IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume))); + IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume))); + if (classResumeIMP != superclassResumeIMP && + originalAFResumeIMP != classResumeIMP) { + [self swizzleResumeAndSuspendMethodForClass:currentClass]; + } + currentClass = [currentClass superclass]; + } + + [localDataTask cancel]; + } +} + ++ (void)swizzleResumeAndSuspendMethodForClass:(Class)class { + Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume)); + Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend)); + + af_addMethod(class, @selector(af_resume), afResumeMethod); + af_addMethod(class, @selector(af_suspend), afSuspendMethod); + + af_swizzleSelector(class, @selector(resume), @selector(af_resume)); + af_swizzleSelector(class, @selector(suspend), @selector(af_suspend)); +} + +- (NSURLSessionTaskState)state { + NSAssert(NO, @"State method should never be called in the actual dummy class"); + return NSURLSessionTaskStateCanceling; +} + +- (void)af_resume { + NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); + NSURLSessionTaskState state = [self state]; + [self af_resume]; + + if (state != NSURLSessionTaskStateRunning) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; + } +} + +- (void)af_suspend { + NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); + NSURLSessionTaskState state = [self state]; + [self af_suspend]; + + if (state != NSURLSessionTaskStateSuspended) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self]; + } +} +@end + +#pragma mark - + @interface AFURLSessionManager () @property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration; @property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; @property (readwrite, nonatomic, strong) NSURLSession *session; @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; +@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks; @property (readwrite, nonatomic, strong) NSLock *lock; @property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid; @property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; +@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession; @property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection; @property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge; @property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream; @@ -262,7 +393,6 @@ @interface AFURLSessionManager () @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask; @property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData; @property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse; -@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession; @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData; @property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume; @@ -284,40 +414,76 @@ - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)config configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; } - self.operationQueue = [[NSOperationQueue alloc] init]; - self.operationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; + self.sessionConfiguration = configuration; - self.responseSerializer = [AFJSONResponseSerializer serializer]; + self.operationQueue = [[NSOperationQueue alloc] init]; + self.operationQueue.maxConcurrentOperationCount = 1; - self.sessionConfiguration = configuration; self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; - self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; + self.responseSerializer = [AFJSONResponseSerializer serializer]; self.securityPolicy = [AFSecurityPolicy defaultPolicy]; self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; + self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; + self.lock = [[NSLock alloc] init]; self.lock.name = AFURLSessionManagerLockName; - + [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { for (NSURLSessionDataTask *task in dataTasks) { [self addDelegateForDataTask:task completionHandler:nil]; } - + for (NSURLSessionUploadTask *uploadTask in uploadTasks) { [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil]; } - + for (NSURLSessionDownloadTask *downloadTask in downloadTasks) { [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil]; } }]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:nil]; + return self; } +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - + +- (NSString *)taskDescriptionForSessionTasks { + return [NSString stringWithFormat:@"%p", self]; +} + +- (void)taskDidResume:(NSNotification *)notification { + NSURLSessionTask *task = notification.object; + if ([task respondsToSelector:@selector(taskDescription)]) { + if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task]; + }); + } + } +} + +- (void)taskDidSuspend:(NSNotification *)notification { + NSURLSessionTask *task = notification.object; + if ([task respondsToSelector:@selector(taskDescription)]) { + if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task]; + }); + } + } +} + #pragma mark - - (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task { @@ -338,7 +504,6 @@ - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate NSParameterAssert(delegate); [self.lock lock]; - [task addObserver:self forKeyPath:NSStringFromSelector(@selector(state)) options:NSKeyValueObservingOptionOld |NSKeyValueObservingOptionNew context:AFTaskStateChangedContext]; self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; [self.lock unlock]; } @@ -350,6 +515,7 @@ - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask delegate.manager = self; delegate.completionHandler = completionHandler; + dataTask.taskDescription = self.taskDescriptionForSessionTasks; [self setDelegate:delegate forTask:dataTask]; } @@ -365,11 +531,16 @@ - (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask if(totalUnitCount == NSURLSessionTransferSizeUnknown) { NSString *contentLength = [uploadTask.originalRequest valueForHTTPHeaderField:@"Content-Length"]; if(contentLength) { - totalUnitCount = (int64_t) [contentLength longLongValue]; + totalUnitCount = (int64_t)[contentLength longLongValue]; } } - delegate.progress = [NSProgress progressWithTotalUnitCount:totalUnitCount]; + if (delegate.progress) { + delegate.progress.totalUnitCount = totalUnitCount; + } else { + delegate.progress = [NSProgress progressWithTotalUnitCount:totalUnitCount]; + } + delegate.progress.pausingHandler = ^{ [uploadTask suspend]; }; @@ -381,6 +552,8 @@ - (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask *progress = delegate.progress; } + uploadTask.taskDescription = self.taskDescriptionForSessionTasks; + [self setDelegate:delegate forTask:uploadTask]; } @@ -393,18 +566,18 @@ - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate.manager = self; delegate.completionHandler = completionHandler; - delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) { - if (destination) { + if (destination) { + delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) { return destination(location, task.response); - } - - return location; - }; + }; + } if (progress) { *progress = delegate.progress; } + downloadTask.taskDescription = self.taskDescriptionForSessionTasks; + [self setDelegate:delegate forTask:downloadTask]; } @@ -412,7 +585,6 @@ - (void)removeDelegateForTask:(NSURLSessionTask *)task { NSParameterAssert(task); [self.lock lock]; - [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(state)) context:AFTaskStateChangedContext]; [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; [self.lock unlock]; } @@ -466,11 +638,13 @@ - (NSArray *)downloadTasks { #pragma mark - - (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks { - if (cancelPendingTasks) { - [self.session invalidateAndCancel]; - } else { - [self.session finishTasksAndInvalidate]; - } + dispatch_async(dispatch_get_main_queue(), ^{ + if (cancelPendingTasks) { + [self.session invalidateAndCancel]; + } else { + [self.session finishTasksAndInvalidate]; + } + }); } #pragma mark - @@ -486,7 +660,10 @@ - (void)setResponseSerializer:(id )responseSerialize - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { - NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request]; + __block NSURLSessionDataTask *dataTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + dataTask = [self.session dataTaskWithRequest:request]; + }); [self addDelegateForDataTask:dataTask completionHandler:completionHandler]; @@ -500,9 +677,13 @@ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request progress:(NSProgress * __autoreleasing *)progress completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { - NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; + __block NSURLSessionUploadTask *uploadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; + }); + if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) { - for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfToRecreateBackgroundSessionUploadTask; attempts++) { + for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) { uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; } } @@ -517,7 +698,10 @@ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request progress:(NSProgress * __autoreleasing *)progress completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { - NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; + __block NSURLSessionUploadTask *uploadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; + }); [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; @@ -528,7 +712,10 @@ - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)reques progress:(NSProgress * __autoreleasing *)progress completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler { - NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request]; + __block NSURLSessionUploadTask *uploadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + uploadTask = [self.session uploadTaskWithStreamedRequest:request]; + }); [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; @@ -542,7 +729,10 @@ - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler { - NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request]; + __block NSURLSessionDownloadTask *downloadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + downloadTask = [self.session downloadTaskWithRequest:request]; + }); [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; @@ -554,7 +744,10 @@ - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler { - NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithResumeData:resumeData]; + __block NSURLSessionDownloadTask *downloadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + downloadTask = [self.session downloadTaskWithResumeData:resumeData]; + }); [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; @@ -581,6 +774,10 @@ - (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChalle self.sessionDidReceiveAuthenticationChallenge = block; } +- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block { + self.didFinishEventsForBackgroundURLSession = block; +} + #pragma mark - - (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block { @@ -621,10 +818,6 @@ - (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSessio self.dataTaskWillCacheResponse = block; } -- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block { - self.didFinishEventsForBackgroundURLSession = block; -} - #pragma mark - - (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block { @@ -659,42 +852,6 @@ - (BOOL)respondsToSelector:(SEL)selector { return [[self class] instancesRespondToSelector:selector]; } -#pragma mark - NSKeyValueObserving - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context -{ - if (context == AFTaskStateChangedContext && [keyPath isEqualToString:@"state"]) { - if (change[NSKeyValueChangeOldKey] && change[NSKeyValueChangeNewKey] && [change[NSKeyValueChangeNewKey] isEqual:change[NSKeyValueChangeOldKey]]) { - return; - } - - NSString *notificationName = nil; - switch ([(NSURLSessionTask *)object state]) { - case NSURLSessionTaskStateRunning: - notificationName = AFNetworkingTaskDidResumeNotification; - break; - case NSURLSessionTaskStateSuspended: - notificationName = AFNetworkingTaskDidSuspendNotification; - break; - case NSURLSessionTaskStateCompleted: - // AFNetworkingTaskDidFinishNotification posted by task completion handlers - default: - break; - } - - if (notificationName) { - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:object]; - }); - } - } else { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - #pragma mark - NSURLSessionDelegate - (void)URLSession:(NSURLSession *)session @@ -704,15 +861,7 @@ - (void)URLSession:(NSURLSession *)session self.sessionDidBecomeInvalid(session, error); } - [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { - NSArray *tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"]; - for (NSURLSessionTask *task in tasks) { - [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(state)) context:AFTaskStateChangedContext]; - } - - [self removeAllDelegates]; - }]; - + [self removeAllDelegates]; [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session]; } @@ -799,7 +948,7 @@ - (void)URLSession:(NSURLSession *)session needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler { NSInputStream *inputStream = nil; - + if (self.taskNeedNewBodyStream) { inputStream = self.taskNeedNewBodyStream(session, task); } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) { @@ -817,7 +966,7 @@ - (void)URLSession:(NSURLSession *)session totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { - + int64_t totalUnitCount = totalBytesExpectedToSend; if(totalUnitCount == NSURLSessionTransferSizeUnknown) { NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"]; @@ -825,7 +974,7 @@ - (void)URLSession:(NSURLSession *)session totalUnitCount = (int64_t) [contentLength longLongValue]; } } - + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalUnitCount]; @@ -928,19 +1077,21 @@ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; if (self.downloadTaskDidFinishDownloading) { NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); if (fileURL) { + delegate.downloadFileURL = fileURL; NSError *error = nil; [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; if (error) { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; } + return; } } - - AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + if (delegate) { [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; } @@ -973,7 +1124,7 @@ - (void)URLSession:(NSURLSession *)session } } -#pragma mark - NSecureCoding +#pragma mark - NSSecureCoding + (BOOL)supportsSecureCoding { return YES; diff --git a/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h b/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h index 312d680..8242035 100644 --- a/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h +++ b/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h @@ -1,6 +1,5 @@ // AFNetworkActivityIndicatorManager.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -35,7 +34,7 @@ [[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES]; - By setting `isNetworkActivityIndicatorVisible` to `YES` for `sharedManager`, the network activity indicator will show and hide automatically as requests start and finish. You should not ever need to call `incrementActivityCount` or `decrementActivityCount` yourself. + By setting `enabled` to `YES` for `sharedManager`, the network activity indicator will show and hide automatically as requests start and finish. You should not ever need to call `incrementActivityCount` or `decrementActivityCount` yourself. See the Apple Human Interface Guidelines section about the Network Activity Indicator for more information: http://developer.apple.com/library/iOS/#documentation/UserExperience/Conceptual/MobileHIG/UIElementGuidelines/UIElementGuidelines.html#//apple_ref/doc/uid/TP40006556-CH13-SW44 diff --git a/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m b/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m index c2d855a..82c6cc3 100644 --- a/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m +++ b/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m @@ -1,6 +1,5 @@ // AFNetworkActivityIndicatorManager.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -114,14 +113,16 @@ - (BOOL)isNetworkActivityIndicatorVisible { } - (void)updateNetworkActivityIndicatorVisibility { +#if !defined(AF_APP_EXTENSIONS) [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActivityIndicatorVisible]]; +#endif } - (void)setActivityCount:(NSInteger)activityCount { @synchronized(self) { _activityCount = activityCount; } - + dispatch_async(dispatch_get_main_queue(), ^{ [self updateNetworkActivityIndicatorVisibilityDelayed]; }); @@ -133,7 +134,7 @@ - (void)incrementActivityCount { _activityCount++; } [self didChangeValueForKey:@"activityCount"]; - + dispatch_async(dispatch_get_main_queue(), ^{ [self updateNetworkActivityIndicatorVisibilityDelayed]; }); @@ -148,7 +149,7 @@ - (void)decrementActivityCount { #pragma clang diagnostic pop } [self didChangeValueForKey:@"activityCount"]; - + dispatch_async(dispatch_get_main_queue(), ^{ [self updateNetworkActivityIndicatorVisibilityDelayed]; }); diff --git a/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h b/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h index 1c1f8dd..06e89c4 100644 --- a/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h +++ b/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h @@ -1,6 +1,5 @@ // UIActivityIndicatorView+AFNetworking.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -54,7 +53,7 @@ /** Binds the animating state to the execution state of the specified operation. - + @param operation The operation. If `nil`, automatic updating from any previously specified operation will be disabled. */ - (void)setAnimatingWithStateOfOperation:(AFURLConnectionOperation *)operation; diff --git a/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m b/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m index 6627dbb..1a36091 100644 --- a/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m +++ b/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m @@ -1,6 +1,5 @@ // UIActivityIndicatorView+AFNetworking.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h b/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h index b94f1cb..80eddc1 100644 --- a/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h +++ b/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h @@ -1,6 +1,5 @@ // UIAlertView+AFNetworking.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -24,7 +23,7 @@ #import -#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS) #import @@ -41,7 +40,7 @@ /** Shows an alert view with the error of the specified session task, if any. - + @param task The session task. @param delegate The alert view delegate. */ @@ -52,7 +51,7 @@ /** Shows an alert view with the error of the specified session task, if any, with a custom cancel button title and other button titles. - + @param task The session task. @param delegate The alert view delegate. @param cancelButtonTitle The title of the cancel button or nil if there is no cancel button. Using this argument is equivalent to setting the cancel button index to the value returned by invoking addButtonWithTitle: specifying this title. diff --git a/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m b/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m index b7e2a26..a128544 100644 --- a/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m +++ b/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m @@ -1,6 +1,5 @@ // UIAlertView+AFNetworking.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -22,7 +21,7 @@ #import "UIAlertView+AFNetworking.h" -#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS) #import "AFURLConnectionOperation.h" @@ -62,17 +61,32 @@ + (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION { - __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingTaskDidCompleteNotification object:task queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { + NSMutableArray *mutableOtherTitles = [NSMutableArray array]; + va_list otherButtonTitleList; + va_start(otherButtonTitleList, otherButtonTitles); + { + for (NSString *otherButtonTitle = otherButtonTitles; otherButtonTitle != nil; otherButtonTitle = va_arg(otherButtonTitleList, NSString *)) { + [mutableOtherTitles addObject:otherButtonTitle]; + } + } + va_end(otherButtonTitleList); + __block __weak id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingTaskDidCompleteNotification object:task queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { NSError *error = notification.userInfo[AFNetworkingTaskDidCompleteErrorKey]; if (error) { NSString *title, *message; AFGetAlertViewTitleAndMessageFromError(error, &title, &message); - [[[UIAlertView alloc] initWithTitle:title message:message delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles, nil] show]; + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:nil delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil, nil]; + for (NSString *otherButtonTitle in mutableOtherTitles) { + [alertView addButtonWithTitle:otherButtonTitle]; + } + [alertView setTitle:title]; + [alertView setMessage:message]; + [alertView show]; } - [[NSNotificationCenter defaultCenter] removeObserver:observer name:AFNetworkingTaskDidCompleteNotification object:notification.object]; + [[NSNotificationCenter defaultCenter] removeObserver:observer]; }]; } #endif @@ -82,7 +96,7 @@ + (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task + (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation delegate:(id)delegate { - [self showAlertViewForRequestOperationWithErrorOnCompletion:operation delegate:delegate cancelButtonTitle:NSLocalizedStringFromTable(@"Dismiss", @"AFNetworking", @"UIAlert View Cancel Button Title") otherButtonTitles:nil, nil]; + [self showAlertViewForRequestOperationWithErrorOnCompletion:operation delegate:delegate cancelButtonTitle:NSLocalizedStringFromTable(@"Dismiss", @"AFNetworking", @"UIAlertView Cancel Button Title") otherButtonTitles:nil, nil]; } + (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation @@ -90,7 +104,17 @@ + (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOp cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION { - __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingOperationDidFinishNotification object:operation queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { + NSMutableArray *mutableOtherTitles = [NSMutableArray array]; + va_list otherButtonTitleList; + va_start(otherButtonTitleList, otherButtonTitles); + { + for (NSString *otherButtonTitle = otherButtonTitles; otherButtonTitle != nil; otherButtonTitle = va_arg(otherButtonTitleList, NSString *)) { + [mutableOtherTitles addObject:otherButtonTitle]; + } + } + va_end(otherButtonTitleList); + + __block __weak id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingOperationDidFinishNotification object:operation queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { if (notification.object && [notification.object isKindOfClass:[AFURLConnectionOperation class]]) { NSError *error = [(AFURLConnectionOperation *)notification.object error]; @@ -98,11 +122,17 @@ + (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOp NSString *title, *message; AFGetAlertViewTitleAndMessageFromError(error, &title, &message); - [[[UIAlertView alloc] initWithTitle:title message:message delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles, nil] show]; + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:nil delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil, nil]; + for (NSString *otherButtonTitle in mutableOtherTitles) { + [alertView addButtonWithTitle:otherButtonTitle]; + } + [alertView setTitle:title]; + [alertView setMessage:message]; + [alertView show]; } } - [[NSNotificationCenter defaultCenter] removeObserver:observer name:AFNetworkingOperationDidFinishNotification object:notification.object]; + [[NSNotificationCenter defaultCenter] removeObserver:observer]; }]; } diff --git a/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h b/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h index fd89ecd..6c09b7a 100644 --- a/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h +++ b/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h @@ -1,6 +1,5 @@ // UIButton+AFNetworking.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,20 +27,51 @@ #import +@protocol AFURLResponseSerialization, AFImageCache; + /** This category adds methods to the UIKit framework's `UIButton` class. The methods in this category provide support for loading remote images and background images asynchronously from a URL. + + @warning Compound values for control `state` (such as `UIControlStateHighlighted | UIControlStateDisabled`) are unsupported. */ @interface UIButton (AFNetworking) +///---------------------------- +/// @name Accessing Image Cache +///---------------------------- + +/** + The image cache used to improve image loadiing performance on scroll views. By default, `UIButton` will use the `sharedImageCache` of `UIImageView`. + */ ++ (id )sharedImageCache; + +/** + Set the cache used for image loading. + + @param imageCache The image cache. + */ ++ (void)setSharedImageCache:(id )imageCache; + +///------------------------------------ +/// @name Accessing Response Serializer +///------------------------------------ + +/** + The response serializer used to create an image representation from the server response and response data. By default, this is an instance of `AFImageResponseSerializer`. + + @discussion Subclasses of `AFImageResponseSerializer` could be used to perform post-processing, such as color correction, face detection, or other effects. See https://github.com/AFNetworking/AFCoreImageSerializer + */ +@property (nonatomic, strong) id imageResponseSerializer; + ///-------------------- /// @name Setting Image ///-------------------- /** Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. - + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - + @param state The control state. @param url The URL used for the image request. */ @@ -50,9 +80,9 @@ /** Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. - + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - + @param state The control state. @param url The URL used for the image request. @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. @@ -65,7 +95,7 @@ Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - + If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setImage:forState:` is applied. @param state The control state. @@ -77,7 +107,7 @@ - (void)setImageForState:(UIControlState)state withURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage - success:(void (^)(NSHTTPURLResponse *response, UIImage *image))success + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSError *error))failure; @@ -123,7 +153,7 @@ - (void)setBackgroundImageForState:(UIControlState)state withURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage - success:(void (^)(NSHTTPURLResponse *response, UIImage *image))success + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSError *error))failure; @@ -132,14 +162,18 @@ ///------------------------------ /** - Cancels any executing image operation for the receiver, if one exists. + Cancels any executing image operation for the specified control state of the receiver, if one exists. + + @param state The control state. */ -- (void)cancelImageRequestOperation; +- (void)cancelImageRequestOperationForState:(UIControlState)state; /** - Cancels any executing background image operation for the receiver, if one exists. + Cancels any executing background image operation for the specified control state of the receiver, if one exists. + + @param state The control state. */ -- (void)cancelBackgroundImageRequestOperation; +- (void)cancelBackgroundImageRequestOperationForState:(UIControlState)state; @end diff --git a/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m b/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m index 689ae9a..f34631e 100644 --- a/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m +++ b/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m @@ -1,6 +1,5 @@ // UIButton+AFNetworking.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,11 +25,12 @@ #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#import "AFURLResponseSerialization.h" #import "AFHTTPRequestOperation.h" +#import "UIImageView+AFNetworking.h" + @interface UIButton (_AFNetworking) -@property (readwrite, nonatomic, strong, setter = af_setImageRequestOperation:) AFHTTPRequestOperation *af_imageRequestOperation; -@property (readwrite, nonatomic, strong, setter = af_setBackgroundImageRequestOperation:) AFHTTPRequestOperation *af_backgroundImageRequestOperation; @end @implementation UIButton (_AFNetworking) @@ -46,20 +46,66 @@ + (NSOperationQueue *)af_sharedImageRequestOperationQueue { return _af_sharedImageRequestOperationQueue; } -- (AFHTTPRequestOperation *)af_imageRequestOperation { - return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_imageRequestOperation)); +#pragma mark - + +static char AFImageRequestOperationNormal; +static char AFImageRequestOperationHighlighted; +static char AFImageRequestOperationSelected; +static char AFImageRequestOperationDisabled; + +static const char * af_imageRequestOperationKeyForState(UIControlState state) { + switch (state) { + case UIControlStateHighlighted: + return &AFImageRequestOperationHighlighted; + case UIControlStateSelected: + return &AFImageRequestOperationSelected; + case UIControlStateDisabled: + return &AFImageRequestOperationDisabled; + case UIControlStateNormal: + default: + return &AFImageRequestOperationNormal; + } +} + +- (AFHTTPRequestOperation *)af_imageRequestOperationForState:(UIControlState)state { + return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, af_imageRequestOperationKeyForState(state)); } -- (void)af_setImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation { - objc_setAssociatedObject(self, @selector(af_imageRequestOperation), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +- (void)af_setImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation + forState:(UIControlState)state +{ + objc_setAssociatedObject(self, af_imageRequestOperationKeyForState(state), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +static char AFBackgroundImageRequestOperationNormal; +static char AFBackgroundImageRequestOperationHighlighted; +static char AFBackgroundImageRequestOperationSelected; +static char AFBackgroundImageRequestOperationDisabled; + +static const char * af_backgroundImageRequestOperationKeyForState(UIControlState state) { + switch (state) { + case UIControlStateHighlighted: + return &AFBackgroundImageRequestOperationHighlighted; + case UIControlStateSelected: + return &AFBackgroundImageRequestOperationSelected; + case UIControlStateDisabled: + return &AFBackgroundImageRequestOperationDisabled; + case UIControlStateNormal: + default: + return &AFBackgroundImageRequestOperationNormal; + } } -- (AFHTTPRequestOperation *)af_backgroundImageRequestOperation { - return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_backgroundImageRequestOperation)); +- (AFHTTPRequestOperation *)af_backgroundImageRequestOperationForState:(UIControlState)state { + return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, af_backgroundImageRequestOperationKeyForState(state)); } -- (void)af_setBackgroundImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation { - objc_setAssociatedObject(self, @selector(af_backgroundImageRequestOperation), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +- (void)af_setBackgroundImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation + forState:(UIControlState)state +{ + objc_setAssociatedObject(self, af_backgroundImageRequestOperationKeyForState(state), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end @@ -68,6 +114,38 @@ - (void)af_setBackgroundImageRequestOperation:(AFHTTPRequestOperation *)imageReq @implementation UIButton (AFNetworking) ++ (id )sharedImageCache { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(sharedImageCache)) ?: [UIImageView sharedImageCache]; +#pragma clang diagnostic pop +} + ++ (void)setSharedImageCache:(id )imageCache { + objc_setAssociatedObject(self, @selector(sharedImageCache), imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (id )imageResponseSerializer { + static id _af_defaultImageResponseSerializer = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultImageResponseSerializer = [AFImageResponseSerializer serializer]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(imageResponseSerializer)) ?: _af_defaultImageResponseSerializer; +#pragma clang diagnostic pop +} + +- (void)setImageResponseSerializer:(id )serializer { + objc_setAssociatedObject(self, @selector(imageResponseSerializer), serializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + - (void)setImageForState:(UIControlState)state withURL:(NSURL *)url { @@ -87,34 +165,49 @@ - (void)setImageForState:(UIControlState)state - (void)setImageForState:(UIControlState)state withURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage - success:(void (^)(NSHTTPURLResponse *response, UIImage *image))success + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSError *error))failure { - [self cancelImageRequestOperation]; - - [self setImage:placeholderImage forState:state]; - - __weak __typeof(self)weakSelf = self; - self.af_imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; - self.af_imageRequestOperation.responseSerializer = [AFImageResponseSerializer serializer]; - [self.af_imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { - __strong __typeof(weakSelf)strongSelf = weakSelf; - if ([[urlRequest URL] isEqual:[operation.request URL]]) { - if (success) { - success(operation.response, responseObject); - } else if (responseObject) { - [strongSelf setImage:responseObject forState:state]; - } + [self cancelImageRequestOperationForState:state]; + + UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest]; + if (cachedImage) { + if (success) { + success(nil, nil, cachedImage); + } else { + [self setImage:cachedImage forState:state]; } - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - if ([[urlRequest URL] isEqual:[operation.response URL]]) { - if (failure) { - failure(error); - } + + [self af_setImageRequestOperation:nil forState:state]; + } else { + if (placeholderImage) { + [self setImage:placeholderImage forState:state]; } - }]; - [[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_imageRequestOperation]; + __weak __typeof(self)weakSelf = self; + AFHTTPRequestOperation *imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; + imageRequestOperation.responseSerializer = self.imageResponseSerializer; + [imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (success) { + success(operation.request, operation.response, responseObject); + } else if (responseObject) { + [strongSelf setImage:responseObject forState:state]; + } + } + [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (failure) { + failure(error); + } + } + }]; + + [self af_setImageRequestOperation:imageRequestOperation forState:state]; + [[[self class] af_sharedImageRequestOperationQueue] addOperation:imageRequestOperation]; + } } #pragma mark - @@ -138,46 +231,61 @@ - (void)setBackgroundImageForState:(UIControlState)state - (void)setBackgroundImageForState:(UIControlState)state withURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage - success:(void (^)(NSHTTPURLResponse *response, UIImage *image))success + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSError *error))failure { - [self cancelBackgroundImageRequestOperation]; - - [self setBackgroundImage:placeholderImage forState:state]; - - __weak __typeof(self)weakSelf = self; - self.af_backgroundImageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; - self.af_backgroundImageRequestOperation.responseSerializer = [AFImageResponseSerializer serializer]; - [self.af_backgroundImageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { - __strong __typeof(weakSelf)strongSelf = weakSelf; - if ([[urlRequest URL] isEqual:[operation.request URL]]) { - if (success) { - success(operation.response, responseObject); - } else if (responseObject) { - [strongSelf setBackgroundImage:responseObject forState:state]; - } + [self cancelBackgroundImageRequestOperationForState:state]; + + UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest]; + if (cachedImage) { + if (success) { + success(nil, nil, cachedImage); + } else { + [self setBackgroundImage:cachedImage forState:state]; } - } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - if ([[urlRequest URL] isEqual:[operation.response URL]]) { - if (failure) { - failure(error); - } + + [self af_setBackgroundImageRequestOperation:nil forState:state]; + } else { + if (placeholderImage) { + [self setBackgroundImage:placeholderImage forState:state]; } - }]; - [[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_backgroundImageRequestOperation]; + __weak __typeof(self)weakSelf = self; + AFHTTPRequestOperation *backgroundImageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; + backgroundImageRequestOperation.responseSerializer = self.imageResponseSerializer; + [backgroundImageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (success) { + success(operation.request, operation.response, responseObject); + } else if (responseObject) { + [strongSelf setBackgroundImage:responseObject forState:state]; + } + } + [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (failure) { + failure(error); + } + } + }]; + + [self af_setBackgroundImageRequestOperation:backgroundImageRequestOperation forState:state]; + [[[self class] af_sharedImageRequestOperationQueue] addOperation:backgroundImageRequestOperation]; + } } #pragma mark - -- (void)cancelImageRequestOperation { - [self.af_imageRequestOperation cancel]; - self.af_imageRequestOperation = nil; +- (void)cancelImageRequestOperationForState:(UIControlState)state { + [[self af_imageRequestOperationForState:state] cancel]; + [self af_setImageRequestOperation:nil forState:state]; } -- (void)cancelBackgroundImageRequestOperation { - [self.af_backgroundImageRequestOperation cancel]; - self.af_backgroundImageRequestOperation = nil; +- (void)cancelBackgroundImageRequestOperationForState:(UIControlState)state { + [[self af_backgroundImageRequestOperationForState:state] cancel]; + [self af_setBackgroundImageRequestOperation:nil forState:state]; } @end diff --git a/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h b/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h index a983d98..2fdff9f 100644 --- a/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h +++ b/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h @@ -1,6 +1,5 @@ // UIImageView+AFNetworking.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -40,13 +39,13 @@ ///---------------------------- /** - The image cache used to improve image loadiing performance on scroll views. By default, this is an `NSCache` subclass conforming to the `AFImageCache` protocol, which listens for notification warnings and evicts objects accordingly. + The image cache used to improve image loading performance on scroll views. By default, this is an `NSCache` subclass conforming to the `AFImageCache` protocol, which listens for notification warnings and evicts objects accordingly. */ + (id )sharedImageCache; /** Set the cache used for image loading. - + @param imageCache The image cache. */ + (void)setSharedImageCache:(id )imageCache; @@ -57,7 +56,7 @@ /** The response serializer used to create an image representation from the server response and response data. By default, this is an instance of `AFImageResponseSerializer`. - + @discussion Subclasses of `AFImageResponseSerializer` could be used to perform post-processing, such as color correction, face detection, or other effects. See https://github.com/AFNetworking/AFCoreImageSerializer */ @property (nonatomic, strong) id imageResponseSerializer; @@ -67,8 +66,8 @@ ///-------------------- /** - Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. - + Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` @@ -78,8 +77,8 @@ - (void)setImageWithURL:(NSURL *)url; /** - Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. - + Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` @@ -91,10 +90,10 @@ placeholderImage:(UIImage *)placeholderImage; /** - Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. - + Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. - + If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied. @param urlRequest The URL request used for the image request. @@ -119,20 +118,20 @@ /** The `AFImageCache` protocol is adopted by an object used to cache images loaded by the AFNetworking category on `UIImageView`. */ -@protocol AFImageCache +@protocol AFImageCache /** Returns a cached image for the specififed request, if available. - + @param request The image request. - + @return The cached image. */ - (UIImage *)cachedImageForRequest:(NSURLRequest *)request; /** Caches a particular image for the specified request. - + @param image The image to cache. @param request The request to be used as a cache key. */ diff --git a/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m b/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m index 64c0943..1400b4a 100644 --- a/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m +++ b/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m @@ -1,6 +1,5 @@ // UIImageView+AFNetworking.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -82,10 +81,12 @@ @implementation UIImageView (AFNetworking) #pragma clang diagnostic pop } -+ (void)setSharedImageCache:(id)imageCache { ++ (void)setSharedImageCache:(id )imageCache { objc_setAssociatedObject(self, @selector(sharedImageCache), imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } +#pragma mark - + - (id )imageResponseSerializer { static id _af_defaultImageResponseSerializer = nil; static dispatch_once_t onceToken; @@ -138,7 +139,7 @@ - (void)setImageWithURLRequest:(NSURLRequest *)urlRequest if (placeholderImage) { self.image = placeholderImage; } - + __weak __typeof(self)weakSelf = self; self.af_imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; self.af_imageRequestOperation.responseSerializer = self.imageResponseSerializer; diff --git a/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h b/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h index 3f1bc08..6a4e16f 100644 --- a/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h +++ b/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h @@ -1,6 +1,5 @@ // UIProgressView+AFNetworking.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -41,7 +40,7 @@ /** Binds the progress to the upload progress of the specified session task. - + @param task The session task. @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. */ diff --git a/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m b/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m index 7281149..ad2c280 100644 --- a/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m +++ b/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m @@ -1,6 +1,5 @@ // UIProgressView+AFNetworking.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -77,8 +76,8 @@ - (void)af_setDownloadProgressAnimated:(BOOL)animated { - (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task animated:(BOOL)animated { - [task addObserver:self forKeyPath:@"state" options:0 context:AFTaskCountOfBytesSentContext]; - [task addObserver:self forKeyPath:@"countOfBytesSent" options:0 context:AFTaskCountOfBytesSentContext]; + [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext]; + [task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext]; [self af_setUploadProgressAnimated:animated]; } @@ -86,8 +85,8 @@ - (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task - (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task animated:(BOOL)animated { - [task addObserver:self forKeyPath:@"state" options:0 context:AFTaskCountOfBytesReceivedContext]; - [task addObserver:self forKeyPath:@"countOfBytesReceived" options:0 context:AFTaskCountOfBytesReceivedContext]; + [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext]; + [task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext]; [self af_setDownloadProgressAnimated:animated]; } diff --git a/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h b/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h index 37ce772..18c12ea 100644 --- a/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h +++ b/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h @@ -42,7 +42,7 @@ /** Binds the refreshing state to the state of the specified task. - @param task The task. If `nil`, automatic updating from any previously specified operation will be diabled. + @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled. */ #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 - (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; diff --git a/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m b/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m index ba28157..e266451 100644 --- a/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m +++ b/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m @@ -41,16 +41,14 @@ - (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task { [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; if (task) { - if (task.state != NSURLSessionTaskStateCompleted) { - if (task.state == NSURLSessionTaskStateRunning) { - [self beginRefreshing]; - } else { - [self endRefreshing]; - } + if (task.state == NSURLSessionTaskStateRunning) { + [self beginRefreshing]; [notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingTaskDidResumeNotification object:task]; [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidCompleteNotification object:task]; [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidSuspendNotification object:task]; + } else { + [self endRefreshing]; } } } diff --git a/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h b/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h index 202e8f4..56e5832 100644 --- a/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h +++ b/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h @@ -1,6 +1,5 @@ // UIWebView+AFNetworking.h -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -33,7 +32,7 @@ /** This category adds methods to the UIKit framework's `UIWebView` class. The methods in this category provide increased control over the request cycle, including progress monitoring and success / failure handling. - + @discussion When using these category methods, make sure to assign `delegate` for the web view, which implements `–webView:shouldStartLoadWithRequest:navigationType:` appropriately. This allows for tapped links to be loaded through AFNetworking, and can ensure that `canGoBack` & `canGoForward` update their values correctly. */ @interface UIWebView (AFNetworking) @@ -50,7 +49,7 @@ /** Asynchronously loads the specified request. - + @param request A URL request identifying the location of the content to load. This must not be `nil`. @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. @param success A block object to be executed when the request finishes loading successfully. This block returns the HTML string to be loaded by the web view, and takes two arguments: the response, and the response string. diff --git a/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m b/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m index 525d02a..93eacaa 100644 --- a/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m +++ b/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m @@ -1,6 +1,5 @@ // UIWebView+AFNetworking.m -// -// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com) +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -136,6 +135,11 @@ - (void)loadRequest:(NSURLRequest *)request #pragma clang diagnostic ignored "-Wgnu" __strong __typeof(weakSelf) strongSelf = weakSelf; [strongSelf loadData:data MIMEType:(MIMEType ?: [operation.response MIMEType]) textEncodingName:(textEncodingName ?: [operation.response textEncodingName]) baseURL:[operation.response URL]]; + + if ([strongSelf.delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) { + [strongSelf.delegate webViewDidFinishLoad:strongSelf]; + } + #pragma clang diagnostic pop } failure:^(AFHTTPRequestOperation * __unused operation, NSError *error) { if (failure) { @@ -144,6 +148,10 @@ - (void)loadRequest:(NSURLRequest *)request }]; [self.af_HTTPRequestOperation start]; + + if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) { + [self.delegate webViewDidStartLoad:self]; + } } @end diff --git a/YTKNetworkDemo/YTKNetworkDemo.xcodeproj/project.xcworkspace/xcshareddata/YTKNetworkDemo.xccheckout b/YTKNetworkDemo/YTKNetworkDemo.xcodeproj/project.xcworkspace/xcshareddata/YTKNetworkDemo.xccheckout deleted file mode 100644 index fbb9485..0000000 --- a/YTKNetworkDemo/YTKNetworkDemo.xcodeproj/project.xcworkspace/xcshareddata/YTKNetworkDemo.xccheckout +++ /dev/null @@ -1,41 +0,0 @@ - - - - - IDESourceControlProjectFavoriteDictionaryKey - - IDESourceControlProjectIdentifier - 8FB3E95E-2CEF-4592-A1DF-5EA9A4179829 - IDESourceControlProjectName - YTKNetworkDemo - IDESourceControlProjectOriginsDictionary - - 327B5DC53056E27E60349B39CBD2CB9AA4F5E614 - github.com:yuantiku/YTKNetwork.git - - IDESourceControlProjectPath - YTKNetworkDemo/YTKNetworkDemo.xcodeproj - IDESourceControlProjectRelativeInstallPathDictionary - - 327B5DC53056E27E60349B39CBD2CB9AA4F5E614 - ../../.. - - IDESourceControlProjectURL - github.com:yuantiku/YTKNetwork.git - IDESourceControlProjectVersion - 111 - IDESourceControlProjectWCCIdentifier - 327B5DC53056E27E60349B39CBD2CB9AA4F5E614 - IDESourceControlProjectWCConfigurations - - - IDESourceControlRepositoryExtensionIdentifierKey - public.vcs.git - IDESourceControlWCCIdentifierKey - 327B5DC53056E27E60349B39CBD2CB9AA4F5E614 - IDESourceControlWCCName - YTKNetwork - - - - diff --git a/YTKNetworkDemo/YTKNetworkDemo.xcodeproj/xcshareddata/xcschemes/YTKNetworkDemo.xcscheme b/YTKNetworkDemo/YTKNetworkDemo.xcodeproj/xcshareddata/xcschemes/YTKNetworkDemo.xcscheme deleted file mode 100644 index 77cfddb..0000000 --- a/YTKNetworkDemo/YTKNetworkDemo.xcodeproj/xcshareddata/xcschemes/YTKNetworkDemo.xcscheme +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -