diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d842745..6cdef042 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - [fixed] Fixes a threadsafety issue in accessing callbacks. [garrettmoon](https://github.com/garrettmoon) - [new] PINRemoteImageManager now respects the request timeout value of session configuration. [garrettmoon](https://github.com/garrettmoon) - [new] Updated to latest PINCache beta 5. [garrettmoon](https://github.com/garrettmoon) +- [new] Added support for getting NSURLResponse from a PINRemoteImageManagerResult object. [garrettmoon](https://github.com/garrettmoon) ## 3.0.0 Beta 10 - [new] Added support (in iOS 10) for skipping cancelation if the estimated amount of time to complete the download is less than the average time to first byte for a host. [#364](https://github.com/pinterest/PINRemoteImage/pull/364) [garrettmoon](https://github.com/garrettmoon) diff --git a/Source/Classes/PINRemoteImageDownloadTask.m b/Source/Classes/PINRemoteImageDownloadTask.m index 912a011f..ab16fc47 100644 --- a/Source/Classes/PINRemoteImageDownloadTask.m +++ b/Source/Classes/PINRemoteImageDownloadTask.m @@ -80,9 +80,10 @@ - (void)callProgressImageWithImage:(nonnull PINImage *)image renderedImageQualit progressImageBlock([PINRemoteImageManagerResult imageResultWithImage:image alternativeRepresentation:nil requestLength:CACurrentMediaTime() - requestTime - error:nil resultType:PINRemoteImageResultTypeProgress UUID:UUID + response:nil + error:nil renderedImageQuality:renderedImageQuality]); }); } @@ -156,6 +157,7 @@ - (nonnull PINRemoteImageManagerResult *)imageResultWithImage:(nullable PINImage error:(nullable NSError *)error resultType:(PINRemoteImageResultType)resultType UUID:(nullable NSUUID *)UUID + response:(nonnull NSURLResponse *)response { __block NSUInteger bytesSavedByResuming; [self.lock lockWithBlock:^{ @@ -164,9 +166,10 @@ - (nonnull PINRemoteImageManagerResult *)imageResultWithImage:(nullable PINImage return [PINRemoteImageManagerResult imageResultWithImage:image alternativeRepresentation:alternativeRepresentation requestLength:requestLength - error:error resultType:resultType UUID:UUID + response:response + error:error bytesSavedByResuming:bytesSavedByResuming]; } @@ -337,7 +340,7 @@ - (void)scheduleDownloadWithRequest:(NSURLRequest *)request } } - completionHandler(data, error); + completionHandler(data, response, error); } }]; }]]; diff --git a/Source/Classes/PINRemoteImageManager+Private.h b/Source/Classes/PINRemoteImageManager+Private.h index d94d75a7..0c673ccf 100644 --- a/Source/Classes/PINRemoteImageManager+Private.h +++ b/Source/Classes/PINRemoteImageManager+Private.h @@ -11,7 +11,7 @@ #import "PINRemoteImageDownloadQueue.h" -typedef void (^PINRemoteImageManagerDataCompletion)(NSData *data, NSError *error); +typedef void (^PINRemoteImageManagerDataCompletion)(NSData *data, NSURLResponse *response, NSError *error); @interface PINRemoteImageManager (private) diff --git a/Source/Classes/PINRemoteImageManager.h b/Source/Classes/PINRemoteImageManager.h index f882b560..3d2adf0d 100644 --- a/Source/Classes/PINRemoteImageManager.h +++ b/Source/Classes/PINRemoteImageManager.h @@ -197,10 +197,12 @@ typedef void(^PINRemoteImageManagerProgressDownload)(int64_t completedBytes, int /** * Sets a custom header to be included in every request. Headers set from this method will override any header from NSURLSessionConfiguration. * + * @deprecated Use NSURLSessionConfiguration.HTTPAdditionalHeaders instead * @param value A value for the header. Pass in nil to remove a previously set value. * @param header A string field for header. + */ -- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)header; +- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)header __attribute__((deprecated)); /** Sets the Request Configuration Block. diff --git a/Source/Classes/PINRemoteImageManager.m b/Source/Classes/PINRemoteImageManager.m index 86d3f445..0fbdc4e5 100644 --- a/Source/Classes/PINRemoteImageManager.m +++ b/Source/Classes/PINRemoteImageManager.m @@ -78,7 +78,7 @@ float dataTaskPriorityWithImageManagerPriority(PINRemoteImageManagerPriority pri NSString * const PINRemoteImageManagerErrorDomain = @"PINRemoteImageManagerErrorDomain"; NSString * const PINRemoteImageCacheKey = @"cacheKey"; NSString * const PINRemoteImageCacheKeyResumePrefix = @"R-"; -typedef void (^PINRemoteImageManagerDataCompletion)(NSData *data, NSError *error); +typedef void (^PINRemoteImageManagerDataCompletion)(NSData *data, NSURLResponse *response, NSError *error); @interface PINTaskQOS : NSObject @@ -622,7 +622,7 @@ - (NSUUID *)downloadImageWithURL:(NSURL *)url if (found) { if (valid) { typeof(self) strongSelf = weakSelf; - [strongSelf callCompletionsWithKey:key image:image alternativeRepresentation:alternativeRepresentation cached:YES error:nil finalized:YES]; + [strongSelf callCompletionsWithKey:key image:image alternativeRepresentation:alternativeRepresentation cached:YES response:nil error:nil finalized:YES]; } else { //Remove completion and try again typeof(self) strongSelf = weakSelf; @@ -713,8 +713,8 @@ - (void)downloadImageWithURL:(NSURL *)url code:PINRemoteImageManagerErrorFailedToProcessImage userInfo:nil]; } - [strongSelf callCompletionsWithKey:key image:image alternativeRepresentation:nil cached:NO error:error finalized:NO]; - + [strongSelf callCompletionsWithKey:key image:image alternativeRepresentation:nil cached:NO response:result.response error:error finalized:NO]; + if (error == nil && image != nil) { BOOL saveAsJPEG = (options & PINRemoteImageManagerSaveProcessedImageAsJPEG) != 0; NSData *diskData = nil; @@ -727,7 +727,7 @@ - (void)downloadImageWithURL:(NSURL *)url [strongSelf materializeAndCacheObject:image cacheInDisk:diskData additionalCost:processCost url:url key:key options:options outImage:nil outAltRep:nil]; } - [strongSelf callCompletionsWithKey:key image:image alternativeRepresentation:nil cached:NO error:error finalized:YES]; + [strongSelf callCompletionsWithKey:key image:image alternativeRepresentation:nil cached:NO response:result.response error:error finalized:YES]; } else { if (error == nil) { error = [NSError errorWithDomain:PINRemoteImageManagerErrorDomain @@ -735,7 +735,7 @@ - (void)downloadImageWithURL:(NSURL *)url userInfo:nil]; } - [strongSelf callCompletionsWithKey:key image:nil alternativeRepresentation:nil cached:NO error:error finalized:YES]; + [strongSelf callCompletionsWithKey:key image:nil alternativeRepresentation:nil cached:NO response:result.response error:error finalized:YES]; } }]; task.downloadTaskUUID = downloadTaskUUID; @@ -762,7 +762,7 @@ - (void)downloadImageWithURL:(NSURL *)url resume:resume skipRetry:(options & PINRemoteImageManagerDownloadOptionsSkipRetry) priority:priority - completionHandler:^(NSData *data, NSError *error) + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { [_concurrentOperationQueue addOperation:^ { @@ -782,7 +782,7 @@ - (void)downloadImageWithURL:(NSURL *)url userInfo:nil]; } - [self callCompletionsWithKey:key image:image alternativeRepresentation:alternativeRepresentation cached:NO error:remoteImageError finalized:YES]; + [self callCompletionsWithKey:key image:image alternativeRepresentation:alternativeRepresentation cached:NO response:response error:remoteImageError finalized:YES]; } withPriority:operationPriorityWithImageManagerPriority(priority)]; }]; } @@ -827,21 +827,18 @@ - (BOOL)earlyReturnWithOptions:(PINRemoteImageManagerDownloadOptions)options url code:NSURLErrorUnsupportedURL userInfo:@{ NSLocalizedDescriptionKey : @"unsupported URL" }]; } + PINRemoteImageManagerResult *result = [PINRemoteImageManagerResult imageResultWithImage:image + alternativeRepresentation:alternativeRepresentation + requestLength:0 + resultType:resultType + UUID:nil + response:nil + error:error]; if (allowEarlyReturn && [NSThread isMainThread]) { - completion([PINRemoteImageManagerResult imageResultWithImage:image - alternativeRepresentation:alternativeRepresentation - requestLength:0 - error:error - resultType:resultType - UUID:nil]); + completion(result); } else { dispatch_async(self.callbackQueue, ^{ - completion([PINRemoteImageManagerResult imageResultWithImage:image - alternativeRepresentation:alternativeRepresentation - requestLength:0 - error:error - resultType:resultType - UUID:nil]); + completion(result); }); } return YES; @@ -932,7 +929,7 @@ - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url userInfo:nil]; } - completion(data, error); + completion(data, response, error); } }]; @@ -943,11 +940,17 @@ - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url return dataTask; } -- (void)callCompletionsWithKey:(NSString *)key image:(PINImage *)image alternativeRepresentation:(id)alternativeRepresentation cached:(BOOL)cached error:(NSError *)error finalized:(BOOL)finalized +- (void)callCompletionsWithKey:(NSString *)key + image:(PINImage *)image + alternativeRepresentation:(id)alternativeRepresentation + cached:(BOOL)cached + response:(NSURLResponse *)response + error:(NSError *)error + finalized:(BOOL)finalized { [self lock]; PINRemoteImageDownloadTask *task = [self.tasks objectForKey:key]; - [task callCompletionsWithImage:image alternativeRepresentation:alternativeRepresentation cached:cached error:error remove:!finalized]; + [task callCompletionsWithImage:image alternativeRepresentation:alternativeRepresentation cached:cached response:response error:error remove:!finalized]; if (finalized) { [self.tasks removeObjectForKey:key]; } @@ -1117,9 +1120,10 @@ - (void)imageFromCacheWithURL:(NSURL *)url completion([PINRemoteImageManagerResult imageResultWithImage:image alternativeRepresentation:alternativeRepresentation requestLength:CACurrentMediaTime() - requestTime - error:error resultType:PINRemoteImageResultTypeCache - UUID:nil]); + UUID:nil + response:nil + error:error]); }); }]; } @@ -1160,9 +1164,10 @@ - (PINRemoteImageManagerResult *)synchronousImageFromCacheWithURL:(NSURL *)url p return [PINRemoteImageManagerResult imageResultWithImage:image alternativeRepresentation:alternativeRepresentation requestLength:CACurrentMediaTime() - requestTime - error:error resultType:PINRemoteImageResultTypeMemoryCache - UUID:nil]; + UUID:nil + response:nil + error:error]; } #pragma mark - Session Task Blocks diff --git a/Source/Classes/PINRemoteImageManagerResult.h b/Source/Classes/PINRemoteImageManagerResult.h index e84c8496..b2769f1a 100644 --- a/Source/Classes/PINRemoteImageManagerResult.h +++ b/Source/Classes/PINRemoteImageManagerResult.h @@ -43,28 +43,32 @@ typedef NS_ENUM(NSUInteger, PINRemoteImageResultType) { @property (nonatomic, readonly, strong, nullable) NSUUID *UUID; @property (nonatomic, readonly, assign) CGFloat renderedImageQuality; @property (nonatomic, readonly, assign) NSUInteger bytesSavedByResuming; +@property (nonatomic, readonly, strong, nullable) NSURLResponse *response; + (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image alternativeRepresentation:(nullable id)alternativeRepresentation requestLength:(NSTimeInterval)requestLength - error:(nullable NSError *)error resultType:(PINRemoteImageResultType)resultType - UUID:(nullable NSUUID *)uuid; + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error; + (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image alternativeRepresentation:(nullable id)alternativeRepresentation requestLength:(NSTimeInterval)requestLength - error:(nullable NSError *)error resultType:(PINRemoteImageResultType)resultType UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error bytesSavedByResuming:(NSUInteger)bytesSavedByResuming; + (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image alternativeRepresentation:(nullable id)alternativeRepresentation requestLength:(NSTimeInterval)requestLength - error:(nullable NSError *)error resultType:(PINRemoteImageResultType)resultType UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error renderedImageQuality:(CGFloat)renderedImageQuality; @end diff --git a/Source/Classes/PINRemoteImageManagerResult.m b/Source/Classes/PINRemoteImageManagerResult.m index a8e1e51a..8076abd5 100644 --- a/Source/Classes/PINRemoteImageManagerResult.m +++ b/Source/Classes/PINRemoteImageManagerResult.m @@ -10,28 +10,31 @@ @implementation PINRemoteImageManagerResult -+ (instancetype)imageResultWithImage:(PINImage *)image - alternativeRepresentation:(id)alternativeRepresentation - requestLength:(NSTimeInterval)requestLength - error:(NSError *)error - resultType:(PINRemoteImageResultType)resultType - UUID:(NSUUID *)uuid ++ (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error { return [self imageResultWithImage:image alternativeRepresentation:alternativeRepresentation requestLength:requestLength - error:error resultType:resultType UUID:uuid + response:response + error:error renderedImageQuality:1.0]; } + (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image alternativeRepresentation:(nullable id)alternativeRepresentation requestLength:(NSTimeInterval)requestLength - error:(nullable NSError *)error resultType:(PINRemoteImageResultType)resultType UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error bytesSavedByResuming:(NSUInteger)bytesSavedByResuming { return [[self alloc] initWithImage:image @@ -40,17 +43,19 @@ + (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image error:error resultType:resultType UUID:uuid + response:response renderedImageQuality:1.0 bytesSavedByResuming:bytesSavedByResuming]; } -+ (instancetype)imageResultWithImage:(PINImage *)image - alternativeRepresentation:(id)alternativeRepresentation - requestLength:(NSTimeInterval)requestLength - error:(NSError *)error - resultType:(PINRemoteImageResultType)resultType - UUID:(NSUUID *)uuid - renderedImageQuality:(CGFloat)renderedImageQuality ++ (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error + renderedImageQuality:(CGFloat)renderedImageQuality { return [[self alloc] initWithImage:image alternativeRepresentation:alternativeRepresentation @@ -58,6 +63,7 @@ + (instancetype)imageResultWithImage:(PINImage *)image error:error resultType:resultType UUID:uuid + response:response renderedImageQuality:renderedImageQuality bytesSavedByResuming:0]; } @@ -68,6 +74,7 @@ - (instancetype)initWithImage:(PINImage *)image error:(NSError *)error resultType:(PINRemoteImageResultType)resultType UUID:(NSUUID *)uuid + response:(NSURLResponse *)response renderedImageQuality:(CGFloat)renderedImageQuality bytesSavedByResuming:(NSUInteger)bytesSavedByResuming; { @@ -78,6 +85,7 @@ - (instancetype)initWithImage:(PINImage *)image _error = error; _resultType = resultType; _UUID = uuid; + _response = response; _renderedImageQuality = renderedImageQuality; _bytesSavedByResuming = bytesSavedByResuming; } @@ -99,7 +107,11 @@ - (NSString *)description description = [description stringByAppendingString:@"\n"]; description = [description stringByAppendingString:[NSString stringWithFormat:@"UUID: %@", self.UUID]]; description = [description stringByAppendingString:@"\n"]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"response: %@", self.response]]; + description = [description stringByAppendingString:@"\n"]; description = [description stringByAppendingString:[NSString stringWithFormat:@"renderedImageQuality: %f", self.renderedImageQuality]]; + description = [description stringByAppendingString:@"\n"]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"bytesSavedByResuming: %lu", (unsigned long)self.bytesSavedByResuming]]; return description; } diff --git a/Source/Classes/PINRemoteImageTask.h b/Source/Classes/PINRemoteImageTask.h index f26cd95d..a44a1544 100644 --- a/Source/Classes/PINRemoteImageTask.h +++ b/Source/Classes/PINRemoteImageTask.h @@ -39,6 +39,7 @@ - (void)callCompletionsWithImage:(nullable PINImage *)image alternativeRepresentation:(nullable id)alternativeRepresentation cached:(BOOL)cached + response:(nullable NSURLResponse *)response error:(nullable NSError *)error remove:(BOOL)remove; @@ -50,8 +51,9 @@ - (nonnull PINRemoteImageManagerResult *)imageResultWithImage:(nullable PINImage *)image alternativeRepresentation:(nullable id)alternativeRepresentation requestLength:(NSTimeInterval)requestLength - error:(nullable NSError *)error resultType:(PINRemoteImageResultType)resultType - UUID:(nullable NSUUID *)uuid; + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error; @end diff --git a/Source/Classes/PINRemoteImageTask.m b/Source/Classes/PINRemoteImageTask.m index f95bdc1a..8eb37017 100644 --- a/Source/Classes/PINRemoteImageTask.m +++ b/Source/Classes/PINRemoteImageTask.m @@ -76,6 +76,7 @@ - (void)l_removeCallbackWithUUID:(NSUUID *)UUID - (void)callCompletionsWithImage:(PINImage *)image alternativeRepresentation:(id)alternativeRepresentation cached:(BOOL)cached + response:(NSURLResponse *)response error:(NSError *)error remove:(BOOL)remove; { @@ -98,9 +99,10 @@ - (void)callCompletionsWithImage:(PINImage *)image completionBlock([self imageResultWithImage:image alternativeRepresentation:alternativeRepresentation requestLength:CACurrentMediaTime() - requestTime - error:error resultType:result - UUID:UUID]); + UUID:UUID + response:response + error:error]); }); } if (remove) { @@ -136,16 +138,18 @@ - (void)setPriority:(PINRemoteImageManagerPriority)priority - (nonnull PINRemoteImageManagerResult *)imageResultWithImage:(nullable PINImage *)image alternativeRepresentation:(nullable id)alternativeRepresentation requestLength:(NSTimeInterval)requestLength - error:(nullable NSError *)error resultType:(PINRemoteImageResultType)resultType UUID:(nullable NSUUID *)UUID + response:(NSURLResponse *)response + error:(nullable NSError *)error { return [PINRemoteImageManagerResult imageResultWithImage:image alternativeRepresentation:alternativeRepresentation requestLength:requestLength - error:error resultType:resultType - UUID:UUID]; + UUID:UUID + response:response + error:error]; } - (NSMutableDictionary *)l_callbackBlocks diff --git a/Tests/PINRemoteImageTests.m b/Tests/PINRemoteImageTests.m index e86ac6f8..fa4b5e89 100644 --- a/Tests/PINRemoteImageTests.m +++ b/Tests/PINRemoteImageTests.m @@ -191,7 +191,6 @@ - (NSURL *)progressiveURL - (void)didReceiveData:(NSData *)data forTask:(NSURLSessionTask *)task { - [self.data appendData:data]; self.task = task; } @@ -204,7 +203,6 @@ - (void)didCompleteTask:(NSURLSessionTask *)task withError:(NSError *)error - (void)setUp { [super setUp]; - self.data = [[NSMutableData alloc] init]; // Put setup code here. This method is called before the invocation of each test method in the class. self.imageManager = [[PINRemoteImageManager alloc] init]; [self.imageManager.cache removeAllObjects]; @@ -260,11 +258,15 @@ - (void)testCustomHeaderIsAddedToImageRequests configuration.HTTPAdditionalHeaders = @{ @"X-Custom-Header" : @"Custom Header Value" }; self.imageManager = [[PINRemoteImageManager alloc] initWithSessionConfiguration:configuration]; self.imageManager.sessionManager.delegate = self; + + //sleep for a moment so values have a chance to asynchronously set. + usleep(10000); + [self.imageManager downloadImageWithURL:[self headersURL] options:PINRemoteImageManagerDownloadOptionsNone completion:^(PINRemoteImageManagerResult *result) { - NSDictionary *headers = [[NSJSONSerialization JSONObjectWithData:self.data options:NSJSONReadingMutableContainers error:nil] valueForKey:@"headers"]; + NSDictionary *headers = [self.task.currentRequest allHTTPHeaderFields]; XCTAssert([headers[@"X-Custom-Header"] isEqualToString:@"Custom Header Value"]); @@ -273,35 +275,6 @@ - (void)testCustomHeaderIsAddedToImageRequests [self waitForExpectationsWithTimeout:[self timeoutTimeInterval] handler:nil]; } -- (void)testCustomRequestHeaderIsAddedToImageRequests -{ - XCTestExpectation *expectation = [self expectationWithDescription:@"Custom request header was added to image request"]; - NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; - configuration.HTTPAdditionalHeaders = @{ @"X-Custom-Header" : @"Custom Header Value" }; - - self.imageManager = [[PINRemoteImageManager alloc] initWithSessionConfiguration:configuration]; - [self.imageManager setValue:@"Should not be overrided" forHTTPHeaderField:@"X-Custom-Header"]; - [self.imageManager setValue:@"Custom Request Header" forHTTPHeaderField:@"X-Custom-Request-Header"]; - [self.imageManager setValue:@"Custom Request Header 2" forHTTPHeaderField:@"X-Custom-Request-Header-2"]; - [self.imageManager setValue:nil forHTTPHeaderField:@"X-Custom-Request-Header-2"]; - self.imageManager.sessionManager.delegate = self; - - //sleep for a moment so values have a chance to asynchronously set. - usleep(10000); - - [self.imageManager downloadImageWithURL:[self progressiveURL] - options:PINRemoteImageManagerDownloadOptionsNone - completion:^(PINRemoteImageManagerResult *result) - { - NSDictionary *headers = [self.task.originalRequest allHTTPHeaderFields]; - XCTAssert([headers[@"X-Custom-Header"] isEqualToString:@"Should not be overrided"]); - XCTAssert([headers[@"X-Custom-Request-Header"] isEqualToString:@"Custom Request Header"]); - XCTAssert(headers[@"X-Custom-Request-Header-2"] == nil); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[self timeoutTimeInterval] handler:nil]; -} - - (void)testRequestConfigurationIsUsedForImageRequest { XCTestExpectation *expectation = [self expectationWithDescription:@"Requestion configuration block was called image request"]; @@ -314,13 +287,16 @@ - (void)testRequestConfigurationIsUsedForImageRequest [mutableRequest setValue:@"Custom Header 2 Value" forHTTPHeaderField:@"X-Custom-Header-2"]; return mutableRequest; }]; - + + //sleep for a moment so values have a chance to asynchronously set. + usleep(10000); + self.imageManager.sessionManager.delegate = self; [self.imageManager downloadImageWithURL:[self headersURL] options:PINRemoteImageManagerDownloadOptionsNone completion:^(PINRemoteImageManagerResult *result) { - NSDictionary *headers = [[NSJSONSerialization JSONObjectWithData:self.data options:NSJSONReadingMutableContainers error:nil] valueForKey:@"headers"]; + NSDictionary *headers = [self.task.currentRequest allHTTPHeaderFields]; XCTAssert([headers[@"X-Custom-Header"] isEqualToString:@"Custom Header Value"]); XCTAssert([headers[@"X-Custom-Header-2"] isEqualToString:@"Custom Header 2 Value"]); @@ -329,6 +305,18 @@ - (void)testRequestConfigurationIsUsedForImageRequest [self waitForExpectationsWithTimeout:[self timeoutTimeInterval] handler:nil]; } +- (void)testResponseHeadersReturned +{ + XCTestExpectation *expectation = [self expectationWithDescription:@"Headers returned in image download result"]; + [self.imageManager downloadImageWithURL:[self JPEGURL] completion:^(PINRemoteImageManagerResult * _Nonnull result) { + NSDictionary *headers = [(NSHTTPURLResponse *)result.response allHeaderFields]; + XCTAssert(headers != nil, @"Expected headers back"); + + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[self timeoutTimeInterval] handler:nil]; +} + - (void)testSkipFLAnimatedImageDownload { XCTestExpectation *expectation = [self expectationWithDescription:@"Download animated image"];