Merge branch 'master' into 4.x
+ SDWebImageDownloader downloadImageWithURL:... properly use the weakself-strongself pattern # Conflicts: # .travis.yml # Examples/SDWebImage Demo.xcodeproj/project.pbxproj # README.md # SDWebImage.xcodeproj/project.pbxproj # SDWebImage/SDWebImageDownloader.m # SDWebImage/SDWebImageDownloaderOperation.h # SDWebImage/SDWebImageDownloaderOperation.m # SDWebImage/SDWebImageManager.m # Tests/Podfile # Tests/SDWebImage Tests.xcodeproj/project.pbxproj
Showing
13 changed files
with
306 additions
and
180 deletions
@@ -39,4 +39,4 @@ script: | @@ -39,4 +39,4 @@ script: | ||
39 | - echo Run the tests | 39 | - echo Run the tests |
40 | - pod install --project-directory=Tests | 40 | - pod install --project-directory=Tests |
41 | - xcodebuild clean -workspace SDWebImage.xcworkspace -scheme 'SDWebImage-static' -sdk iphonesimulator PLATFORM_NAME=iphonesimulator -configuration Debug | xcpretty -c | 41 | - xcodebuild clean -workspace SDWebImage.xcworkspace -scheme 'SDWebImage-static' -sdk iphonesimulator PLATFORM_NAME=iphonesimulator -configuration Debug | xcpretty -c |
42 | - - xcodebuild test -workspace SDWebImage.xcworkspace -scheme 'Tests' -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' -configuration Debug | xcpretty -c | ||
42 | + - xcodebuild test -workspace SDWebImage.xcworkspace -scheme 'Tests' -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' -configuration Debug | xcpretty -c |
1 | +## [3.8.0 Minor release - Replaces NSURLConnection (deprecated) with NSURLSession - on Jun 6th, 2016](https://github.com/rs/SDWebImage/releases/tag/3.8.0) | ||
2 | + | ||
3 | +#### Infrastructure: | ||
4 | + | ||
5 | +- Had to update the iOS deployment target to 7.0 from 5 - 6545a3a | ||
6 | + | ||
7 | +#### Features: | ||
8 | + | ||
9 | +- Replace deprecated `NSURLConnection` with `NSURLSession` #1578 #1586 - fixes #1291 #1318 #823 #1566 #1515 | ||
10 | +- Allow to customise cache and image downloader instances used with `SDWebImageManager` 86fc47bf7b - fixes #1398 #870 | ||
11 | + | ||
12 | +#### Fixes: | ||
13 | + | ||
14 | +- Removed the URL query params from the filename (key) fb0cdb6d 1bf62d4 #1584 - fixes #1433 #1533 #1583 #1585 | ||
15 | +- Fixed the WebP build with the official 1.0.0 CocoaPods release f1a471e - fixes #1444 | ||
16 | +- Updated doc: `removeImageForKey:` not synchronous e6e5c51 - fixes #1379 #1415 | ||
17 | + | ||
1 | ## [3.7.6 Patch release for 3.7.0 on May 8th, 2016](https://github.com/rs/SDWebImage/releases/tag/3.7.6) | 18 | ## [3.7.6 Patch release for 3.7.0 on May 8th, 2016](https://github.com/rs/SDWebImage/releases/tag/3.7.6) |
2 | 19 | ||
3 | #### Infrastructure: | 20 | #### Infrastructure: |
@@ -323,7 +323,7 @@ | @@ -323,7 +323,7 @@ | ||
323 | GCC_WARN_ABOUT_RETURN_TYPE = YES; | 323 | GCC_WARN_ABOUT_RETURN_TYPE = YES; |
324 | GCC_WARN_UNINITIALIZED_AUTOS = YES; | 324 | GCC_WARN_UNINITIALIZED_AUTOS = YES; |
325 | GCC_WARN_UNUSED_VARIABLE = YES; | 325 | GCC_WARN_UNUSED_VARIABLE = YES; |
326 | - IPHONEOS_DEPLOYMENT_TARGET = 6.0; | 326 | + IPHONEOS_DEPLOYMENT_TARGET = 7.0; |
327 | ONLY_ACTIVE_ARCH = YES; | 327 | ONLY_ACTIVE_ARCH = YES; |
328 | SDKROOT = iphoneos; | 328 | SDKROOT = iphoneos; |
329 | }; | 329 | }; |
@@ -341,7 +341,7 @@ | @@ -341,7 +341,7 @@ | ||
341 | GCC_WARN_ABOUT_RETURN_TYPE = YES; | 341 | GCC_WARN_ABOUT_RETURN_TYPE = YES; |
342 | GCC_WARN_UNINITIALIZED_AUTOS = YES; | 342 | GCC_WARN_UNINITIALIZED_AUTOS = YES; |
343 | GCC_WARN_UNUSED_VARIABLE = YES; | 343 | GCC_WARN_UNUSED_VARIABLE = YES; |
344 | - IPHONEOS_DEPLOYMENT_TARGET = 6.0; | 344 | + IPHONEOS_DEPLOYMENT_TARGET = 7.0; |
345 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; | 345 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; |
346 | SDKROOT = iphoneos; | 346 | SDKROOT = iphoneos; |
347 | VALIDATE_PRODUCT = YES; | 347 | VALIDATE_PRODUCT = YES; |
@@ -120,8 +120,8 @@ There are three ways to use SDWebImage in your project: | @@ -120,8 +120,8 @@ There are three ways to use SDWebImage in your project: | ||
120 | 120 | ||
121 | #### Podfile | 121 | #### Podfile |
122 | ``` | 122 | ``` |
123 | -platform :ios, '6.1' | ||
124 | -pod 'SDWebImage', '~>3.7' | 123 | +platform :ios, '7.0' |
124 | +pod 'SDWebImage', '~>3.8' | ||
125 | ``` | 125 | ``` |
126 | 126 | ||
127 | If you are using Swift, be sure to add `use_frameworks!` and set your target to iOS 8+: | 127 | If you are using Swift, be sure to add `use_frameworks!` and set your target to iOS 8+: |
1 | Pod::Spec.new do |s| | 1 | Pod::Spec.new do |s| |
2 | s.name = 'SDWebImage' | 2 | s.name = 'SDWebImage' |
3 | - s.version = '3.7.6' | ||
4 | - s.ios.deployment_target = '5.0' | 3 | + s.version = '3.8.0' |
4 | + s.ios.deployment_target = '7.0' | ||
5 | s.tvos.deployment_target = '9.0' | 5 | s.tvos.deployment_target = '9.0' |
6 | s.license = 'MIT' | 6 | s.license = 'MIT' |
7 | s.summary = 'Asynchronous image downloader with cache support with an UIImageView category.' | 7 | s.summary = 'Asynchronous image downloader with cache support with an UIImageView category.' |
@@ -29,7 +29,7 @@ Pod::Spec.new do |s| | @@ -29,7 +29,7 @@ Pod::Spec.new do |s| | ||
29 | end | 29 | end |
30 | 30 | ||
31 | s.subspec 'MapKit' do |mk| | 31 | s.subspec 'MapKit' do |mk| |
32 | - mk.ios.deployment_target = '5.0' | 32 | + mk.ios.deployment_target = '7.0' |
33 | mk.source_files = 'SDWebImage/MKAnnotationView+WebCache.*' | 33 | mk.source_files = 'SDWebImage/MKAnnotationView+WebCache.*' |
34 | mk.framework = 'MapKit' | 34 | mk.framework = 'MapKit' |
35 | mk.dependency 'SDWebImage/Core' | 35 | mk.dependency 'SDWebImage/Core' |
@@ -1396,7 +1396,7 @@ | @@ -1396,7 +1396,7 @@ | ||
1396 | GCC_WARN_UNUSED_PARAMETER = NO; | 1396 | GCC_WARN_UNUSED_PARAMETER = NO; |
1397 | GCC_WARN_UNUSED_VARIABLE = YES; | 1397 | GCC_WARN_UNUSED_VARIABLE = YES; |
1398 | HEADER_SEARCH_PATHS = Vendors/libwebp/src; | 1398 | HEADER_SEARCH_PATHS = Vendors/libwebp/src; |
1399 | - IPHONEOS_DEPLOYMENT_TARGET = 5.0; | 1399 | + IPHONEOS_DEPLOYMENT_TARGET = 7.0; |
1400 | ONLY_ACTIVE_ARCH = YES; | 1400 | ONLY_ACTIVE_ARCH = YES; |
1401 | OTHER_LDFLAGS = "-ObjC"; | 1401 | OTHER_LDFLAGS = "-ObjC"; |
1402 | RUN_CLANG_STATIC_ANALYZER = YES; | 1402 | RUN_CLANG_STATIC_ANALYZER = YES; |
@@ -1451,7 +1451,7 @@ | @@ -1451,7 +1451,7 @@ | ||
1451 | GCC_WARN_UNUSED_PARAMETER = NO; | 1451 | GCC_WARN_UNUSED_PARAMETER = NO; |
1452 | GCC_WARN_UNUSED_VARIABLE = YES; | 1452 | GCC_WARN_UNUSED_VARIABLE = YES; |
1453 | HEADER_SEARCH_PATHS = Vendors/libwebp/src; | 1453 | HEADER_SEARCH_PATHS = Vendors/libwebp/src; |
1454 | - IPHONEOS_DEPLOYMENT_TARGET = 5.0; | 1454 | + IPHONEOS_DEPLOYMENT_TARGET = 7.0; |
1455 | OTHER_LDFLAGS = "-ObjC"; | 1455 | OTHER_LDFLAGS = "-ObjC"; |
1456 | RUN_CLANG_STATIC_ANALYZER = YES; | 1456 | RUN_CLANG_STATIC_ANALYZER = YES; |
1457 | SDKROOT = iphoneos; | 1457 | SDKROOT = iphoneos; |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | @end | 14 | @end |
15 | 15 | ||
16 | 16 | ||
17 | -@interface SDWebImageDownloader () | 17 | +@interface SDWebImageDownloader () <NSURLSessionTaskDelegate, NSURLSessionDataDelegate> |
18 | 18 | ||
19 | @property (strong, nonatomic) NSOperationQueue *downloadQueue; | 19 | @property (strong, nonatomic) NSOperationQueue *downloadQueue; |
20 | @property (weak, nonatomic) NSOperation *lastAddedOperation; | 20 | @property (weak, nonatomic) NSOperation *lastAddedOperation; |
@@ -24,6 +24,9 @@ | @@ -24,6 +24,9 @@ | ||
24 | // This queue is used to serialize the handling of the network responses of all the download operation in a single queue | 24 | // This queue is used to serialize the handling of the network responses of all the download operation in a single queue |
25 | @property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue; | 25 | @property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue; |
26 | 26 | ||
27 | +// The session in which data tasks will run | ||
28 | +@property (strong, nonatomic) NSURLSession *session; | ||
29 | + | ||
27 | @end | 30 | @end |
28 | 31 | ||
29 | @implementation SDWebImageDownloader | 32 | @implementation SDWebImageDownloader |
@@ -75,11 +78,26 @@ | @@ -75,11 +78,26 @@ | ||
75 | #endif | 78 | #endif |
76 | _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); | 79 | _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); |
77 | _downloadTimeout = 15.0; | 80 | _downloadTimeout = 15.0; |
81 | + | ||
82 | + NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; | ||
83 | + sessionConfig.timeoutIntervalForRequest = _downloadTimeout; | ||
84 | + | ||
85 | + /** | ||
86 | + * Create the session for this task | ||
87 | + * We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate | ||
88 | + * method calls and completion handler calls. | ||
89 | + */ | ||
90 | + self.session = [NSURLSession sessionWithConfiguration:sessionConfig | ||
91 | + delegate:self | ||
92 | + delegateQueue:nil]; | ||
78 | } | 93 | } |
79 | return self; | 94 | return self; |
80 | } | 95 | } |
81 | 96 | ||
82 | - (void)dealloc { | 97 | - (void)dealloc { |
98 | + [self.session invalidateAndCancel]; | ||
99 | + self.session = nil; | ||
100 | + | ||
83 | [self.downloadQueue cancelAllOperations]; | 101 | [self.downloadQueue cancelAllOperations]; |
84 | SDDispatchQueueRelease(_barrierQueue); | 102 | SDDispatchQueueRelease(_barrierQueue); |
85 | } | 103 | } |
@@ -120,7 +138,8 @@ | @@ -120,7 +138,8 @@ | ||
120 | __weak SDWebImageDownloader *wself = self; | 138 | __weak SDWebImageDownloader *wself = self; |
121 | 139 | ||
122 | return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{ | 140 | return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{ |
123 | - NSTimeInterval timeoutInterval = wself.downloadTimeout; | 141 | + __strong __typeof (wself) sself = wself; |
142 | + NSTimeInterval timeoutInterval = sself.downloadTimeout; | ||
124 | if (timeoutInterval == 0.0) { | 143 | if (timeoutInterval == 0.0) { |
125 | timeoutInterval = 15.0; | 144 | timeoutInterval = 15.0; |
126 | } | 145 | } |
@@ -129,19 +148,19 @@ | @@ -129,19 +148,19 @@ | ||
129 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; | 148 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; |
130 | request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); | 149 | request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); |
131 | request.HTTPShouldUsePipelining = YES; | 150 | request.HTTPShouldUsePipelining = YES; |
132 | - if (wself.headersFilter) { | ||
133 | - request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]); | 151 | + if (sself.headersFilter) { |
152 | + request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]); | ||
134 | } | 153 | } |
135 | else { | 154 | else { |
136 | - request.allHTTPHeaderFields = wself.HTTPHeaders; | 155 | + request.allHTTPHeaderFields = sself.HTTPHeaders; |
137 | } | 156 | } |
138 | - SDWebImageDownloaderOperation *operation = [[wself.operationClass alloc] initWithRequest:request options:options]; | ||
139 | - operation.shouldDecompressImages = wself.shouldDecompressImages; | 157 | + SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options]; |
158 | + operation.shouldDecompressImages = sself.shouldDecompressImages; | ||
140 | 159 | ||
141 | - if (wself.urlCredential) { | ||
142 | - operation.credential = wself.urlCredential; | ||
143 | - } else if (wself.username && wself.password) { | ||
144 | - operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession]; | 160 | + if (sself.urlCredential) { |
161 | + operation.credential = sself.urlCredential; | ||
162 | + } else if (sself.username && sself.password) { | ||
163 | + operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession]; | ||
145 | } | 164 | } |
146 | 165 | ||
147 | if (options & SDWebImageDownloaderHighPriority) { | 166 | if (options & SDWebImageDownloaderHighPriority) { |
@@ -150,11 +169,11 @@ | @@ -150,11 +169,11 @@ | ||
150 | operation.queuePriority = NSOperationQueuePriorityLow; | 169 | operation.queuePriority = NSOperationQueuePriorityLow; |
151 | } | 170 | } |
152 | 171 | ||
153 | - [wself.downloadQueue addOperation:operation]; | ||
154 | - if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { | 172 | + [sself.downloadQueue addOperation:operation]; |
173 | + if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { | ||
155 | // Emulate LIFO execution order by systematically adding new operations as last operation's dependency | 174 | // Emulate LIFO execution order by systematically adding new operations as last operation's dependency |
156 | - [wself.lastAddedOperation addDependency:operation]; | ||
157 | - wself.lastAddedOperation = operation; | 175 | + [sself.lastAddedOperation addDependency:operation]; |
176 | + sself.lastAddedOperation = operation; | ||
158 | } | 177 | } |
159 | 178 | ||
160 | return operation; | 179 | return operation; |
@@ -218,4 +237,66 @@ | @@ -218,4 +237,66 @@ | ||
218 | [self.downloadQueue cancelAllOperations]; | 237 | [self.downloadQueue cancelAllOperations]; |
219 | } | 238 | } |
220 | 239 | ||
240 | +#pragma mark Helper methods | ||
241 | + | ||
242 | +- (SDWebImageDownloaderOperation *)operationWithTask:(NSURLSessionTask *)task { | ||
243 | + SDWebImageDownloaderOperation *returnOperation = nil; | ||
244 | + for (SDWebImageDownloaderOperation *operation in self.downloadQueue.operations) { | ||
245 | + if (operation.dataTask.taskIdentifier == task.taskIdentifier) { | ||
246 | + returnOperation = operation; | ||
247 | + break; | ||
248 | + } | ||
249 | + } | ||
250 | + return returnOperation; | ||
251 | +} | ||
252 | + | ||
253 | +#pragma mark NSURLSessionDataDelegate | ||
254 | + | ||
255 | +- (void)URLSession:(NSURLSession *)session | ||
256 | + dataTask:(NSURLSessionDataTask *)dataTask | ||
257 | +didReceiveResponse:(NSURLResponse *)response | ||
258 | + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { | ||
259 | + | ||
260 | + // Identify the operation that runs this task and pass it the delegate method | ||
261 | + SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask]; | ||
262 | + | ||
263 | + [dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler]; | ||
264 | +} | ||
265 | + | ||
266 | +- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { | ||
267 | + | ||
268 | + // Identify the operation that runs this task and pass it the delegate method | ||
269 | + SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask]; | ||
270 | + | ||
271 | + [dataOperation URLSession:session dataTask:dataTask didReceiveData:data]; | ||
272 | +} | ||
273 | + | ||
274 | +- (void)URLSession:(NSURLSession *)session | ||
275 | + dataTask:(NSURLSessionDataTask *)dataTask | ||
276 | + willCacheResponse:(NSCachedURLResponse *)proposedResponse | ||
277 | + completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler { | ||
278 | + | ||
279 | + // Identify the operation that runs this task and pass it the delegate method | ||
280 | + SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask]; | ||
281 | + | ||
282 | + [dataOperation URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler]; | ||
283 | +} | ||
284 | + | ||
285 | +#pragma mark NSURLSessionTaskDelegate | ||
286 | + | ||
287 | +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { | ||
288 | + // Identify the operation that runs this task and pass it the delegate method | ||
289 | + SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task]; | ||
290 | + | ||
291 | + [dataOperation URLSession:session task:task didCompleteWithError:error]; | ||
292 | +} | ||
293 | + | ||
294 | +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { | ||
295 | + | ||
296 | + // Identify the operation that runs this task and pass it the delegate method | ||
297 | + SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task]; | ||
298 | + | ||
299 | + [dataOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler]; | ||
300 | +} | ||
301 | + | ||
221 | @end | 302 | @end |
@@ -15,22 +15,26 @@ extern NSString *const SDWebImageDownloadReceiveResponseNotification; | @@ -15,22 +15,26 @@ extern NSString *const SDWebImageDownloadReceiveResponseNotification; | ||
15 | extern NSString *const SDWebImageDownloadStopNotification; | 15 | extern NSString *const SDWebImageDownloadStopNotification; |
16 | extern NSString *const SDWebImageDownloadFinishNotification; | 16 | extern NSString *const SDWebImageDownloadFinishNotification; |
17 | 17 | ||
18 | -@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageOperation> | 18 | +@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageOperation, NSURLSessionTaskDelegate, NSURLSessionDataDelegate> |
19 | 19 | ||
20 | /** | 20 | /** |
21 | - * The request used by the operation's connection. | 21 | + * The request used by the operation's task. |
22 | */ | 22 | */ |
23 | @property (strong, nonatomic, readonly) NSURLRequest *request; | 23 | @property (strong, nonatomic, readonly) NSURLRequest *request; |
24 | 24 | ||
25 | +/** | ||
26 | + * The operation's task | ||
27 | + */ | ||
28 | +@property (strong, nonatomic, readonly) NSURLSessionTask *dataTask; | ||
29 | + | ||
25 | 30 | ||
26 | @property (assign, nonatomic) BOOL shouldDecompressImages; | 31 | @property (assign, nonatomic) BOOL shouldDecompressImages; |
27 | 32 | ||
28 | /** | 33 | /** |
29 | - * Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default. | ||
30 | - * | ||
31 | - * This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`. | 34 | + * Was used to determine whether the URL connection should consult the credential storage for authenticating the connection. |
35 | + * @deprecated Not used for a couple of versions | ||
32 | */ | 36 | */ |
33 | -@property (nonatomic, assign) BOOL shouldUseCredentialStorage; | 37 | +@property (nonatomic, assign) BOOL shouldUseCredentialStorage __deprecated_msg("Property deprecated. Does nothing. Kept only for backwards compatibility"); |
34 | 38 | ||
35 | /** | 39 | /** |
36 | * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. | 40 | * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. |
@@ -60,11 +64,13 @@ extern NSString *const SDWebImageDownloadFinishNotification; | @@ -60,11 +64,13 @@ extern NSString *const SDWebImageDownloadFinishNotification; | ||
60 | * @see SDWebImageDownloaderOperation | 64 | * @see SDWebImageDownloaderOperation |
61 | * | 65 | * |
62 | * @param request the URL request | 66 | * @param request the URL request |
67 | + * @param session the URL session in which this operation will run | ||
63 | * @param options downloader options | 68 | * @param options downloader options |
64 | * | 69 | * |
65 | * @return the initialized instance | 70 | * @return the initialized instance |
66 | */ | 71 | */ |
67 | - (instancetype)initWithRequest:(NSURLRequest *)request | 72 | - (instancetype)initWithRequest:(NSURLRequest *)request |
73 | + inSession:(NSURLSession *)session | ||
68 | options:(SDWebImageDownloaderOptions)options NS_DESIGNATED_INITIALIZER; | 74 | options:(SDWebImageDownloaderOptions)options NS_DESIGNATED_INITIALIZER; |
69 | 75 | ||
70 | /** | 76 | /** |
@@ -20,14 +20,22 @@ NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinis | @@ -20,14 +20,22 @@ NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinis | ||
20 | static NSString *const kProgressCallbackKey = @"progress"; | 20 | static NSString *const kProgressCallbackKey = @"progress"; |
21 | static NSString *const kCompletedCallbackKey = @"completed"; | 21 | static NSString *const kCompletedCallbackKey = @"completed"; |
22 | 22 | ||
23 | -@interface SDWebImageDownloaderOperation () <NSURLConnectionDataDelegate> | 23 | +@interface SDWebImageDownloaderOperation () |
24 | 24 | ||
25 | @property (strong, nonatomic) NSMutableArray *callbackBlocks; | 25 | @property (strong, nonatomic) NSMutableArray *callbackBlocks; |
26 | 26 | ||
27 | @property (assign, nonatomic, getter = isExecuting) BOOL executing; | 27 | @property (assign, nonatomic, getter = isExecuting) BOOL executing; |
28 | @property (assign, nonatomic, getter = isFinished) BOOL finished; | 28 | @property (assign, nonatomic, getter = isFinished) BOOL finished; |
29 | @property (strong, nonatomic) NSMutableData *imageData; | 29 | @property (strong, nonatomic) NSMutableData *imageData; |
30 | -@property (strong, nonatomic) NSURLConnection *connection; | 30 | + |
31 | +// This is weak because it is injected by whoever manages this session. If this gets nil-ed out, we won't be able to run | ||
32 | +// the task associated with this operation | ||
33 | +@property (weak, nonatomic) NSURLSession *unownedSession; | ||
34 | +// This is set if we're using not using an injected NSURLSession. We're responsible of invalidating this one | ||
35 | +@property (strong, nonatomic) NSURLSession *ownedSession; | ||
36 | + | ||
37 | +@property (strong, nonatomic, readwrite) NSURLSessionTask *dataTask; | ||
38 | + | ||
31 | @property (strong, atomic) NSThread *thread; | 39 | @property (strong, atomic) NSThread *thread; |
32 | @property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue; | 40 | @property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue; |
33 | 41 | ||
@@ -47,23 +55,22 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -47,23 +55,22 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
47 | @synthesize finished = _finished; | 55 | @synthesize finished = _finished; |
48 | 56 | ||
49 | - (instancetype)init { | 57 | - (instancetype)init { |
50 | - if (self = [self initWithRequest:nil options:0]) { | ||
51 | - } | ||
52 | - return self; | 58 | + return [self initWithRequest:nil inSession:nil options:0]; |
53 | } | 59 | } |
54 | 60 | ||
55 | - (instancetype)initWithRequest:(NSURLRequest *)request | 61 | - (instancetype)initWithRequest:(NSURLRequest *)request |
62 | + inSession:(NSURLSession *)session | ||
56 | options:(SDWebImageDownloaderOptions)options { | 63 | options:(SDWebImageDownloaderOptions)options { |
57 | if ((self = [super init])) { | 64 | if ((self = [super init])) { |
58 | _request = request; | 65 | _request = request; |
59 | _shouldDecompressImages = YES; | 66 | _shouldDecompressImages = YES; |
60 | - _shouldUseCredentialStorage = YES; | ||
61 | _options = options; | 67 | _options = options; |
62 | _callbackBlocks = [NSMutableArray new]; | 68 | _callbackBlocks = [NSMutableArray new]; |
63 | _executing = NO; | 69 | _executing = NO; |
64 | _finished = NO; | 70 | _finished = NO; |
65 | _expectedSize = 0; | 71 | _expectedSize = 0; |
66 | - responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called | 72 | + _unownedSession = session; |
73 | + responseFromCached = YES; // Initially wrong until `- URLSession:dataTask:willCacheResponse:completionHandler: is called or not called | ||
67 | _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue", DISPATCH_QUEUE_CONCURRENT); | 74 | _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue", DISPATCH_QUEUE_CONCURRENT); |
68 | } | 75 | } |
69 | return self; | 76 | return self; |
@@ -134,38 +141,37 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -134,38 +141,37 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
134 | }]; | 141 | }]; |
135 | } | 142 | } |
136 | #endif | 143 | #endif |
137 | - | 144 | + NSURLSession *session = self.unownedSession; |
145 | + if (!self.unownedSession) { | ||
146 | + NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; | ||
147 | + sessionConfig.timeoutIntervalForRequest = 15; | ||
148 | + | ||
149 | + /** | ||
150 | + * Create the session for this task | ||
151 | + * We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate | ||
152 | + * method calls and completion handler calls. | ||
153 | + */ | ||
154 | + self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig | ||
155 | + delegate:self | ||
156 | + delegateQueue:nil]; | ||
157 | + session = self.ownedSession; | ||
158 | + } | ||
159 | + | ||
160 | + self.dataTask = [session dataTaskWithRequest:self.request]; | ||
138 | self.executing = YES; | 161 | self.executing = YES; |
139 | - self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; | ||
140 | self.thread = [NSThread currentThread]; | 162 | self.thread = [NSThread currentThread]; |
141 | } | 163 | } |
164 | + | ||
165 | + [self.dataTask resume]; | ||
142 | 166 | ||
143 | - [self.connection start]; | ||
144 | - | ||
145 | - if (self.connection) { | 167 | + if (self.dataTask) { |
146 | for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) { | 168 | for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) { |
147 | progressBlock(0, NSURLResponseUnknownLength); | 169 | progressBlock(0, NSURLResponseUnknownLength); |
148 | } | 170 | } |
149 | dispatch_async(dispatch_get_main_queue(), ^{ | 171 | dispatch_async(dispatch_get_main_queue(), ^{ |
150 | [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self]; | 172 | [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self]; |
151 | }); | 173 | }); |
152 | - | ||
153 | - if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) { | ||
154 | - // Make sure to run the runloop in our background thread so it can process downloaded data | ||
155 | - // Note: we use a timeout to work around an issue with NSURLConnection cancel under iOS 5 | ||
156 | - // not waking up the runloop, leading to dead threads (see https://github.com/rs/SDWebImage/issues/466) | ||
157 | - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false); | ||
158 | - } | ||
159 | - else { | ||
160 | - CFRunLoopRun(); | ||
161 | - } | ||
162 | - | ||
163 | - if (!self.isFinished) { | ||
164 | - [self.connection cancel]; | ||
165 | - [self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey : self.request.URL}]]; | ||
166 | - } | ||
167 | - } | ||
168 | - else { | 174 | + } else { |
169 | for (SDWebImageDownloaderCompletedBlock completedBlock in [self callbacksForKey:kCompletedCallbackKey]) { | 175 | for (SDWebImageDownloaderCompletedBlock completedBlock in [self callbacksForKey:kCompletedCallbackKey]) { |
170 | completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES); | 176 | completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES); |
171 | } | 177 | } |
@@ -198,15 +204,14 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -198,15 +204,14 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
198 | - (void)cancelInternalAndStop { | 204 | - (void)cancelInternalAndStop { |
199 | if (self.isFinished) return; | 205 | if (self.isFinished) return; |
200 | [self cancelInternal]; | 206 | [self cancelInternal]; |
201 | - CFRunLoopStop(CFRunLoopGetCurrent()); | ||
202 | } | 207 | } |
203 | 208 | ||
204 | - (void)cancelInternal { | 209 | - (void)cancelInternal { |
205 | if (self.isFinished) return; | 210 | if (self.isFinished) return; |
206 | [super cancel]; | 211 | [super cancel]; |
207 | 212 | ||
208 | - if (self.connection) { | ||
209 | - [self.connection cancel]; | 213 | + if (self.dataTask) { |
214 | + [self.dataTask cancel]; | ||
210 | dispatch_async(dispatch_get_main_queue(), ^{ | 215 | dispatch_async(dispatch_get_main_queue(), ^{ |
211 | [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; | 216 | [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; |
212 | }); | 217 | }); |
@@ -230,9 +235,13 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -230,9 +235,13 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
230 | dispatch_barrier_async(self.barrierQueue, ^{ | 235 | dispatch_barrier_async(self.barrierQueue, ^{ |
231 | [self.callbackBlocks removeAllObjects]; | 236 | [self.callbackBlocks removeAllObjects]; |
232 | }); | 237 | }); |
233 | - self.connection = nil; | 238 | + self.dataTask = nil; |
234 | self.imageData = nil; | 239 | self.imageData = nil; |
235 | self.thread = nil; | 240 | self.thread = nil; |
241 | + if (self.ownedSession) { | ||
242 | + [self.ownedSession invalidateAndCancel]; | ||
243 | + self.ownedSession = nil; | ||
244 | + } | ||
236 | } | 245 | } |
237 | 246 | ||
238 | - (void)setFinished:(BOOL)finished { | 247 | - (void)setFinished:(BOOL)finished { |
@@ -251,9 +260,12 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -251,9 +260,12 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
251 | return YES; | 260 | return YES; |
252 | } | 261 | } |
253 | 262 | ||
254 | -#pragma mark NSURLConnection (delegate) | 263 | +#pragma mark NSURLSessionDataDelegate |
255 | 264 | ||
256 | -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { | 265 | +- (void)URLSession:(NSURLSession *)session |
266 | + dataTask:(NSURLSessionDataTask *)dataTask | ||
267 | +didReceiveResponse:(NSURLResponse *)response | ||
268 | + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { | ||
257 | 269 | ||
258 | //'304 Not Modified' is an exceptional one | 270 | //'304 Not Modified' is an exceptional one |
259 | if (![response respondsToSelector:@selector(statusCode)] || (((NSHTTPURLResponse *)response).statusCode < 400 && ((NSHTTPURLResponse *)response).statusCode != 304)) { | 271 | if (![response respondsToSelector:@selector(statusCode)] || (((NSHTTPURLResponse *)response).statusCode < 400 && ((NSHTTPURLResponse *)response).statusCode != 304)) { |
@@ -262,7 +274,7 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -262,7 +274,7 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
262 | for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) { | 274 | for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) { |
263 | progressBlock(0, expected); | 275 | progressBlock(0, expected); |
264 | } | 276 | } |
265 | - | 277 | + |
266 | self.imageData = [[NSMutableData alloc] initWithCapacity:expected]; | 278 | self.imageData = [[NSMutableData alloc] initWithCapacity:expected]; |
267 | self.response = response; | 279 | self.response = response; |
268 | dispatch_async(dispatch_get_main_queue(), ^{ | 280 | dispatch_async(dispatch_get_main_queue(), ^{ |
@@ -277,7 +289,7 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -277,7 +289,7 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
277 | if (code == 304) { | 289 | if (code == 304) { |
278 | [self cancelInternal]; | 290 | [self cancelInternal]; |
279 | } else { | 291 | } else { |
280 | - [self.connection cancel]; | 292 | + [self.dataTask cancel]; |
281 | } | 293 | } |
282 | dispatch_async(dispatch_get_main_queue(), ^{ | 294 | dispatch_async(dispatch_get_main_queue(), ^{ |
283 | [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; | 295 | [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; |
@@ -286,12 +298,15 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -286,12 +298,15 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
286 | for (SDWebImageDownloaderCompletedBlock completedBlock in [self callbacksForKey:kCompletedCallbackKey]) { | 298 | for (SDWebImageDownloaderCompletedBlock completedBlock in [self callbacksForKey:kCompletedCallbackKey]) { |
287 | completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:((NSHTTPURLResponse *)response).statusCode userInfo:nil], YES); | 299 | completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:((NSHTTPURLResponse *)response).statusCode userInfo:nil], YES); |
288 | } | 300 | } |
289 | - CFRunLoopStop(CFRunLoopGetCurrent()); | ||
290 | [self done]; | 301 | [self done]; |
291 | } | 302 | } |
303 | + | ||
304 | + if (completionHandler) { | ||
305 | + completionHandler(NSURLSessionResponseAllow); | ||
306 | + } | ||
292 | } | 307 | } |
293 | 308 | ||
294 | -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { | 309 | +- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { |
295 | [self.imageData appendData:data]; | 310 | [self.imageData appendData:data]; |
296 | 311 | ||
297 | if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) { | 312 | if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) { |
@@ -319,10 +334,9 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -319,10 +334,9 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
319 | // When we draw to Core Graphics, we lose orientation information, | 334 | // When we draw to Core Graphics, we lose orientation information, |
320 | // which means the image below born of initWithCGIImage will be | 335 | // which means the image below born of initWithCGIImage will be |
321 | // oriented incorrectly sometimes. (Unlike the image born of initWithData | 336 | // oriented incorrectly sometimes. (Unlike the image born of initWithData |
322 | - // in connectionDidFinishLoading.) So save it here and pass it on later. | 337 | + // in didCompleteWithError.) So save it here and pass it on later. |
323 | orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)]; | 338 | orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)]; |
324 | } | 339 | } |
325 | - | ||
326 | } | 340 | } |
327 | 341 | ||
328 | if (width + height > 0 && totalSize < self.expectedSize) { | 342 | if (width + height > 0 && totalSize < self.expectedSize) { |
@@ -376,138 +390,143 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -376,138 +390,143 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
376 | } | 390 | } |
377 | } | 391 | } |
378 | 392 | ||
379 | -+ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value { | ||
380 | - switch (value) { | ||
381 | - case 1: | ||
382 | - return UIImageOrientationUp; | ||
383 | - case 3: | ||
384 | - return UIImageOrientationDown; | ||
385 | - case 8: | ||
386 | - return UIImageOrientationLeft; | ||
387 | - case 6: | ||
388 | - return UIImageOrientationRight; | ||
389 | - case 2: | ||
390 | - return UIImageOrientationUpMirrored; | ||
391 | - case 4: | ||
392 | - return UIImageOrientationDownMirrored; | ||
393 | - case 5: | ||
394 | - return UIImageOrientationLeftMirrored; | ||
395 | - case 7: | ||
396 | - return UIImageOrientationRightMirrored; | ||
397 | - default: | ||
398 | - return UIImageOrientationUp; | 393 | +- (void)URLSession:(NSURLSession *)session |
394 | + dataTask:(NSURLSessionDataTask *)dataTask | ||
395 | + willCacheResponse:(NSCachedURLResponse *)proposedResponse | ||
396 | + completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler { | ||
397 | + | ||
398 | + responseFromCached = NO; // If this method is called, it means the response wasn't read from cache | ||
399 | + NSCachedURLResponse *cachedResponse = proposedResponse; | ||
400 | + | ||
401 | + if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) { | ||
402 | + // Prevents caching of responses | ||
403 | + cachedResponse = nil; | ||
404 | + } | ||
405 | + if (completionHandler) { | ||
406 | + completionHandler(cachedResponse); | ||
399 | } | 407 | } |
400 | } | 408 | } |
401 | 409 | ||
402 | -- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image { | ||
403 | - return SDScaledImageForKey(key, image); | ||
404 | -} | 410 | +#pragma mark NSURLSessionTaskDelegate |
405 | 411 | ||
406 | -- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection { | 412 | +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { |
407 | NSArray *completionBlocks = [[self callbacksForKey:kCompletedCallbackKey] copy]; | 413 | NSArray *completionBlocks = [[self callbacksForKey:kCompletedCallbackKey] copy]; |
408 | @synchronized(self) { | 414 | @synchronized(self) { |
409 | - CFRunLoopStop(CFRunLoopGetCurrent()); | ||
410 | self.thread = nil; | 415 | self.thread = nil; |
411 | - self.connection = nil; | 416 | + self.dataTask = nil; |
412 | dispatch_async(dispatch_get_main_queue(), ^{ | 417 | dispatch_async(dispatch_get_main_queue(), ^{ |
413 | [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; | 418 | [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; |
414 | - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:self]; | 419 | + if (!error) { |
420 | + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:self]; | ||
421 | + } | ||
415 | }); | 422 | }); |
416 | } | 423 | } |
417 | 424 | ||
418 | - if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) { | ||
419 | - responseFromCached = NO; | ||
420 | - } | ||
421 | - | ||
422 | - if (completionBlocks.count > 0) { | ||
423 | - if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) { | ||
424 | - for (SDWebImageDownloaderCompletedBlock completionBlock in completionBlocks) { | ||
425 | - completionBlock(nil, nil, nil, YES); | ||
426 | - } | ||
427 | - } else if (self.imageData) { | ||
428 | - UIImage *image = [UIImage sd_imageWithData:self.imageData]; | ||
429 | - NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; | ||
430 | - image = [self scaledImageForKey:key image:image]; | ||
431 | - | ||
432 | - // Do not force decoding animated GIFs | ||
433 | - if (!image.images) { | ||
434 | - if (self.shouldDecompressImages) { | ||
435 | - image = [UIImage decodedImageWithImage:image]; | 425 | + if (error) { |
426 | + for (SDWebImageDownloaderCompletedBlock completionBlock in completionBlocks) { | ||
427 | + completionBlock(nil, nil, error, YES); | ||
428 | + } | ||
429 | + } else { | ||
430 | + if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) { | ||
431 | + responseFromCached = NO; | ||
432 | + } | ||
433 | + | ||
434 | + if (completionBlocks.count > 0) { | ||
435 | + if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) { | ||
436 | + for (SDWebImageDownloaderCompletedBlock completionBlock in completionBlocks) { | ||
437 | + completionBlock(nil, nil, nil, YES); | ||
438 | + } | ||
439 | + } else if (self.imageData) { | ||
440 | + UIImage *image = [UIImage sd_imageWithData:self.imageData]; | ||
441 | + NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; | ||
442 | + image = [self scaledImageForKey:key image:image]; | ||
443 | + | ||
444 | + // Do not force decoding animated GIFs | ||
445 | + if (!image.images) { | ||
446 | + if (self.shouldDecompressImages) { | ||
447 | + image = [UIImage decodedImageWithImage:image]; | ||
448 | + } | ||
436 | } | 449 | } |
437 | - } | ||
438 | - for (SDWebImageDownloaderCompletedBlock completionBlock in completionBlocks) { | ||
439 | if (CGSizeEqualToSize(image.size, CGSizeZero)) { | 450 | if (CGSizeEqualToSize(image.size, CGSizeZero)) { |
440 | - completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}], YES); | 451 | + for (SDWebImageDownloaderCompletedBlock completionBlock in completionBlocks) { |
452 | + completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}], YES); | ||
453 | + } | ||
454 | + } else { | ||
455 | + for (SDWebImageDownloaderCompletedBlock completionBlock in completionBlocks) { | ||
456 | + completionBlock(image, self.imageData, nil, YES); | ||
457 | + } | ||
441 | } | 458 | } |
442 | - else { | ||
443 | - completionBlock(image, self.imageData, nil, YES); | 459 | + } else { |
460 | + for (SDWebImageDownloaderCompletedBlock completionBlock in completionBlocks) { | ||
461 | + completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}], YES); | ||
444 | } | 462 | } |
445 | } | 463 | } |
446 | - } else { | ||
447 | - for (SDWebImageDownloaderCompletedBlock completionBlock in completionBlocks) { | ||
448 | - completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}], YES); | ||
449 | - } | ||
450 | } | 464 | } |
451 | } | 465 | } |
452 | [self done]; | 466 | [self done]; |
453 | } | 467 | } |
454 | 468 | ||
455 | -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { | ||
456 | - @synchronized(self) { | ||
457 | - CFRunLoopStop(CFRunLoopGetCurrent()); | ||
458 | - self.thread = nil; | ||
459 | - self.connection = nil; | ||
460 | - dispatch_async(dispatch_get_main_queue(), ^{ | ||
461 | - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; | ||
462 | - }); | ||
463 | - } | ||
464 | - | ||
465 | - for (SDWebImageDownloaderCompletedBlock completedBlock in [self callbacksForKey:kCompletedCallbackKey]) { | ||
466 | - completedBlock(nil, nil, error, YES); | ||
467 | - } | ||
468 | - self.completionBlock = nil; | ||
469 | - [self done]; | ||
470 | -} | ||
471 | - | ||
472 | -- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { | ||
473 | - responseFromCached = NO; // If this method is called, it means the response wasn't read from cache | ||
474 | - if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) { | ||
475 | - // Prevents caching of responses | ||
476 | - return nil; | ||
477 | - } | ||
478 | - else { | ||
479 | - return cachedResponse; | ||
480 | - } | ||
481 | -} | ||
482 | - | ||
483 | -- (BOOL)shouldContinueWhenAppEntersBackground { | ||
484 | - return self.options & SDWebImageDownloaderContinueInBackground; | ||
485 | -} | ||
486 | - | ||
487 | -- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection { | ||
488 | - return self.shouldUseCredentialStorage; | ||
489 | -} | ||
490 | - | ||
491 | -- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{ | 469 | +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { |
470 | + | ||
471 | + NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; | ||
472 | + __block NSURLCredential *credential = nil; | ||
473 | + | ||
492 | if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { | 474 | if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { |
493 | - if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates) && | ||
494 | - [challenge.sender respondsToSelector:@selector(performDefaultHandlingForAuthenticationChallenge:)]) { | ||
495 | - [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge]; | 475 | + if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates)) { |
476 | + disposition = NSURLSessionAuthChallengePerformDefaultHandling; | ||
496 | } else { | 477 | } else { |
497 | - NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; | ||
498 | - [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; | 478 | + credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; |
479 | + disposition = NSURLSessionAuthChallengeUseCredential; | ||
499 | } | 480 | } |
500 | } else { | 481 | } else { |
501 | if (challenge.previousFailureCount == 0) { | 482 | if (challenge.previousFailureCount == 0) { |
502 | if (self.credential) { | 483 | if (self.credential) { |
503 | - [challenge.sender useCredential:self.credential forAuthenticationChallenge:challenge]; | 484 | + credential = self.credential; |
485 | + disposition = NSURLSessionAuthChallengeUseCredential; | ||
504 | } else { | 486 | } else { |
505 | - [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; | 487 | + disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; |
506 | } | 488 | } |
507 | } else { | 489 | } else { |
508 | - [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; | 490 | + disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; |
509 | } | 491 | } |
510 | } | 492 | } |
493 | + | ||
494 | + if (completionHandler) { | ||
495 | + completionHandler(disposition, credential); | ||
496 | + } | ||
497 | +} | ||
498 | + | ||
499 | +#pragma mark Helper methods | ||
500 | + | ||
501 | ++ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value { | ||
502 | + switch (value) { | ||
503 | + case 1: | ||
504 | + return UIImageOrientationUp; | ||
505 | + case 3: | ||
506 | + return UIImageOrientationDown; | ||
507 | + case 8: | ||
508 | + return UIImageOrientationLeft; | ||
509 | + case 6: | ||
510 | + return UIImageOrientationRight; | ||
511 | + case 2: | ||
512 | + return UIImageOrientationUpMirrored; | ||
513 | + case 4: | ||
514 | + return UIImageOrientationDownMirrored; | ||
515 | + case 5: | ||
516 | + return UIImageOrientationLeftMirrored; | ||
517 | + case 7: | ||
518 | + return UIImageOrientationRightMirrored; | ||
519 | + default: | ||
520 | + return UIImageOrientationUp; | ||
521 | + } | ||
522 | +} | ||
523 | + | ||
524 | +- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image { | ||
525 | + return SDScaledImageForKey(key, image); | ||
526 | +} | ||
527 | + | ||
528 | +- (BOOL)shouldContinueWhenAppEntersBackground { | ||
529 | + return self.options & SDWebImageDownloaderContinueInBackground; | ||
511 | } | 530 | } |
512 | 531 | ||
513 | @end | 532 | @end |
@@ -54,14 +54,17 @@ | @@ -54,14 +54,17 @@ | ||
54 | } | 54 | } |
55 | 55 | ||
56 | - (NSString *)cacheKeyForURL:(NSURL *)url { | 56 | - (NSString *)cacheKeyForURL:(NSURL *)url { |
57 | + if (!url) { | ||
58 | + return @""; | ||
59 | + } | ||
60 | + | ||
57 | if (self.cacheKeyFilter) { | 61 | if (self.cacheKeyFilter) { |
58 | return self.cacheKeyFilter(url); | 62 | return self.cacheKeyFilter(url); |
59 | - } | ||
60 | - else { | 63 | + } else { |
61 | if (NSClassFromString(@"NSURLComponents") && [NSURLComponents instancesRespondToSelector:@selector(string)]) { | 64 | if (NSClassFromString(@"NSURLComponents") && [NSURLComponents instancesRespondToSelector:@selector(string)]) { |
62 | NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO]; | 65 | NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO]; |
63 | urlComponents.query = nil; // Strip out query parameters. | 66 | urlComponents.query = nil; // Strip out query parameters. |
64 | - return urlComponents.string; | 67 | + return urlComponents.URL.absoluteString; |
65 | } else { | 68 | } else { |
66 | return url.absoluteString; | 69 | return url.absoluteString; |
67 | } | 70 | } |
@@ -4,7 +4,7 @@ xcodeproj 'SDWebImage Tests' | @@ -4,7 +4,7 @@ xcodeproj 'SDWebImage Tests' | ||
4 | workspace '../SDWebImage' | 4 | workspace '../SDWebImage' |
5 | 5 | ||
6 | target 'Tests' do | 6 | target 'Tests' do |
7 | - platform :ios, '5.0' | 7 | + platform :ios, '7.0' |
8 | pod 'Expecta', '<=0.3.1' | 8 | pod 'Expecta', '<=0.3.1' |
9 | pod 'SDWebImage', :path => '../' | 9 | pod 'SDWebImage', :path => '../' |
10 | 10 |
@@ -8,8 +8,8 @@ | @@ -8,8 +8,8 @@ | ||
8 | 8 | ||
9 | /* Begin PBXBuildFile section */ | 9 | /* Begin PBXBuildFile section */ |
10 | 0D87E1F83BD319CEC7622E9F /* libPods-Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0462A7F023A057322E59B3C5 /* libPods-Tests.a */; }; | 10 | 0D87E1F83BD319CEC7622E9F /* libPods-Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0462A7F023A057322E59B3C5 /* libPods-Tests.a */; }; |
11 | - 5F7F38AD1AE2A77A00B0E330 /* TestImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5F7F38AC1AE2A77A00B0E330 /* TestImage.jpg */; }; | ||
12 | 1E3C51E919B46E370092B5E6 /* SDWebImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */; }; | 11 | 1E3C51E919B46E370092B5E6 /* SDWebImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */; }; |
12 | + 5F7F38AD1AE2A77A00B0E330 /* TestImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5F7F38AC1AE2A77A00B0E330 /* TestImage.jpg */; }; | ||
13 | DA248D57195472AA00390AB0 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA248D56195472AA00390AB0 /* XCTest.framework */; }; | 13 | DA248D57195472AA00390AB0 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA248D56195472AA00390AB0 /* XCTest.framework */; }; |
14 | DA248D59195472AA00390AB0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA248D58195472AA00390AB0 /* Foundation.framework */; }; | 14 | DA248D59195472AA00390AB0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA248D58195472AA00390AB0 /* Foundation.framework */; }; |
15 | DA248D5B195472AA00390AB0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA248D5A195472AA00390AB0 /* UIKit.framework */; }; | 15 | DA248D5B195472AA00390AB0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA248D5A195472AA00390AB0 /* UIKit.framework */; }; |
@@ -21,10 +21,10 @@ | @@ -21,10 +21,10 @@ | ||
21 | 21 | ||
22 | /* Begin PBXFileReference section */ | 22 | /* Begin PBXFileReference section */ |
23 | 0462A7F023A057322E59B3C5 /* libPods-Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; | 23 | 0462A7F023A057322E59B3C5 /* libPods-Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; |
24 | + 1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDownloaderTests.m; sourceTree = "<group>"; }; | ||
24 | 5F7F38AC1AE2A77A00B0E330 /* TestImage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = TestImage.jpg; sourceTree = "<group>"; }; | 25 | 5F7F38AC1AE2A77A00B0E330 /* TestImage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = TestImage.jpg; sourceTree = "<group>"; }; |
25 | 700B00151041D7EE118B1ABD /* Pods-Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.debug.xcconfig"; sourceTree = "<group>"; }; | 26 | 700B00151041D7EE118B1ABD /* Pods-Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.debug.xcconfig"; sourceTree = "<group>"; }; |
26 | A0085854E7D88C98F2F6C9FC /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.release.xcconfig"; sourceTree = "<group>"; }; | 27 | A0085854E7D88C98F2F6C9FC /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.release.xcconfig"; sourceTree = "<group>"; }; |
27 | - 1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDownloaderTests.m; sourceTree = "<group>"; }; | ||
28 | DA248D53195472AA00390AB0 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; | 28 | DA248D53195472AA00390AB0 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; |
29 | DA248D56195472AA00390AB0 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; | 29 | DA248D56195472AA00390AB0 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; |
30 | DA248D58195472AA00390AB0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; | 30 | DA248D58195472AA00390AB0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; |
@@ -251,7 +251,7 @@ | @@ -251,7 +251,7 @@ | ||
251 | isa = XCBuildConfiguration; | 251 | isa = XCBuildConfiguration; |
252 | buildSettings = { | 252 | buildSettings = { |
253 | ENABLE_TESTABILITY = YES; | 253 | ENABLE_TESTABILITY = YES; |
254 | - IPHONEOS_DEPLOYMENT_TARGET = 6.0; | 254 | + IPHONEOS_DEPLOYMENT_TARGET = 7.0; |
255 | ONLY_ACTIVE_ARCH = YES; | 255 | ONLY_ACTIVE_ARCH = YES; |
256 | }; | 256 | }; |
257 | name = Debug; | 257 | name = Debug; |
@@ -259,7 +259,7 @@ | @@ -259,7 +259,7 @@ | ||
259 | DA248D4B1954721A00390AB0 /* Release */ = { | 259 | DA248D4B1954721A00390AB0 /* Release */ = { |
260 | isa = XCBuildConfiguration; | 260 | isa = XCBuildConfiguration; |
261 | buildSettings = { | 261 | buildSettings = { |
262 | - IPHONEOS_DEPLOYMENT_TARGET = 6.0; | 262 | + IPHONEOS_DEPLOYMENT_TARGET = 7.0; |
263 | }; | 263 | }; |
264 | name = Release; | 264 | name = Release; |
265 | }; | 265 | }; |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | <key>CFBundlePackageType</key> | 15 | <key>CFBundlePackageType</key> |
16 | <string>FMWK</string> | 16 | <string>FMWK</string> |
17 | <key>CFBundleShortVersionString</key> | 17 | <key>CFBundleShortVersionString</key> |
18 | - <string>3.7.6</string> | 18 | + <string>3.8.0</string> |
19 | <key>CFBundleSignature</key> | 19 | <key>CFBundleSignature</key> |
20 | <string>????</string> | 20 | <string>????</string> |
21 | <key>CFBundleVersion</key> | 21 | <key>CFBundleVersion</key> |
-
Please register or login to post a comment