From c951ba4934b544cc313ed98a4b503cb10ac4385b Mon Sep 17 00:00:00 2001 From: onevcat Date: Fri, 14 Aug 2015 10:30:01 +0900 Subject: [PATCH 1/8] Add resource struct --- Kingfisher.xcodeproj/project.pbxproj | 4 +++ Kingfisher/Resource.swift | 45 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 Kingfisher/Resource.swift diff --git a/Kingfisher.xcodeproj/project.pbxproj b/Kingfisher.xcodeproj/project.pbxproj index 8b1f09871..68b02c66e 100644 --- a/Kingfisher.xcodeproj/project.pbxproj +++ b/Kingfisher.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 0D9C68098E20AB4F19D7C313 /* libPods-KingfisherTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A9E621E297FEFAD35D39C34E /* libPods-KingfisherTests.a */; }; + 4B2C4DF81B7D7ACD000912CA /* Resource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2C4DF71B7D7ACD000912CA /* Resource.swift */; }; 4B3E714F1B02005900F5AAED /* WatchKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B3E714D1B01FEB200F5AAED /* WatchKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 4B412CA51AE8A2F9008D530A /* KingfisherOptionsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B412CA41AE8A2F9008D530A /* KingfisherOptionsInfo.swift */; }; 4B6D4F651AE0A46D0084D15B /* UIImageViewExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6D4F641AE0A46D0084D15B /* UIImageViewExtensionTests.swift */; }; @@ -107,6 +108,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4B2C4DF71B7D7ACD000912CA /* Resource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Resource.swift; sourceTree = ""; }; 4B3E714D1B01FEB200F5AAED /* WatchKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WatchKit.framework; path = System/Library/Frameworks/WatchKit.framework; sourceTree = SDKROOT; }; 4B412CA41AE8A2F9008D530A /* KingfisherOptionsInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KingfisherOptionsInfo.swift; sourceTree = ""; }; 4B6D4F641AE0A46D0084D15B /* UIImageViewExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewExtensionTests.swift; sourceTree = ""; }; @@ -293,6 +295,7 @@ D1ED2D571AD2D0F900CFC3EB /* ImageCache.swift */, D1ED2D581AD2D0F900CFC3EB /* ImageDownloader.swift */, D1ED2D591AD2D0F900CFC3EB /* KingfisherManager.swift */, + 4B2C4DF71B7D7ACD000912CA /* Resource.swift */, D1ED2D5A1AD2D0F900CFC3EB /* KingfisherOptions.swift */, 4B412CA41AE8A2F9008D530A /* KingfisherOptionsInfo.swift */, D1ED2D371AD2D09F00CFC3EB /* Supporting Files */, @@ -605,6 +608,7 @@ 4BD352F31AF36A0700B18A40 /* WKInterfaceImage+Kingfisher.swift in Sources */, D1ED2D5E1AD2D0F900CFC3EB /* ImageDownloader.swift in Sources */, 4B412CA51AE8A2F9008D530A /* KingfisherOptionsInfo.swift in Sources */, + 4B2C4DF81B7D7ACD000912CA /* Resource.swift in Sources */, D1ED2D601AD2D0F900CFC3EB /* KingfisherOptions.swift in Sources */, D151E72B1AD3C48D004FD4AE /* UIImage+Decode.swift in Sources */, ); diff --git a/Kingfisher/Resource.swift b/Kingfisher/Resource.swift new file mode 100644 index 000000000..8c8f1cdae --- /dev/null +++ b/Kingfisher/Resource.swift @@ -0,0 +1,45 @@ +// +// Resource.swift +// Kingfisher +// +// Created by Wei Wang on 15/4/6. +// +// Copyright (c) 2015 Wei Wang +// +// 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 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +struct Resource { + let cacheKey: String + let downloadURL: NSURL +} + +extension Resource: DictionaryLiteralConvertible { + typealias Key = String + typealias Value = NSURL + + init(dictionaryLiteral elements: (Key, Value)...) { + assert(elements.count == 1, "There should be one and only one pair of cacheKey and downloadURL passed to create a Resource") + + cacheKey = elements.first!.0 + downloadURL = elements.first!.1 + + } +} \ No newline at end of file From c1dd90bc62011015075953486d4e8b4644ce329e Mon Sep 17 00:00:00 2001 From: onevcat Date: Fri, 14 Aug 2015 10:32:19 +0900 Subject: [PATCH 2/8] Mark public --- Kingfisher/Resource.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Kingfisher/Resource.swift b/Kingfisher/Resource.swift index 8c8f1cdae..0fa007ea5 100644 --- a/Kingfisher/Resource.swift +++ b/Kingfisher/Resource.swift @@ -26,16 +26,16 @@ import Foundation -struct Resource { - let cacheKey: String - let downloadURL: NSURL +public struct Resource { + public let cacheKey: String + public let downloadURL: NSURL } extension Resource: DictionaryLiteralConvertible { - typealias Key = String - typealias Value = NSURL + public typealias Key = String + public typealias Value = NSURL - init(dictionaryLiteral elements: (Key, Value)...) { + public init(dictionaryLiteral elements: (Key, Value)...) { assert(elements.count == 1, "There should be one and only one pair of cacheKey and downloadURL passed to create a Resource") cacheKey = elements.first!.0 From 2e20232e3d4ee7ea8faca6c527f0fc8742f8fc71 Mon Sep 17 00:00:00 2001 From: onevcat Date: Fri, 14 Aug 2015 11:01:13 +0900 Subject: [PATCH 3/8] Remove DictionaryLiteralConvertible Since it could lead some misunderstanding. --- Kingfisher/Resource.swift | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Kingfisher/Resource.swift b/Kingfisher/Resource.swift index 0fa007ea5..7e53d6897 100644 --- a/Kingfisher/Resource.swift +++ b/Kingfisher/Resource.swift @@ -29,17 +29,9 @@ import Foundation public struct Resource { public let cacheKey: String public let downloadURL: NSURL -} - -extension Resource: DictionaryLiteralConvertible { - public typealias Key = String - public typealias Value = NSURL - public init(dictionaryLiteral elements: (Key, Value)...) { - assert(elements.count == 1, "There should be one and only one pair of cacheKey and downloadURL passed to create a Resource") - - cacheKey = elements.first!.0 - downloadURL = elements.first!.1 - + init(downloadURL: NSURL, cacheKey: String? = nil) { + self.downloadURL = downloadURL + self.cacheKey = cacheKey ?? downloadURL.absoluteString! } } \ No newline at end of file From 57070f10424f560632e9efe89741bf36d570a644 Mon Sep 17 00:00:00 2001 From: onevcat Date: Fri, 14 Aug 2015 11:01:23 +0900 Subject: [PATCH 4/8] Update APIs for resource --- Kingfisher/KingfisherManager.swift | 97 +++++++++++++--------- Kingfisher/UIImageView+Kingfisher.swift | 106 +++++++++++++++++++++--- 2 files changed, 154 insertions(+), 49 deletions(-) diff --git a/Kingfisher/KingfisherManager.swift b/Kingfisher/KingfisherManager.swift index 961353de3..e51ee1755 100644 --- a/Kingfisher/KingfisherManager.swift +++ b/Kingfisher/KingfisherManager.swift @@ -95,24 +95,24 @@ public class KingfisherManager { cache = ImageCache.defaultCache downloader = ImageDownloader.defaultDownloader } - + /** - Get an image with URL as the key. + Get an image with resource. If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first. - If not found, it will download the image at URL and cache it. + If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`. These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more. - :param: URL The image URL. + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. :param: progressBlock Called every time downloaded data changed. This could be used as a progress UI. :param: completionHandler Called when the whole retriving process finished. :returns: A `RetrieveImageTask` task object. You can use this object to cancel the task. */ - public func retrieveImageWithURL(URL: NSURL, - optionsInfo: KingfisherOptionsInfo?, - progressBlock: DownloadProgressBlock?, - completionHandler: CompletionHandler?) -> RetrieveImageTask + public func retrieveImageWithResource(resource: Resource, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask { func parseOptionsInfo(optionsInfo: KingfisherOptionsInfo?) -> (Options, ImageCache, ImageDownloader) { let options: Options @@ -142,42 +142,61 @@ public class KingfisherManager { let parsedOptions = parseOptionsInfo(optionsInfo) let (options, targetCache, downloader) = (parsedOptions.0, parsedOptions.1, parsedOptions.2) - if let key = URL.absoluteString { - if options.forceRefresh { - downloadAndCacheImageWithURL(URL, - forKey: key, - retrieveImageTask: task, - progressBlock: progressBlock, - completionHandler: completionHandler, - options: options, - targetCache: targetCache, - downloader: downloader) - } else { - let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> () in - // Break retain cycle created inside diskTask closure below - task.diskRetrieveTask = nil - completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL) - } - let diskTask = targetCache.retrieveImageForKey(key, options: options, completionHandler: { (image, cacheType) -> () in - if image != nil { - diskTaskCompletionHandler(image: image, error: nil, cacheType:cacheType, imageURL: URL) - } else { - self.downloadAndCacheImageWithURL(URL, - forKey: key, - retrieveImageTask: task, - progressBlock: progressBlock, - completionHandler: diskTaskCompletionHandler, - options: options, - targetCache: targetCache, - downloader: downloader) - } - }) - task.diskRetrieveTask = diskTask + if options.forceRefresh { + downloadAndCacheImageWithURL(resource.downloadURL, + forKey: resource.cacheKey, + retrieveImageTask: task, + progressBlock: progressBlock, + completionHandler: completionHandler, + options: options, + targetCache: targetCache, + downloader: downloader) + } else { + let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> () in + // Break retain cycle created inside diskTask closure below + task.diskRetrieveTask = nil + completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL) } + let diskTask = targetCache.retrieveImageForKey(resource.cacheKey, options: options, completionHandler: { (image, cacheType) -> () in + if image != nil { + diskTaskCompletionHandler(image: image, error: nil, cacheType:cacheType, imageURL: resource.downloadURL) + } else { + self.downloadAndCacheImageWithURL(resource.downloadURL, + forKey: resource.cacheKey, + retrieveImageTask: task, + progressBlock: progressBlock, + completionHandler: diskTaskCompletionHandler, + options: options, + targetCache: targetCache, + downloader: downloader) + } + }) + task.diskRetrieveTask = diskTask } return task } + + /** + Get an image with URL as the key. + If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first. + If not found, it will download the image at URL and cache it. + These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more. + + :param: URL The image URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: progressBlock Called every time downloaded data changed. This could be used as a progress UI. + :param: completionHandler Called when the whole retriving process finished. + + :returns: A `RetrieveImageTask` task object. You can use this object to cancel the task. + */ + public func retrieveImageWithURL(URL: NSURL, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return retrieveImageWithResource(Resource(downloadURL: URL), optionsInfo: optionsInfo, progressBlock: progressBlock, completionHandler: completionHandler) + } func downloadAndCacheImageWithURL(URL: NSURL, forKey key: String, diff --git a/Kingfisher/UIImageView+Kingfisher.swift b/Kingfisher/UIImageView+Kingfisher.swift index 2ae26466e..ada79aa67 100644 --- a/Kingfisher/UIImageView+Kingfisher.swift +++ b/Kingfisher/UIImageView+Kingfisher.swift @@ -33,6 +33,20 @@ import Foundation public extension UIImageView { + /** + Set an image with a resource. + It will ask for Kingfisher's manager to get the image for the `cacheKey` property in `resource`. + The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `cacheKey` for next use. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } + /** Set an image with a URL. It will ask for Kingfisher's manager to get the image for the URL. @@ -46,6 +60,20 @@ public extension UIImageView { { return kf_setImageWithURL(URL, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + + /** + Set an image with a resource and a placeholder image. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: placeholderImage A placeholder image when retrieving the image at URL. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + placeholderImage: UIImage?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } /** Set an image with a URL and a placeholder image. @@ -61,6 +89,22 @@ public extension UIImageView { return kf_setImageWithURL(URL, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + /** + Set an image with a resource, a placaholder image and options. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) + } + /** Set an image with a URL, a placaholder image and options. @@ -77,6 +121,24 @@ public extension UIImageView { return kf_setImageWithURL(URL, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) } + /** + Set an image with a resource, a placeholder image, options and completion handler. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: completionHandler) + } + /** Set an image with a URL, a placeholder image, options and completion handler. @@ -98,7 +160,7 @@ public extension UIImageView { /** Set an image with a URL, a placeholder image, options, progress handler and completion handler. - :param: URL The URL of image. + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. :param: placeholderImage A placeholder image when retrieving the image at URL. :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. :param: progressBlock Called when the image downloading progress gets updated. @@ -106,12 +168,11 @@ public extension UIImageView { :returns: A task represents the retriving process. */ - - public func kf_setImageWithURL(URL: NSURL, - placeholderImage: UIImage?, - optionsInfo: KingfisherOptionsInfo?, - progressBlock: DownloadProgressBlock?, - completionHandler: CompletionHandler?) -> RetrieveImageTask + public func kf_setImageWithResource(resource: Resource, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask { let showIndicatorWhenLoading = kf_showIndicatorWhenLoading var indicator: UIActivityIndicatorView? = nil @@ -123,8 +184,8 @@ public extension UIImageView { image = placeholderImage - kf_setWebURL(URL) - let task = KingfisherManager.sharedManager.retrieveImageWithURL(URL, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in + kf_setWebURL(resource.downloadURL) + let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in if let progressBlock = progressBlock { dispatch_async(dispatch_get_main_queue(), { () -> Void in progressBlock(receivedSize: receivedSize, totalSize: totalSize) @@ -137,13 +198,38 @@ public extension UIImageView { sSelf.image = image; indicator?.stopAnimating() } - + completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL) }) }) return task } + + /** + Set an image with a URL, a placeholder image, options, progress handler and completion handler. + + :param: URL The URL of image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: progressBlock Called when the image downloading progress gets updated. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + + public func kf_setImageWithURL(URL: NSURL, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setImageWithResource(Resource(downloadURL: URL), + placeholderImage: placeholderImage, + optionsInfo: optionsInfo, + progressBlock: progressBlock, + completionHandler: completionHandler) + } } // MARK: - Associated Object From d496574b70d1dac0efe619c38433d2c81f1949cb Mon Sep 17 00:00:00 2001 From: onevcat Date: Fri, 14 Aug 2015 12:16:44 +0900 Subject: [PATCH 5/8] Resource APIs for button extension --- Kingfisher.xcodeproj/project.pbxproj | 2 +- Kingfisher/UIButton+Kingfisher.swift | 253 ++++++++++++++++++++++++--- 2 files changed, 226 insertions(+), 29 deletions(-) diff --git a/Kingfisher.xcodeproj/project.pbxproj b/Kingfisher.xcodeproj/project.pbxproj index 68b02c66e..1eaed4bd3 100644 --- a/Kingfisher.xcodeproj/project.pbxproj +++ b/Kingfisher.xcodeproj/project.pbxproj @@ -295,9 +295,9 @@ D1ED2D571AD2D0F900CFC3EB /* ImageCache.swift */, D1ED2D581AD2D0F900CFC3EB /* ImageDownloader.swift */, D1ED2D591AD2D0F900CFC3EB /* KingfisherManager.swift */, - 4B2C4DF71B7D7ACD000912CA /* Resource.swift */, D1ED2D5A1AD2D0F900CFC3EB /* KingfisherOptions.swift */, 4B412CA41AE8A2F9008D530A /* KingfisherOptionsInfo.swift */, + 4B2C4DF71B7D7ACD000912CA /* Resource.swift */, D1ED2D371AD2D09F00CFC3EB /* Supporting Files */, ); path = Kingfisher; diff --git a/Kingfisher/UIButton+Kingfisher.swift b/Kingfisher/UIButton+Kingfisher.swift index 057279551..e58c4acd6 100644 --- a/Kingfisher/UIButton+Kingfisher.swift +++ b/Kingfisher/UIButton+Kingfisher.swift @@ -30,6 +30,23 @@ import Foundation * Set image to use from web for a specified state. */ public extension UIButton { + + /** + Set an image to use for a specified state with a resource. + It will ask for Kingfisher's manager to get the image for the `cacheKey` property in `resource` and then set it for a button state. + The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `cacheKey` for next use. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + forState state: UIControlState) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, forState: state, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } + /** Set an image to use for a specified state with a URL. It will ask for Kingfisher's manager to get the image for the URL and then set it for a button state. @@ -45,6 +62,22 @@ public extension UIButton { { return kf_setImageWithURL(URL, forState: state, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + + /** + Set an image to use for a specified state with a resource and a placeholder image. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } /** Set an image to use for a specified state with a URL and a placeholder image. @@ -61,6 +94,24 @@ public extension UIButton { { return kf_setImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + + /** + Set an image to use for a specified state with a resource, a placeholder image and options. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) + } /** Set an image to use for a specified state with a URL, a placeholder image and options. @@ -79,6 +130,26 @@ public extension UIButton { { return kf_setImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) } + + /** + Set an image to use for a specified state with a resource, a placeholder image, options and completion handler. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: completionHandler) + } /** Set an image to use for a specified state with a URL, a placeholder image, options and completion handler. @@ -99,11 +170,12 @@ public extension UIButton { { return kf_setImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: completionHandler) } + /** - Set an image to use for a specified state with a URL, a placeholder image, options, progress handler and completion handler. + Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and completion handler. - :param: URL The URL of image for specified state. + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. :param: state The state that uses the specified image. :param: placeholderImage A placeholder image when retrieving the image at URL. :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. @@ -112,32 +184,59 @@ public extension UIButton { :returns: A task represents the retriving process. */ - public func kf_setImageWithURL(URL: NSURL, - forState state: UIControlState, - placeholderImage: UIImage?, - optionsInfo: KingfisherOptionsInfo?, - progressBlock: DownloadProgressBlock?, - completionHandler: CompletionHandler?) -> RetrieveImageTask + public func kf_setImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask { setImage(placeholderImage, forState: state) - kf_setWebURL(URL, forState: state) - let task = KingfisherManager.sharedManager.retrieveImageWithURL(URL, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in + kf_setWebURL(resource.downloadURL, forState: state) + let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in if let progressBlock = progressBlock { dispatch_async(dispatch_get_main_queue(), { () -> Void in progressBlock(receivedSize: receivedSize, totalSize: totalSize) }) } - }) { (image, error, cacheType, imageURL) -> () in - dispatch_async(dispatch_get_main_queue(), { () -> Void in - if (imageURL == self.kf_webURLForState(state) && image != nil) { - self.setImage(image, forState: state) - } - completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL) - }) + }) { (image, error, cacheType, imageURL) -> () in + dispatch_async(dispatch_get_main_queue(), { () -> Void in + if (imageURL == self.kf_webURLForState(state) && image != nil) { + self.setImage(image, forState: state) + } + completionHandler?(image: image, error: error, cacheType: cacheType, imageURL: imageURL) + }) } return task } + + /** + Set an image to use for a specified state with a URL, a placeholder image, options, progress handler and completion handler. + + :param: URL The URL of image for specified state. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: progressBlock Called when the image downloading progress gets updated. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + public func kf_setImageWithURL(URL: NSURL, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setImageWithResource(Resource(downloadURL: URL), + forState: state, + placeholderImage: placeholderImage, + optionsInfo: optionsInfo, + progressBlock: progressBlock, + completionHandler: completionHandler) + } } private var lastURLKey: Void? @@ -177,6 +276,22 @@ public extension UIButton { * Set background image to use from web for a specified state. */ public extension UIButton { + /** + Set the background image to use for a specified state with a resource. + It will ask for Kingfisher's manager to get the image for the `cacheKey` property in `resource` and then set it for a button state. + The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `cacheKey` for next use. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + + :returns: A task represents the retriving process. + */ + public func kf_setBackgroundImageWithResource(resource: Resource, + forState state: UIControlState) -> RetrieveImageTask + { + return kf_setBackgroundImageWithResource(resource, forState: state, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } + /** Set the background image to use for a specified state with a URL. It will ask for Kingfisher's manager to get the image for the URL and then set it for a button state. @@ -192,6 +307,22 @@ public extension UIButton { { return kf_setBackgroundImageWithURL(URL, forState: state, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + + /** + Set the background image to use for a specified state with a resource and a placeholder image. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + + :returns: A task represents the retriving process. + */ + public func kf_setBackgroundImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?) -> RetrieveImageTask + { + return kf_setBackgroundImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) + } /** Set the background image to use for a specified state with a URL and a placeholder image. @@ -208,6 +339,24 @@ public extension UIButton { { return kf_setBackgroundImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: nil, progressBlock: nil, completionHandler: nil) } + + /** + Set the background image to use for a specified state with a resource, a placeholder image and options. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + + :returns: A task represents the retriving process. + */ + public func kf_setBackgroundImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?) -> RetrieveImageTask + { + return kf_setBackgroundImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) + } /** Set the background image to use for a specified state with a URL, a placeholder image and options. @@ -226,6 +375,26 @@ public extension UIButton { { return kf_setBackgroundImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: nil) } + + /** + Set the background image to use for a specified state with a resource, a placeholder image, options and completion handler. + + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + public func kf_setBackgroundImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setBackgroundImageWithResource(resource, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: completionHandler) + } /** Set the background image to use for a specified state with a URL, a placeholder image, options and completion handler. @@ -246,12 +415,12 @@ public extension UIButton { { return kf_setBackgroundImageWithURL(URL, forState: state, placeholderImage: placeholderImage, optionsInfo: optionsInfo, progressBlock: nil, completionHandler: completionHandler) } - + /** - Set the background image to use for a specified state with a URL, + Set the background image to use for a specified state with a resource, a placeholder image, options progress handler and completion handler. - :param: URL The URL of image for specified state. + :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. :param: state The state that uses the specified image. :param: placeholderImage A placeholder image when retrieving the image at URL. :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. @@ -260,16 +429,16 @@ public extension UIButton { :returns: A task represents the retriving process. */ - public func kf_setBackgroundImageWithURL(URL: NSURL, - forState state: UIControlState, - placeholderImage: UIImage?, - optionsInfo: KingfisherOptionsInfo?, - progressBlock: DownloadProgressBlock?, - completionHandler: CompletionHandler?) -> RetrieveImageTask + public func kf_setBackgroundImageWithResource(resource: Resource, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask { setBackgroundImage(placeholderImage, forState: state) - kf_setBackgroundWebURL(URL, forState: state) - let task = KingfisherManager.sharedManager.retrieveImageWithURL(URL, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in + kf_setBackgroundWebURL(resource.downloadURL, forState: state) + let task = KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: optionsInfo, progressBlock: { (receivedSize, totalSize) -> () in if let progressBlock = progressBlock { dispatch_async(dispatch_get_main_queue(), { () -> Void in progressBlock(receivedSize: receivedSize, totalSize: totalSize) @@ -286,6 +455,34 @@ public extension UIButton { return task } + + /** + Set the background image to use for a specified state with a URL, + a placeholder image, options progress handler and completion handler. + + :param: URL The URL of image for specified state. + :param: state The state that uses the specified image. + :param: placeholderImage A placeholder image when retrieving the image at URL. + :param: optionsInfo A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. + :param: progressBlock Called when the image downloading progress gets updated. + :param: completionHandler Called when the image retrieved and set. + + :returns: A task represents the retriving process. + */ + public func kf_setBackgroundImageWithURL(URL: NSURL, + forState state: UIControlState, + placeholderImage: UIImage?, + optionsInfo: KingfisherOptionsInfo?, + progressBlock: DownloadProgressBlock?, + completionHandler: CompletionHandler?) -> RetrieveImageTask + { + return kf_setBackgroundImageWithResource(Resource(downloadURL: URL), + forState: state, + placeholderImage: placeholderImage, + optionsInfo: optionsInfo, + progressBlock: progressBlock, + completionHandler: completionHandler) + } } private var lastBackgroundURLKey: Void? From a564e1c50fcc9be6fce742f7961cb48acdfe91a2 Mon Sep 17 00:00:00 2001 From: onevcat Date: Fri, 14 Aug 2015 12:35:28 +0900 Subject: [PATCH 6/8] Update demo API to use resource --- Kingfisher-Demo/ViewController.swift | 4 +++- Kingfisher/Resource.swift | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Kingfisher-Demo/ViewController.swift b/Kingfisher-Demo/ViewController.swift index e591070b7..8c25bf7fd 100644 --- a/Kingfisher-Demo/ViewController.swift +++ b/Kingfisher-Demo/ViewController.swift @@ -58,7 +58,9 @@ extension ViewController: UICollectionViewDataSource { override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier("collectionViewCell", forIndexPath: indexPath) as! CollectionViewCell cell.cellImageView.kf_showIndicatorWhenLoading = true - cell.cellImageView.kf_setImageWithURL(NSURL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/kingfisher-\(indexPath.row + 1).jpg")!, placeholderImage: nil, optionsInfo: nil, progressBlock: { (receivedSize, totalSize) -> () in + + let URL = NSURL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/kingfisher-\(indexPath.row + 1).jpg")! + cell.cellImageView.kf_setImageWithResource(Resource(downloadURL: URL), placeholderImage: nil, optionsInfo: nil, progressBlock: { (receivedSize, totalSize) -> () in println("\(indexPath.row + 1): \(receivedSize)/\(totalSize)") }) { (image, error, cacheType, imageURL) -> () in println("\(indexPath.row + 1): Finished") diff --git a/Kingfisher/Resource.swift b/Kingfisher/Resource.swift index 7e53d6897..f39b8e09c 100644 --- a/Kingfisher/Resource.swift +++ b/Kingfisher/Resource.swift @@ -30,7 +30,7 @@ public struct Resource { public let cacheKey: String public let downloadURL: NSURL - init(downloadURL: NSURL, cacheKey: String? = nil) { + public init(downloadURL: NSURL, cacheKey: String? = nil) { self.downloadURL = downloadURL self.cacheKey = cacheKey ?? downloadURL.absoluteString! } From 264dbaed1843d9be31362efc7ef0f1e35c9f6df6 Mon Sep 17 00:00:00 2001 From: onevcat Date: Fri, 14 Aug 2015 12:44:30 +0900 Subject: [PATCH 7/8] Update documentation to make things clearer --- Kingfisher/KingfisherManager.swift | 7 +++++-- Kingfisher/UIButton+Kingfisher.swift | 12 ++++++++---- Kingfisher/UIImageView+Kingfisher.swift | 6 ++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Kingfisher/KingfisherManager.swift b/Kingfisher/KingfisherManager.swift index e51ee1755..9ca89d325 100644 --- a/Kingfisher/KingfisherManager.swift +++ b/Kingfisher/KingfisherManager.swift @@ -178,9 +178,12 @@ public class KingfisherManager { } /** - Get an image with URL as the key. + Get an image with `URL.absoluteString` as the key. If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first. - If not found, it will download the image at URL and cache it. + If not found, it will download the image at URL and cache it with `URL.absoluteString` value as its key. + + If you need to specify the key other than `URL.absoluteString`, please use resource version of this API with `resource.cacheKey` set to what you want. + These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more. :param: URL The image URL. diff --git a/Kingfisher/UIButton+Kingfisher.swift b/Kingfisher/UIButton+Kingfisher.swift index e58c4acd6..27ca5ec5b 100644 --- a/Kingfisher/UIButton+Kingfisher.swift +++ b/Kingfisher/UIButton+Kingfisher.swift @@ -34,7 +34,7 @@ public extension UIButton { /** Set an image to use for a specified state with a resource. It will ask for Kingfisher's manager to get the image for the `cacheKey` property in `resource` and then set it for a button state. - The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `cacheKey` for next use. + The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `resource.cacheKey` for next use. :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. :param: state The state that uses the specified image. @@ -50,7 +50,9 @@ public extension UIButton { /** Set an image to use for a specified state with a URL. It will ask for Kingfisher's manager to get the image for the URL and then set it for a button state. - The memory and disk will be searched first. If the manager does not find it, it will try to download the image at this URL and store it for next use. + The memory and disk will be searched first with `URL.absoluteString` as the cache key. If the manager does not find it, it will try to download the image at this URL and store the image with `URL.absoluteString` as cache key for next use. + + If you need to specify the key other than `URL.absoluteString`, please use resource version of these APIs with `resource.cacheKey` set to what you want. :param: URL The URL of image for specified state. :param: state The state that uses the specified image. @@ -279,7 +281,7 @@ public extension UIButton { /** Set the background image to use for a specified state with a resource. It will ask for Kingfisher's manager to get the image for the `cacheKey` property in `resource` and then set it for a button state. - The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `cacheKey` for next use. + The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `resource.cacheKey` for next use. :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. :param: state The state that uses the specified image. @@ -295,7 +297,9 @@ public extension UIButton { /** Set the background image to use for a specified state with a URL. It will ask for Kingfisher's manager to get the image for the URL and then set it for a button state. - The memory and disk will be searched first. If the manager does not find it, it will try to download the image at this URL and store it for next use. + The memory and disk will be searched first with `URL.absoluteString` as the cache key. If the manager does not find it, it will try to download the image at this URL and store the image with `URL.absoluteString` as cache key for next use. + + If you need to specify the key other than `URL.absoluteString`, please use resource version of these APIs with `resource.cacheKey` set to what you want. :param: URL The URL of image for specified state. :param: state The state that uses the specified image. diff --git a/Kingfisher/UIImageView+Kingfisher.swift b/Kingfisher/UIImageView+Kingfisher.swift index ada79aa67..19b8ac509 100644 --- a/Kingfisher/UIImageView+Kingfisher.swift +++ b/Kingfisher/UIImageView+Kingfisher.swift @@ -36,7 +36,7 @@ public extension UIImageView { /** Set an image with a resource. It will ask for Kingfisher's manager to get the image for the `cacheKey` property in `resource`. - The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `cacheKey` for next use. + The memory and disk will be searched first. If the manager does not find it, it will try to download the image at the `resource.downloadURL` and store it with `resource.cacheKey` for next use. :param: resource Resource object contains information such as `cacheKey` and `downloadURL`. @@ -50,7 +50,9 @@ public extension UIImageView { /** Set an image with a URL. It will ask for Kingfisher's manager to get the image for the URL. - The memory and disk will be searched first. If the manager does not find it, it will try to download the image at this URL and store it for next use. + The memory and disk will be searched first with `URL.absoluteString` as the cache key. If the manager does not find it, it will try to download the image at this URL and store the image with `URL.absoluteString` as cache key for next use. + + If you need to specify the key other than `URL.absoluteString`, please use resource version of these APIs with `resource.cacheKey` set to what you want. :param: URL The URL of image. From bc6f480197482e34a39594316e621ff885bd6d4a Mon Sep 17 00:00:00 2001 From: onevcat Date: Fri, 14 Aug 2015 13:51:07 +0900 Subject: [PATCH 8/8] Add test for resource apis --- .../UIImageViewExtensionTests.swift | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/KingfisherTests/UIImageViewExtensionTests.swift b/KingfisherTests/UIImageViewExtensionTests.swift index a75425415..970ac3bdb 100644 --- a/KingfisherTests/UIImageViewExtensionTests.swift +++ b/KingfisherTests/UIImageViewExtensionTests.swift @@ -85,6 +85,33 @@ class UIImageViewExtensionTests: XCTestCase { waitForExpectationsWithTimeout(5, handler: nil) } + func testImageDownloadWithResourceForImageView() { + let expectation = expectationWithDescription("wait for downloading image") + + let URLString = testKeys[0] + stubRequest("GET", URLString).andReturn(200).withBody(testImageData) + let URL = NSURL(string: URLString)! + let resource = Resource(downloadURL: URL) + + var progressBlockIsCalled = false + + imageView.kf_setImageWithResource(resource, placeholderImage: nil, optionsInfo: nil, progressBlock: { (receivedSize, totalSize) -> () in + progressBlockIsCalled = true + }) { (image, error, cacheType, imageURL) -> () in + expectation.fulfill() + + XCTAssert(progressBlockIsCalled, "progressBlock should be called at least once.") + XCTAssert(image != nil, "Downloaded image should exist.") + XCTAssert(image! == testImage, "Downloaded image should be the same as test image.") + XCTAssert(self.imageView.image! == testImage, "Downloaded image should be already set to the image property.") + XCTAssert(self.imageView.kf_webURL == imageURL, "Web URL should equal to the downloaded url.") + + XCTAssert(cacheType == .None, "The cache type should be none here. This image was just downloaded.") + } + + waitForExpectationsWithTimeout(5, handler: nil) + } + func testImageDownloadCancelForImageView() { let expectation = expectationWithDescription("wait for downloading image")