diff --git a/Cartfile.resolved b/Cartfile.resolved index e8557b44..b6f91d77 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "Alamofire/Alamofire" "5.0.0" +github "Alamofire/Alamofire" "5.0.1" diff --git a/Carthage/Checkouts/Alamofire b/Carthage/Checkouts/Alamofire index 0c8cb78d..cdb2f4a5 160000 --- a/Carthage/Checkouts/Alamofire +++ b/Carthage/Checkouts/Alamofire @@ -1 +1 @@ -Subproject commit 0c8cb78d05b6d067ee331c05058ff4dedcb45ffa +Subproject commit cdb2f4a574c3f7ae9ac0b0913058d64ff6fa09ac diff --git a/Source/ImageFilter.swift b/Source/ImageFilter.swift index 49a9b840..cc5073dc 100644 --- a/Source/ImageFilter.swift +++ b/Source/ImageFilter.swift @@ -171,7 +171,7 @@ public struct ScaledToSizeFilter: ImageFilter, Sizable { /// The filter closure used to create the modified representation of the given image. public var filter: (Image) -> Image { return { image in - return image.af_imageScaled(to: self.size) + return image.af.imageScaled(to: self.size) } } } @@ -195,7 +195,7 @@ public struct AspectScaledToFitSizeFilter: ImageFilter, Sizable { /// The filter closure used to create the modified representation of the given image. public var filter: (Image) -> Image { return { image in - return image.af_imageAspectScaled(toFit: self.size) + return image.af.imageAspectScaled(toFit: self.size) } } } @@ -220,7 +220,7 @@ public struct AspectScaledToFillSizeFilter: ImageFilter, Sizable { /// The filter closure used to create the modified representation of the given image. public var filter: (Image) -> Image { return { image in - return image.af_imageAspectScaled(toFill: self.size) + return image.af.imageAspectScaled(toFill: self.size) } } } @@ -253,7 +253,7 @@ public struct RoundedCornersFilter: ImageFilter, Roundable { /// The filter closure used to create the modified representation of the given image. public var filter: (Image) -> Image { return { image in - return image.af_imageRounded( + return image.af.imageRounded( withCornerRadius: self.radius, divideRadiusByImageScale: self.divideRadiusByImageScale ) @@ -279,7 +279,7 @@ public struct CircleFilter: ImageFilter { /// The filter closure used to create the modified representation of the given image. public var filter: (Image) -> Image { return { image in - return image.af_imageRoundedIntoCircle() + return image.af.imageRoundedIntoCircle() } } } @@ -303,7 +303,7 @@ public extension ImageFilter where Self: CoreImageFilter { /// The filter closure used to create the modified representation of the given image. var filter: (Image) -> Image { return { image in - return image.af_imageFiltered(withCoreImageFilter: self.filterName, parameters: self.parameters) ?? image + return image.af.imageFiltered(withCoreImageFilter: self.filterName, parameters: self.parameters) ?? image } } diff --git a/Source/Request+AlamofireImage.swift b/Source/Request+AlamofireImage.swift index b2f7980f..fa194bc7 100644 --- a/Source/Request+AlamofireImage.swift +++ b/Source/Request+AlamofireImage.swift @@ -102,11 +102,11 @@ public final class ImageResponseSerializer: ResponseSerializer { } #if os(iOS) || os(tvOS) || os(watchOS) - guard let image = UIImage.af_threadSafeImage(with: data, scale: imageScale) else { + guard let image = UIImage.af.threadSafeImage(with: data, scale: imageScale) else { throw AFIError.imageSerializationFailed } - if inflateResponseImage { image.af_inflate() } + if inflateResponseImage { image.af.inflate() } #elseif os(macOS) guard let bitmapImage = NSBitmapImageRep(data: data) else { throw AFIError.imageSerializationFailed diff --git a/Source/UIButton+AlamofireImage.swift b/Source/UIButton+AlamofireImage.swift index 02c987ff..98f080d4 100644 --- a/Source/UIButton+AlamofireImage.swift +++ b/Source/UIButton+AlamofireImage.swift @@ -31,77 +31,63 @@ import UIKit public typealias ControlState = UIControl.State -extension UIButton { - - // MARK: - Private - AssociatedKeys - - private struct AssociatedKey { - static var imageDownloader = "af_UIButton.ImageDownloader" - static var sharedImageDownloader = "af_UIButton.SharedImageDownloader" - static var imageReceipts = "af_UIButton.ImageReceipts" - static var backgroundImageReceipts = "af_UIButton.BackgroundImageReceipts" - } +extension UIButton: AlamofireExtended {} +extension AlamofireExtension where ExtendedType: UIButton { // MARK: - Properties /// The instance image downloader used to download all images. If this property is `nil`, the `UIButton` will - /// fallback on the `af_sharedImageDownloader` for all downloads. The most common use case for needing to use a + /// fallback on the `sharedImageDownloader` for all downloads. The most common use case for needing to use a /// custom instance image downloader is when images are behind different basic auth credentials. - public var af_imageDownloader: ImageDownloader? { + public var imageDownloader: ImageDownloader? { get { - return objc_getAssociatedObject(self, &AssociatedKey.imageDownloader) as? ImageDownloader + return objc_getAssociatedObject(type, &AssociatedKeys.imageDownloader) as? ImageDownloader } - set { - objc_setAssociatedObject(self, &AssociatedKey.imageDownloader, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + nonmutating set { + objc_setAssociatedObject(type, &AssociatedKeys.imageDownloader, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } /// The shared image downloader used to download all images. By default, this is the default `ImageDownloader` /// instance backed with an `AutoPurgingImageCache` which automatically evicts images from the cache when the memory /// capacity is reached or memory warning notifications occur. The shared image downloader is only used if the - /// `af_imageDownloader` is `nil`. - public class var af_sharedImageDownloader: ImageDownloader { + /// `imageDownloader` is `nil`. + public static var sharedImageDownloader: ImageDownloader { get { guard let - downloader = objc_getAssociatedObject(self, &AssociatedKey.sharedImageDownloader) as? ImageDownloader - else { - return ImageDownloader.default - } + downloader = objc_getAssociatedObject(UIButton.self, &AssociatedKeys.sharedImageDownloader) as? ImageDownloader + else { return ImageDownloader.default } return downloader } set { - objc_setAssociatedObject(self, &AssociatedKey.sharedImageDownloader, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(UIButton.self, &AssociatedKeys.sharedImageDownloader, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } private var imageRequestReceipts: [UInt: RequestReceipt] { get { guard let - receipts = objc_getAssociatedObject(self, &AssociatedKey.imageReceipts) as? [UInt: RequestReceipt] - else { - return [:] - } + receipts = objc_getAssociatedObject(type, &AssociatedKeys.imageReceipts) as? [UInt: RequestReceipt] + else { return [:] } return receipts } - set { - objc_setAssociatedObject(self, &AssociatedKey.imageReceipts, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + nonmutating set { + objc_setAssociatedObject(type, &AssociatedKeys.imageReceipts, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } private var backgroundImageRequestReceipts: [UInt: RequestReceipt] { get { guard let - receipts = objc_getAssociatedObject(self, &AssociatedKey.backgroundImageReceipts) as? [UInt: RequestReceipt] - else { - return [:] - } + receipts = objc_getAssociatedObject(type, &AssociatedKeys.backgroundImageReceipts) as? [UInt: RequestReceipt] + else { return [:] } return receipts } - set { - objc_setAssociatedObject(self, &AssociatedKey.backgroundImageReceipts, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + nonmutating set { + objc_setAssociatedObject(type, &AssociatedKeys.backgroundImageReceipts, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } @@ -130,7 +116,7 @@ extension UIButton { /// single response value containing either the image or the error that occurred. If /// the image was returned from the image cache, the response will be `nil`. Defaults /// to `nil`. - public func af_setImage( + public func setImage( for state: ControlState, url: URL, cacheKey: String? = nil, @@ -141,7 +127,7 @@ extension UIButton { progressQueue: DispatchQueue = DispatchQueue.main, completion: ((AFIDataResponse) -> Void)? = nil) { - af_setImage( + setImage( for: state, urlRequest: urlRequest(with: url), cacheKey: cacheKey, @@ -154,7 +140,7 @@ extension UIButton { ) } - /// Asynchronously downloads an image from the specified URL request and sets it once the request is finished. + /// Asynchronously downloads an image from the specified URL and sets it once the request is finished. /// /// If the image is cached locally, the image is set immediately. Otherwise the specified placeholder image will be /// set immediately, and then the remote image will be set once the image request is finished. @@ -177,7 +163,7 @@ extension UIButton { /// single response value containing either the image or the error that occurred. If /// the image was returned from the image cache, the response will be `nil`. Defaults /// to `nil`. - public func af_setImage( + public func setImage( for state: ControlState, urlRequest: URLRequestConvertible, cacheKey: String? = nil, @@ -203,9 +189,9 @@ extension UIButton { return } - af_cancelImageRequest(for: state) + cancelImageRequest(for: state) - let imageDownloader = af_imageDownloader ?? UIButton.af_sharedImageDownloader + let imageDownloader = self.imageDownloader ?? UIButton.af.sharedImageDownloader let imageCache = imageDownloader.imageCache // Use the image from the image cache if it exists @@ -228,7 +214,7 @@ extension UIButton { result: .success(image) ) - setImage(image, for: state) + type.setImage(image, for: state) completion?(response) return @@ -236,11 +222,14 @@ extension UIButton { } // Set the placeholder since we're going to have to download - if let placeholderImage = placeholderImage { setImage(placeholderImage, for: state) } + if let placeholderImage = placeholderImage { type.setImage(placeholderImage, for: state) } // Generate a unique download id to check whether the active request has changed while downloading let downloadID = UUID().uuidString + // Weakify the button to allow it to go out-of-memory while download is running if deallocated + weak var button = self.type + // Download the image, then set the image for the control state let requestReceipt = imageDownloader.download( urlRequest, @@ -250,9 +239,9 @@ extension UIButton { filter: filter, progress: progress, progressQueue: progressQueue, - completion: { [weak self] response in + completion: { response in guard - let strongSelf = self, + let strongSelf = button?.af, strongSelf.isImageURLRequest(response.request, equalToActiveRequestURLForState: state) && strongSelf.imageRequestReceipt(for: state)?.receiptID == downloadID else { @@ -261,7 +250,7 @@ extension UIButton { } if case .success(let image) = response.result { - strongSelf.setImage(image, for: state) + strongSelf.type.setImage(image, for: state) } strongSelf.setImageRequestReceipt(nil, for: state) @@ -274,10 +263,10 @@ extension UIButton { } /// Cancels the active download request for the image, if one exists. - public func af_cancelImageRequest(for state: ControlState) { + public func cancelImageRequest(for state: ControlState) { guard let receipt = imageRequestReceipt(for: state) else { return } - let imageDownloader = af_imageDownloader ?? UIButton.af_sharedImageDownloader + let imageDownloader = self.imageDownloader ?? UIButton.af.sharedImageDownloader imageDownloader.cancelRequest(with: receipt) setImageRequestReceipt(nil, for: state) @@ -308,7 +297,7 @@ extension UIButton { /// single response value containing either the image or the error that occurred. If /// the image was returned from the image cache, the response will be `nil`. Defaults /// to `nil`. - public func af_setBackgroundImage( + public func setBackgroundImage( for state: ControlState, url: URL, cacheKey: String? = nil, @@ -319,7 +308,7 @@ extension UIButton { progressQueue: DispatchQueue = DispatchQueue.main, completion: ((AFIDataResponse) -> Void)? = nil) { - af_setBackgroundImage( + setBackgroundImage( for: state, urlRequest: urlRequest(with: url), cacheKey: cacheKey, @@ -355,7 +344,7 @@ extension UIButton { /// single response value containing either the image or the error that occurred. If /// the image was returned from the image cache, the response will be `nil`. Defaults /// to `nil`. - public func af_setBackgroundImage( + public func setBackgroundImage( for state: ControlState, urlRequest: URLRequestConvertible, cacheKey: String? = nil, @@ -381,9 +370,9 @@ extension UIButton { return } - af_cancelBackgroundImageRequest(for: state) + cancelBackgroundImageRequest(for: state) - let imageDownloader = af_imageDownloader ?? UIButton.af_sharedImageDownloader + let imageDownloader = self.imageDownloader ?? UIButton.af.sharedImageDownloader let imageCache = imageDownloader.imageCache // Use the image from the image cache if it exists @@ -406,7 +395,7 @@ extension UIButton { result: .success(image) ) - setBackgroundImage(image, for: state) + type.setBackgroundImage(image, for: state) completion?(response) return @@ -414,11 +403,14 @@ extension UIButton { } // Set the placeholder since we're going to have to download - if let placeholderImage = placeholderImage { self.setBackgroundImage(placeholderImage, for: state) } + if let placeholderImage = placeholderImage { type.setBackgroundImage(placeholderImage, for: state) } // Generate a unique download id to check whether the active request has changed while downloading let downloadID = UUID().uuidString + // Weakify the button to allow it to go out-of-memory while download is running if deallocated + weak var button = self.type + // Download the image, then set the image for the control state let requestReceipt = imageDownloader.download( urlRequest, @@ -428,9 +420,9 @@ extension UIButton { filter: filter, progress: progress, progressQueue: progressQueue, - completion: { [weak self] response in + completion: { response in guard - let strongSelf = self, + let strongSelf = button?.af, strongSelf.isBackgroundImageURLRequest(response.request, equalToActiveRequestURLForState: state) && strongSelf.backgroundImageRequestReceipt(for: state)?.receiptID == downloadID else { @@ -439,7 +431,7 @@ extension UIButton { } if case .success(let image) = response.result { - strongSelf.setBackgroundImage(image, for: state) + strongSelf.type.setBackgroundImage(image, for: state) } strongSelf.setBackgroundImageRequestReceipt(nil, for: state) @@ -452,10 +444,10 @@ extension UIButton { } /// Cancels the active download request for the background image, if one exists. - public func af_cancelBackgroundImageRequest(for state: ControlState) { + public func cancelBackgroundImageRequest(for state: ControlState) { guard let receipt = backgroundImageRequestReceipt(for: state) else { return } - let imageDownloader = af_imageDownloader ?? UIButton.af_sharedImageDownloader + let imageDownloader = self.imageDownloader ?? UIButton.af.sharedImageDownloader imageDownloader.cancelRequest(with: receipt) setBackgroundImageRequestReceipt(nil, for: state) @@ -534,4 +526,139 @@ extension UIButton { } } +// MARK: - Deprecated + +extension UIButton { + @available(*, deprecated, message: "Replaced by `button.af.imageDownloader`") + public var af_imageDownloader: ImageDownloader? { + get { return af.imageDownloader } + set { af.imageDownloader = newValue } + } + + @available(*, deprecated, message: "Replaced by `button.af.sharedImageDownloader`") + public class var af_sharedImageDownloader: ImageDownloader { + get { return af.sharedImageDownloader } + set { af.sharedImageDownloader = newValue } + } + + @available(*, deprecated, message: "Replaced by `button.af.sharedImageDownloader`") + public func af_setImage( + for state: ControlState, + url: URL, + cacheKey: String? = nil, + placeholderImage: UIImage? = nil, + serializer: ImageResponseSerializer? = nil, + filter: ImageFilter? = nil, + progress: ImageDownloader.ProgressHandler? = nil, + progressQueue: DispatchQueue = DispatchQueue.main, + completion: ((AFIDataResponse) -> Void)? = nil) + { + af.setImage( + for: state, + url: url, + cacheKey: cacheKey, + placeholderImage: placeholderImage, + serializer: serializer, + filter: filter, + progress: progress, + progressQueue: progressQueue, + completion: completion + ) + } + + @available(*, deprecated, message: "Replaced by `button.af.sharedImageDownloader`") + public func af_setImage( + for state: ControlState, + urlRequest: URLRequestConvertible, + cacheKey: String? = nil, + placeholderImage: UIImage? = nil, + serializer: ImageResponseSerializer? = nil, + filter: ImageFilter? = nil, + progress: ImageDownloader.ProgressHandler? = nil, + progressQueue: DispatchQueue = DispatchQueue.main, + completion: ((AFIDataResponse) -> Void)? = nil) + { + af.setImage( + for: state, + urlRequest: urlRequest, + cacheKey: cacheKey, + placeholderImage: placeholderImage, + serializer: serializer, + filter: filter, + progress: progress, + progressQueue: progressQueue, + completion: completion + ) + } + + /// Cancels the active download request for the image, if one exists. + public func af_cancelImageRequest(for state: ControlState) { + af.cancelImageRequest(for: state) + } + + @available(*, deprecated, message: "Replaced by `button.af.sharedImageDownloader`") + public func af_setBackgroundImage( + for state: ControlState, + url: URL, + cacheKey: String? = nil, + placeholderImage: UIImage? = nil, + serializer: ImageResponseSerializer? = nil, + filter: ImageFilter? = nil, + progress: ImageDownloader.ProgressHandler? = nil, + progressQueue: DispatchQueue = DispatchQueue.main, + completion: ((AFIDataResponse) -> Void)? = nil) + { + af.setBackgroundImage( + for: state, + url: url, + cacheKey: cacheKey, + placeholderImage: placeholderImage, + serializer: serializer, + filter: filter, + progress: progress, + progressQueue: progressQueue, + completion: completion + ) + } + + @available(*, deprecated, message: "Replaced by `button.af.sharedImageDownloader`") + public func af_setBackgroundImage( + for state: ControlState, + urlRequest: URLRequestConvertible, + cacheKey: String? = nil, + placeholderImage: UIImage? = nil, + serializer: ImageResponseSerializer? = nil, + filter: ImageFilter? = nil, + progress: ImageDownloader.ProgressHandler? = nil, + progressQueue: DispatchQueue = DispatchQueue.main, + completion: ((AFIDataResponse) -> Void)? = nil) + { + af.setBackgroundImage( + for: state, + urlRequest: urlRequest, + cacheKey: cacheKey, + placeholderImage: placeholderImage, + serializer: serializer, + filter: filter, + progress: progress, + progressQueue: progressQueue, + completion: completion + ) + } + + /// Cancels the active download request for the background image, if one exists. + public func af_cancelBackgroundImageRequest(for state: ControlState) { + af.cancelBackgroundImageRequest(for: state) + } +} + +// MARK: - Private - AssociatedKeys + +private struct AssociatedKeys { + static var imageDownloader = "UIButton.af.imageDownloader" + static var sharedImageDownloader = "UIButton.af.sharedImageDownloader" + static var imageReceipts = "UIButton.af.imageReceipts" + static var backgroundImageReceipts = "UIButton.af.backgroundImageReceipts" +} + #endif diff --git a/Source/UIImage+AlamofireImage.swift b/Source/UIImage+AlamofireImage.swift index 6d1a5c8a..ff9abdc8 100644 --- a/Source/UIImage+AlamofireImage.swift +++ b/Source/UIImage+AlamofireImage.swift @@ -24,6 +24,7 @@ #if os(iOS) || os(tvOS) || os(watchOS) +import Alamofire import CoreGraphics import Foundation import UIKit @@ -32,7 +33,8 @@ import UIKit private let lock = NSLock() -extension UIImage { +extension UIImage: AlamofireExtended {} +extension AlamofireExtension where ExtendedType: UIImage { /// Initializes and returns the image object with the specified data in a thread-safe manner. /// /// It has been reported that there are thread-safety issues when initializing large amounts of images @@ -42,7 +44,7 @@ extension UIImage { /// - parameter data: The data object containing the image data. /// /// - returns: An initialized `UIImage` object, or `nil` if the method failed. - public static func af_threadSafeImage(with data: Data) -> UIImage? { + public static func threadSafeImage(with data: Data) -> UIImage? { lock.lock() let image = UIImage(data: data) lock.unlock() @@ -62,7 +64,7 @@ extension UIImage { /// different scale factor changes the size of the image as reported by the size property. /// /// - returns: An initialized `UIImage` object, or `nil` if the method failed. - public static func af_threadSafeImage(with data: Data, scale: CGFloat) -> UIImage? { + public static func threadSafeImage(with data: Data, scale: CGFloat) -> UIImage? { lock.lock() let image = UIImage(data: data, scale: scale) lock.unlock() @@ -71,24 +73,32 @@ extension UIImage { } } -// MARK: - Inflation - extension UIImage { - private struct AssociatedKey { - static var inflated = "af_UIImage.Inflated" + @available(*, deprecated, message: "Replaced by `UIImage.af.threadSafeImage(with:)`") + public static func af_threadSafeImage(with data: Data) -> UIImage? { + return af.threadSafeImage(with: data) + } + + @available(*, deprecated, message: "Replaced by `UIImage.af.threadSafeImage(with:scale:)`") + public static func af_threadSafeImage(with data: Data, scale: CGFloat) -> UIImage? { + return af.threadSafeImage(with: data, scale: scale) } +} + +// MARK: - Inflation +extension AlamofireExtension where ExtendedType: UIImage { /// Returns whether the image is inflated. - public var af_inflated: Bool { + public var isInflated: Bool { get { - if let inflated = objc_getAssociatedObject(self, &AssociatedKey.inflated) as? Bool { - return inflated + if let isInflated = objc_getAssociatedObject(type, &AssociatedKeys.isInflated) as? Bool { + return isInflated } else { return false } } - set { - objc_setAssociatedObject(self, &AssociatedKey.inflated, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + nonmutating set { + objc_setAssociatedObject(type, &AssociatedKeys.isInflated, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } @@ -96,20 +106,32 @@ extension UIImage { /// /// Inflating compressed image formats (such as PNG or JPEG) can significantly improve drawing performance as it /// allows a bitmap representation to be constructed in the background rather than on the main thread. - public func af_inflate() { - guard !af_inflated else { return } + public func inflate() { + guard !isInflated else { return } + + isInflated = true + _ = type.cgImage?.dataProvider?.data + } +} + +extension UIImage { + @available(*, deprecated, message: "Replaced by `image.af.isInflated`") + public var af_inflated: Bool { + return af.isInflated + } - af_inflated = true - _ = cgImage?.dataProvider?.data + @available(*, deprecated, message: "Replaced by `image.af.inflate()`") + public func af_inflate() { + af.inflate() } } // MARK: - Alpha -extension UIImage { +extension AlamofireExtension where ExtendedType: UIImage { /// Returns whether the image contains an alpha component. - public var af_containsAlphaComponent: Bool { - let alphaInfo = cgImage?.alphaInfo + public var containsAlphaComponent: Bool { + let alphaInfo = type.cgImage?.alphaInfo return ( alphaInfo == .first || @@ -120,12 +142,20 @@ extension UIImage { } /// Returns whether the image is opaque. - public var af_isOpaque: Bool { return !af_containsAlphaComponent } + public var isOpaque: Bool { return !containsAlphaComponent } +} + +extension UIImage { + @available(*, deprecated, message: "Replaced by `image.af.containsAlphaComponent`") + public var af_containsAlphaComponent: Bool { return af.containsAlphaComponent } + + @available(*, deprecated, message: "Replaced by `image.af.isOpaque`") + public var af_isOpaque: Bool { return af.isOpaque } } // MARK: - Scaling -extension UIImage { +extension AlamofireExtension where ExtendedType: UIImage { /// Returns a new version of the image scaled to the specified size. /// /// - Parameters: @@ -133,13 +163,13 @@ extension UIImage { /// - scale: The scale to set for the new image. Defaults to `nil` which will maintain the current image scale. /// /// - Returns: The new image object. - public func af_imageScaled(to size: CGSize, scale: CGFloat? = nil) -> UIImage { + public func imageScaled(to size: CGSize, scale: CGFloat? = nil) -> UIImage { assert(size.width > 0 && size.height > 0, "You cannot safely scale an image to a zero width or height") - UIGraphicsBeginImageContextWithOptions(size, af_isOpaque, scale ?? self.scale) - draw(in: CGRect(origin: .zero, size: size)) + UIGraphicsBeginImageContextWithOptions(size, isOpaque, scale ?? type.scale) + type.draw(in: CGRect(origin: .zero, size: size)) - let scaledImage = UIGraphicsGetImageFromCurrentImageContext() ?? self + let scaledImage = UIGraphicsGetImageFromCurrentImageContext() ?? type UIGraphicsEndImageContext() return scaledImage @@ -150,7 +180,7 @@ extension UIImage { /// /// The resulting image contains an alpha component used to pad the width or height with the necessary transparent /// pixels to fit the specified size. In high performance critical situations, this may not be the optimal approach. - /// To maintain an opaque image, you could compute the `scaledSize` manually, then use the `af_imageScaledToSize` + /// To maintain an opaque image, you could compute the `scaledSize` manually, then use the `af.imageScaledToSize` /// method in conjunction with a `.Center` content mode to achieve the same visual result. /// /// - Parameters: @@ -158,27 +188,27 @@ extension UIImage { /// - scale: The scale to set for the new image. Defaults to `nil` which will maintain the current image scale. /// /// - Returns: A new image object. - public func af_imageAspectScaled(toFit size: CGSize, scale: CGFloat? = nil) -> UIImage { + public func imageAspectScaled(toFit size: CGSize, scale: CGFloat? = nil) -> UIImage { assert(size.width > 0 && size.height > 0, "You cannot safely scale an image to a zero width or height") - let imageAspectRatio = self.size.width / self.size.height + let imageAspectRatio = type.size.width / type.size.height let canvasAspectRatio = size.width / size.height var resizeFactor: CGFloat if imageAspectRatio > canvasAspectRatio { - resizeFactor = size.width / self.size.width + resizeFactor = size.width / type.size.width } else { - resizeFactor = size.height / self.size.height + resizeFactor = size.height / type.size.height } - let scaledSize = CGSize(width: self.size.width * resizeFactor, height: self.size.height * resizeFactor) + let scaledSize = CGSize(width: type.size.width * resizeFactor, height: type.size.height * resizeFactor) let origin = CGPoint(x: (size.width - scaledSize.width) / 2.0, y: (size.height - scaledSize.height) / 2.0) - UIGraphicsBeginImageContextWithOptions(size, false, scale ?? self.scale) - draw(in: CGRect(origin: origin, size: scaledSize)) + UIGraphicsBeginImageContextWithOptions(size, false, scale ?? type.scale) + type.draw(in: CGRect(origin: origin, size: scaledSize)) - let scaledImage = UIGraphicsGetImageFromCurrentImageContext() ?? self + let scaledImage = UIGraphicsGetImageFromCurrentImageContext() ?? type UIGraphicsEndImageContext() return scaledImage @@ -192,36 +222,53 @@ extension UIImage { /// - scale: The scale to set for the new image. Defaults to `nil` which will maintain the current image scale. /// /// - Returns: A new image object. - public func af_imageAspectScaled(toFill size: CGSize, scale: CGFloat? = nil) -> UIImage { + public func imageAspectScaled(toFill size: CGSize, scale: CGFloat? = nil) -> UIImage { assert(size.width > 0 && size.height > 0, "You cannot safely scale an image to a zero width or height") - let imageAspectRatio = self.size.width / self.size.height + let imageAspectRatio = type.size.width / type.size.height let canvasAspectRatio = size.width / size.height var resizeFactor: CGFloat if imageAspectRatio > canvasAspectRatio { - resizeFactor = size.height / self.size.height + resizeFactor = size.height / type.size.height } else { - resizeFactor = size.width / self.size.width + resizeFactor = size.width / type.size.width } - let scaledSize = CGSize(width: self.size.width * resizeFactor, height: self.size.height * resizeFactor) + let scaledSize = CGSize(width: type.size.width * resizeFactor, height: type.size.height * resizeFactor) let origin = CGPoint(x: (size.width - scaledSize.width) / 2.0, y: (size.height - scaledSize.height) / 2.0) - UIGraphicsBeginImageContextWithOptions(size, af_isOpaque, scale ?? self.scale) - draw(in: CGRect(origin: origin, size: scaledSize)) + UIGraphicsBeginImageContextWithOptions(size, isOpaque, scale ?? type.scale) + type.draw(in: CGRect(origin: origin, size: scaledSize)) - let scaledImage = UIGraphicsGetImageFromCurrentImageContext() ?? self + let scaledImage = UIGraphicsGetImageFromCurrentImageContext() ?? type UIGraphicsEndImageContext() return scaledImage } } +extension UIImage { + @available(*, deprecated, message: "Replaced by `image.af.imageScale(to:scale:)`") + public func af_imageScaled(to size: CGSize, scale: CGFloat? = nil) -> UIImage { + return af.imageScaled(to: size, scale: scale) + } + + @available(*, deprecated, message: "Replaced by `image.af.imageAspectScale(toFit:scale:)`") + public func af_imageAspectScaled(toFit size: CGSize, scale: CGFloat? = nil) -> UIImage { + return af.imageAspectScaled(toFit: size, scale: scale) + } + + @available(*, deprecated, message: "Replaced by `image.af.imageAspectScale(toFill:scale:)`") + public func af_imageAspectScaled(toFill size: CGSize, scale: CGFloat? = nil) -> UIImage { + return af.imageAspectScaled(toFill: size, scale: scale) + } +} + // MARK: - Rounded Corners -extension UIImage { +extension AlamofireExtension where ExtendedType: UIImage { /// Returns a new version of the image with the corners rounded to the specified radius. /// /// - Parameters: @@ -232,7 +279,10 @@ extension UIImage { /// with varying resolutions for each screen scale. `false` by default. /// /// - Returns: A new image object. - public func af_imageRounded(withCornerRadius radius: CGFloat, divideRadiusByImageScale: Bool = false) -> UIImage { + public func imageRounded(withCornerRadius radius: CGFloat, divideRadiusByImageScale: Bool = false) -> UIImage { + let size = type.size + let scale = type.scale + UIGraphicsBeginImageContextWithOptions(size, false, scale) let scaledRadius = divideRadiusByImageScale ? radius / scale : radius @@ -240,7 +290,7 @@ extension UIImage { let clippingPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: size), cornerRadius: scaledRadius) clippingPath.addClip() - draw(in: CGRect(origin: CGPoint.zero, size: size)) + type.draw(in: CGRect(origin: CGPoint.zero, size: size)) let roundedImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() @@ -251,17 +301,18 @@ extension UIImage { /// Returns a new version of the image rounded into a circle. /// /// - Returns: A new image object. - public func af_imageRoundedIntoCircle() -> UIImage { + public func imageRoundedIntoCircle() -> UIImage { + let size = type.size let radius = min(size.width, size.height) / 2.0 - var squareImage = self + var squareImage: UIImage = type if size.width != size.height { let squareDimension = min(size.width, size.height) let squareSize = CGSize(width: squareDimension, height: squareDimension) - squareImage = af_imageAspectScaled(toFill: squareSize) + squareImage = imageAspectScaled(toFill: squareSize) } - UIGraphicsBeginImageContextWithOptions(squareImage.size, false, scale) + UIGraphicsBeginImageContextWithOptions(squareImage.size, false, type.scale) let clippingPath = UIBezierPath( roundedRect: CGRect(origin: CGPoint.zero, size: squareImage.size), @@ -279,6 +330,18 @@ extension UIImage { } } +extension UIImage { + @available(*, deprecated, message: "Replaced by `image.af.imageRounded(withCornerRadius:divideRadiusByImageScale:)`") + public func af_imageRounded(withCornerRadius radius: CGFloat, divideRadiusByImageScale: Bool = false) -> UIImage { + return af.imageRounded(withCornerRadius: radius, divideRadiusByImageScale: divideRadiusByImageScale) + } + + @available(*, deprecated, message: "Replaced by `image.af.imageRoundedIntoCircle()`") + public func af_imageRoundedIntoCircle() -> UIImage { + return af.imageRoundedIntoCircle() + } +} + #endif #if os(iOS) || os(tvOS) @@ -287,8 +350,7 @@ import CoreImage // MARK: - Core Image Filters -@available(iOS 9.0, *) -extension UIImage { +extension AlamofireExtension where ExtendedType: UIImage { /// Returns a new version of the image using a CoreImage filter with the specified name and parameters. /// /// - Parameters: @@ -296,10 +358,10 @@ extension UIImage { /// - parameters: The parameters to apply to the CoreImage filter. /// /// - Returns: A new image object, or `nil` if the filter failed for any reason. - public func af_imageFiltered(withCoreImageFilter name: String, parameters: [String: Any]? = nil) -> UIImage? { - var image: CoreImage.CIImage? = ciImage + public func imageFiltered(withCoreImageFilter name: String, parameters: [String: Any]? = nil) -> UIImage? { + var image: CoreImage.CIImage? = type.ciImage - if image == nil, let CGImage = self.cgImage { + if image == nil, let CGImage = type.cgImage { image = CoreImage.CIImage(cgImage: CGImage) } @@ -315,8 +377,21 @@ extension UIImage { let cgImageRef = context.createCGImage(outputImage, from: outputImage.extent) - return UIImage(cgImage: cgImageRef!, scale: scale, orientation: imageOrientation) + return UIImage(cgImage: cgImageRef!, scale: type.scale, orientation: type.imageOrientation) + } +} + +extension UIImage { + @available(*, deprecated, message: "Replaced by `image.af.imageFiltered(withCoreImageFilter:parameters:)`") + public func af_imageFiltered(withCoreImageFilter name: String, parameters: [String: Any]? = nil) -> UIImage? { + af.imageFiltered(withCoreImageFilter: name, parameters: parameters) } } #endif + +// MARK: - + +private struct AssociatedKeys { + static var isInflated = "UIImage.af.isInflated" +} diff --git a/Source/UIImageView+AlamofireImage.swift b/Source/UIImageView+AlamofireImage.swift index 89c4d3b8..a041e367 100644 --- a/Source/UIImageView+AlamofireImage.swift +++ b/Source/UIImageView+AlamofireImage.swift @@ -32,9 +32,6 @@ import UIKit public typealias AnimationOptions = UIView.AnimationOptions extension UIImageView { - - // MARK: - ImageTransition - /// Used to wrap all `UIView` animation transition options alongside a duration. public enum ImageTransition { case noTransition @@ -120,52 +117,50 @@ extension UIImageView { } } } +} - // MARK: - Private - AssociatedKeys +// MARK: - - private struct AssociatedKey { - static var imageDownloader = "af_UIImageView.ImageDownloader" - static var sharedImageDownloader = "af_UIImageView.SharedImageDownloader" - static var activeRequestReceipt = "af_UIImageView.ActiveRequestReceipt" - } +extension UIImageView: AlamofireExtended {} +extension AlamofireExtension where ExtendedType: UIImageView { - // MARK: - Associated Properties + // MARK: - Properties /// The instance image downloader used to download all images. If this property is `nil`, the `UIImageView` will - /// fallback on the `af_sharedImageDownloader` for all downloads. The most common use case for needing to use a - /// custom instance image downloader is when images are behind different basic auth credentials. - public var af_imageDownloader: ImageDownloader? { + /// fallback on the `sharedImageDownloader` for all downloads. The most common use case for needing to use a custom + /// instance image downloader is when images are behind different basic auth credentials. + public var imageDownloader: ImageDownloader? { get { - return objc_getAssociatedObject(self, &AssociatedKey.imageDownloader) as? ImageDownloader + return objc_getAssociatedObject(type, &AssociatedKeys.imageDownloader) as? ImageDownloader } - set(downloader) { - objc_setAssociatedObject(self, &AssociatedKey.imageDownloader, downloader, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + nonmutating set(downloader) { + objc_setAssociatedObject(type, &AssociatedKeys.imageDownloader, downloader, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } /// The shared image downloader used to download all images. By default, this is the default `ImageDownloader` /// instance backed with an `AutoPurgingImageCache` which automatically evicts images from the cache when the memory /// capacity is reached or memory warning notifications occur. The shared image downloader is only used if the - /// `af_imageDownloader` is `nil`. - public class var af_sharedImageDownloader: ImageDownloader { + /// `imageDownloader` is `nil`. + public static var sharedImageDownloader: ImageDownloader { get { - if let downloader = objc_getAssociatedObject(self, &AssociatedKey.sharedImageDownloader) as? ImageDownloader { + if let downloader = objc_getAssociatedObject(UIImageView.self, &AssociatedKeys.sharedImageDownloader) as? ImageDownloader { return downloader } else { return ImageDownloader.default } } set { - objc_setAssociatedObject(self, &AssociatedKey.sharedImageDownloader, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + objc_setAssociatedObject(UIImageView.self, &AssociatedKeys.sharedImageDownloader, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } - var af_activeRequestReceipt: RequestReceipt? { + var activeRequestReceipt: RequestReceipt? { get { - return objc_getAssociatedObject(self, &AssociatedKey.activeRequestReceipt) as? RequestReceipt + return objc_getAssociatedObject(type, &AssociatedKeys.activeRequestReceipt) as? RequestReceipt } - set { - objc_setAssociatedObject(self, &AssociatedKey.activeRequestReceipt, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + nonmutating set { + objc_setAssociatedObject(type, &AssociatedKeys.activeRequestReceipt, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } @@ -207,7 +202,7 @@ extension UIImageView { /// the response from the server and the result containing either the /// image or the error that occurred. If the image was returned from the /// image cache, the response will be `nil`. Defaults to `nil`. - public func af_setImage( + public func setImage( withURL url: URL, cacheKey: String? = nil, placeholderImage: UIImage? = nil, @@ -215,11 +210,11 @@ extension UIImageView { filter: ImageFilter? = nil, progress: ImageDownloader.ProgressHandler? = nil, progressQueue: DispatchQueue = DispatchQueue.main, - imageTransition: ImageTransition = .noTransition, + imageTransition: UIImageView.ImageTransition = .noTransition, runImageTransitionIfCached: Bool = false, completion: ((AFIDataResponse) -> Void)? = nil) { - af_setImage( + setImage( withURLRequest: urlRequest(with: url), cacheKey: cacheKey, placeholderImage: placeholderImage, @@ -269,7 +264,7 @@ extension UIImageView { /// the response from the server and the result containing either the /// image or the error that occurred. If the image was returned from the /// image cache, the response will be `nil`. Defaults to `nil`. - public func af_setImage( + public func setImage( withURLRequest urlRequest: URLRequestConvertible, cacheKey: String? = nil, placeholderImage: UIImage? = nil, @@ -277,7 +272,7 @@ extension UIImageView { filter: ImageFilter? = nil, progress: ImageDownloader.ProgressHandler? = nil, progressQueue: DispatchQueue = DispatchQueue.main, - imageTransition: ImageTransition = .noTransition, + imageTransition: UIImageView.ImageTransition = .noTransition, runImageTransitionIfCached: Bool = false, completion: ((AFIDataResponse) -> Void)? = nil) { @@ -296,9 +291,9 @@ extension UIImageView { return } - af_cancelImageRequest() + cancelImageRequest() - let imageDownloader = af_imageDownloader ?? UIImageView.af_sharedImageDownloader + let imageDownloader = self.imageDownloader ?? UIImageView.af.sharedImageDownloader let imageCache = imageDownloader.imageCache // Use the image from the image cache if it exists @@ -325,18 +320,18 @@ extension UIImageView { // It's important to display the placeholder image again otherwise you have some odd disparity // between the request loading from the cache and those that download. It's important to keep // the same behavior between both, otherwise the user can actually see the difference. - if let placeholderImage = placeholderImage { self.image = placeholderImage } + if let placeholderImage = placeholderImage { type.image = placeholderImage } // Need to let the runloop cycle for the placeholder image to take affect DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) { // Added this additional check to ensure another request didn't get in during the delay - guard self.af_activeRequestReceipt == nil else { return } + guard self.activeRequestReceipt == nil else { return } self.run(imageTransition, with: image) completion?(response) } } else { - self.image = image + self.type.image = image completion?(response) } @@ -345,11 +340,14 @@ extension UIImageView { } // Set the placeholder since we're going to have to download - if let placeholderImage = placeholderImage { self.image = placeholderImage } + if let placeholderImage = placeholderImage { type.image = placeholderImage } // Generate a unique download id to check whether the active request has changed while downloading let downloadID = UUID().uuidString + // Weakify the image view to allow it to go out-of-memory while download is running if deallocated + weak var imageView = self.type + // Download the image, then run the image transition or completion handler let requestReceipt = imageDownloader.download( urlRequest, @@ -359,11 +357,11 @@ extension UIImageView { filter: filter, progress: progress, progressQueue: progressQueue, - completion: { [weak self] response in + completion: { response in guard - let strongSelf = self, + let strongSelf = imageView?.af, strongSelf.isURLRequestURLEqualToActiveRequestURL(response.request) && - strongSelf.af_activeRequestReceipt?.receiptID == downloadID + strongSelf.activeRequestReceipt?.receiptID == downloadID else { completion?(response) return @@ -373,25 +371,25 @@ extension UIImageView { strongSelf.run(imageTransition, with: image) } - strongSelf.af_activeRequestReceipt = nil + strongSelf.activeRequestReceipt = nil completion?(response) } ) - af_activeRequestReceipt = requestReceipt + activeRequestReceipt = requestReceipt } // MARK: - Image Download Cancellation /// Cancels the active download request, if one exists. - public func af_cancelImageRequest() { - guard let activeRequestReceipt = af_activeRequestReceipt else { return } + public func cancelImageRequest() { + guard let activeRequestReceipt = activeRequestReceipt else { return } - let imageDownloader = af_imageDownloader ?? UIImageView.af_sharedImageDownloader + let imageDownloader = self.imageDownloader ?? UIImageView.af.sharedImageDownloader imageDownloader.cancelRequest(with: activeRequestReceipt) - af_activeRequestReceipt = nil + self.activeRequestReceipt = nil } // MARK: - Image Transition @@ -400,12 +398,14 @@ extension UIImageView { /// /// - parameter imageTransition: The image transition to ran on the image view. /// - parameter image: The image to use for the image transition. - public func run(_ imageTransition: ImageTransition, with image: Image) { + public func run(_ imageTransition: UIImageView.ImageTransition, with image: Image) { + let imageView = type + UIView.transition( - with: self, + with: type, duration: imageTransition.duration, options: imageTransition.animationOptions, - animations: { imageTransition.animations(self, image) }, + animations: { imageTransition.animations(imageView, image) }, completion: imageTransition.completion ) } @@ -424,7 +424,7 @@ extension UIImageView { private func isURLRequestURLEqualToActiveRequestURL(_ urlRequest: URLRequestConvertible?) -> Bool { if - let currentRequestURL = af_activeRequestReceipt?.request.task?.originalRequest?.url, + let currentRequestURL = activeRequestReceipt?.request.task?.originalRequest?.url, let requestURL = urlRequest?.urlRequest?.url, currentRequestURL == requestURL { @@ -435,4 +435,92 @@ extension UIImageView { } } +// MARK: - Deprecated + +extension UIImageView { + @available(*, deprecated, message: "Replaced by `imageView.af.imageDownloader`") + public var af_imageDownloader: ImageDownloader? { + get { return af.imageDownloader } + set { af.imageDownloader = newValue } + } + + @available(*, deprecated, message: "Replaced by `imageView.af.sharedImageDownloader`") + public class var af_sharedImageDownloader: ImageDownloader { + get { return af.sharedImageDownloader } + set { af.sharedImageDownloader = newValue } + } + + @available(*, deprecated, message: "Replaced by `imageView.af.setImage(withURL: ...)`") + public func af_setImage( + withURL url: URL, + cacheKey: String? = nil, + placeholderImage: UIImage? = nil, + serializer: ImageResponseSerializer? = nil, + filter: ImageFilter? = nil, + progress: ImageDownloader.ProgressHandler? = nil, + progressQueue: DispatchQueue = DispatchQueue.main, + imageTransition: ImageTransition = .noTransition, + runImageTransitionIfCached: Bool = false, + completion: ((AFIDataResponse) -> Void)? = nil) + { + af.setImage( + withURL: url, + cacheKey: cacheKey, + placeholderImage: placeholderImage, + serializer: serializer, + filter: filter, + progress: progress, + progressQueue: progressQueue, + imageTransition: imageTransition, + runImageTransitionIfCached: runImageTransitionIfCached, + completion: completion + ) + } + + @available(*, deprecated, message: "Replaced by `imageView.af.setImage(withURLRequest: ...)`") + public func af_setImage( + withURLRequest urlRequest: URLRequestConvertible, + cacheKey: String? = nil, + placeholderImage: UIImage? = nil, + serializer: ImageResponseSerializer? = nil, + filter: ImageFilter? = nil, + progress: ImageDownloader.ProgressHandler? = nil, + progressQueue: DispatchQueue = DispatchQueue.main, + imageTransition: ImageTransition = .noTransition, + runImageTransitionIfCached: Bool = false, + completion: ((AFIDataResponse) -> Void)? = nil) + { + af.setImage( + withURLRequest: urlRequest, + cacheKey: cacheKey, + placeholderImage: placeholderImage, + serializer: serializer, + filter: filter, + progress: progress, + progressQueue: progressQueue, + imageTransition: imageTransition, + runImageTransitionIfCached: runImageTransitionIfCached, + completion: completion + ) + } + + @available(*, deprecated, message: "Replaced by `imageView.af.cancelImageRequest()`") + public func af_cancelImageRequest() { + af.cancelImageRequest() + } + + @available(*, deprecated, message: "Replaced by `imageView.af.run(_:with:)`") + public func run(_ imageTransition: ImageTransition, with image: Image) { + af.run(imageTransition, with: image) + } +} + +// MARK: - + +private struct AssociatedKeys { + static var imageDownloader = "UIImageView.af.imageDownloader" + static var sharedImageDownloader = "UIImageView.af.sharedImageDownloader" + static var activeRequestReceipt = "UIImageView.af.activeRequestReceipt" +} + #endif diff --git a/Tests/BaseTestCase.swift b/Tests/BaseTestCase.swift index 5888ea0c..baed725f 100644 --- a/Tests/BaseTestCase.swift +++ b/Tests/BaseTestCase.swift @@ -67,7 +67,7 @@ class BaseTestCase : XCTestCase { let data = try! Data(contentsOf: resourceURL) #if os(iOS) || os(tvOS) - let image = Image.af_threadSafeImage(with: data, scale: UIScreen.main.scale)! + let image = Image.af.threadSafeImage(with: data, scale: UIScreen.main.scale)! #elseif os(macOS) let image = Image(data: data)! #endif diff --git a/Tests/ImageDownloaderTests.swift b/Tests/ImageDownloaderTests.swift index 19cc0791..e1e91747 100644 --- a/Tests/ImageDownloaderTests.swift +++ b/Tests/ImageDownloaderTests.swift @@ -50,7 +50,7 @@ private class TestCircleFilter: ImageFilter { var filter: (Image) -> Image { return { image in self.filterOperationCompleted = true - return image.af_imageRoundedIntoCircle() + return image.af.imageRoundedIntoCircle() } } } diff --git a/Tests/ImageFilterTests.swift b/Tests/ImageFilterTests.swift index 4198e78b..1c365893 100644 --- a/Tests/ImageFilterTests.swift +++ b/Tests/ImageFilterTests.swift @@ -101,7 +101,7 @@ class ImageFilterTestCase: BaseTestCase { // Given let image = self.image(forResource: "pirate", withExtension: "jpg") let filter = DynamicImageFilter("DynamicScaleToSizeFilter") { image in - return image.af_imageScaled(to: CGSize(width: 50.0, height: 50.0)) + return image.af.imageScaled(to: CGSize(width: 50.0, height: 50.0)) } // When @@ -109,7 +109,7 @@ class ImageFilterTestCase: BaseTestCase { // Then let expectedFilteredImage = self.image(forResource: "pirate-scaled-50x50-@\(scale)x", withExtension: "png") - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") } // MARK: - DynamicCompositeImageFilter Tests @@ -132,7 +132,7 @@ class ImageFilterTestCase: BaseTestCase { withExtension: "png" ) - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") } // MARK: - Single Pass Image Filter Tests @@ -147,7 +147,7 @@ class ImageFilterTestCase: BaseTestCase { // Then let expectedFilteredImage = self.image(forResource: "pirate-scaled-50x50-@\(scale)x", withExtension: "png") - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") } func testThatAspectScaledToFitSizeFilterReturnsCorrectFilteredImage() { @@ -160,7 +160,7 @@ class ImageFilterTestCase: BaseTestCase { // Then let expectedFilteredImage = self.image(forResource: "pirate-aspect.scaled.to.fit-50x50-@\(scale)x", withExtension: "png") - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") } func testThatAspectScaledToFillSizeFilterReturnsCorrectFilteredImage() { @@ -173,7 +173,7 @@ class ImageFilterTestCase: BaseTestCase { // Then let expectedFilteredImage = self.image(forResource: "pirate-aspect.scaled.to.fill-50x50-@\(scale)x", withExtension: "png") - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") } // TODO: Needs updates for latest rendering results. @@ -187,7 +187,7 @@ class ImageFilterTestCase: BaseTestCase { // Then let expectedFilteredImage = self.image(forResource: "pirate-radius-20", withExtension: "png") - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") let expectedIdentifier = "RoundedCornersFilter-radius:(20)-divided:(true)" XCTAssertEqual(filter.identifier, expectedIdentifier, "filter identifier does not match") @@ -203,7 +203,7 @@ class ImageFilterTestCase: BaseTestCase { // Then let expectedFilteredImage = self.image(forResource: "pirate-circle", withExtension: "png") - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") } func testThatBlurFilterReturnsCorrectFilteredImage() { @@ -218,7 +218,7 @@ class ImageFilterTestCase: BaseTestCase { // Then let expectedFilteredImage = self.image(forResource: "unicorn-blurred-8", withExtension: "png") - let pixelsMatch = filteredImage.af_isEqualToImage(expectedFilteredImage) + let pixelsMatch = filteredImage.af.isEqualToImage(expectedFilteredImage) XCTAssertTrue(pixelsMatch, "pixels match should be true") } @@ -240,7 +240,7 @@ class ImageFilterTestCase: BaseTestCase { withExtension: "png" ) - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") } // TODO: Needs updates for latest rendering results. @@ -258,7 +258,7 @@ class ImageFilterTestCase: BaseTestCase { withExtension: "png" ) - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") } func testThatScaledToSizeCircleFilterReturnsCorrectFilteredImage() { @@ -275,7 +275,7 @@ class ImageFilterTestCase: BaseTestCase { withExtension: "png" ) - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") } func testThatAspectScaledToFillSizeCircleFilterReturnsCorrectFilteredImage() { @@ -292,7 +292,7 @@ class ImageFilterTestCase: BaseTestCase { withExtension: "png" ) - XCTAssertTrue(filteredImage.af_isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") + XCTAssertTrue(filteredImage.af.isEqualToImage(expectedFilteredImage), "filtered image pixels do not match") } } diff --git a/Tests/UIButtonTests.swift b/Tests/UIButtonTests.swift index 11309fbe..27ac5c9d 100644 --- a/Tests/UIButtonTests.swift +++ b/Tests/UIButtonTests.swift @@ -64,7 +64,7 @@ class UIButtonTests: BaseTestCase { ImageDownloader.defaultURLCache().removeAllCachedResponses() ImageDownloader.default.imageCache?.removeAllImages() - UIButton.af_sharedImageDownloader = ImageDownloader.default + UIButton.af.sharedImageDownloader = ImageDownloader.default } // MARK: - Image Download @@ -80,7 +80,7 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setImage(for: [], url: url) + button.af.setImage(for: [], url: url) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -98,7 +98,7 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setBackgroundImage(for: [], url: url) + button.af.setBackgroundImage(for: [], url: url) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -112,9 +112,9 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setImage(for: [], url: url) - button.af_cancelImageRequest(for: []) - button.af_setImage( + button.af.setImage(for: [], url: url) + button.af.cancelImageRequest(for: []) + button.af.setImage( for: [], urlRequest: URLRequest(url: url), placeholderImage: nil, @@ -136,9 +136,9 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setBackgroundImage(for: [], url: url) - button.af_cancelBackgroundImageRequest(for: []) - button.af_setBackgroundImage( + button.af.setBackgroundImage(for: [], url: url) + button.af.cancelBackgroundImageRequest(for: []) + button.af.setBackgroundImage( for: [], urlRequest: URLRequest(url: url), placeholderImage: nil, @@ -165,12 +165,12 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setImage(for: [], url: url) + button.af.setImage(for: [], url: url) waitForExpectations(timeout: timeout, handler: nil) // Then XCTAssertTrue(imageDownloadComplete) - XCTAssertNil(button.backgroundImageRequestReceipt(for: [])) + XCTAssertNil(button.af.backgroundImageRequestReceipt(for: [])) } func testThatActiveBackgroundImageRequestReceiptIsNilAfterImageDownloadCompletes() { @@ -184,12 +184,12 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setBackgroundImage(for: [], url: url) + button.af.setBackgroundImage(for: [], url: url) waitForExpectations(timeout: timeout, handler: nil) // Then XCTAssertTrue(backgroundImageDownloadComplete) - XCTAssertNil(button.backgroundImageRequestReceipt(for: [])) + XCTAssertNil(button.af.backgroundImageRequestReceipt(for: [])) } func testThatMultipleImageRequestReceiptStatesCanBeDownloadedInParallel() { @@ -200,7 +200,7 @@ class UIButtonTests: BaseTestCase { // When let expectation1 = expectation(description: "background image should download successfully") var normalStateImageDownloadComplete = false - button.af_setImage(for: [], url: url) + button.af.setImage(for: [], url: url) button.imageObserver = { normalStateImageDownloadComplete = true expectation1.fulfill() @@ -212,7 +212,7 @@ class UIButtonTests: BaseTestCase { var selectedStateImageDownloadComplete = false url = URL(string: "https://httpbin.org/image/jpeg?random=\(arc4random())")! - button.af_setImage(for: [.selected], url: url) + button.af.setImage(for: [.selected], url: url) button.imageObserver = { selectedStateImageDownloadComplete = true expectation2.fulfill() @@ -224,7 +224,7 @@ class UIButtonTests: BaseTestCase { var highlightedStateImageDownloadComplete = false url = URL(string: "https://httpbin.org/image/jpeg?random=\(arc4random())")! - button.af_setImage(for: [.highlighted], url: url) + button.af.setImage(for: [.highlighted], url: url) button.imageObserver = { highlightedStateImageDownloadComplete = true expectation3.fulfill() @@ -236,7 +236,7 @@ class UIButtonTests: BaseTestCase { var disabledStateImageDownloadComplete = false url = URL(string: "https://httpbin.org/image/jpeg?random=\(arc4random())")! - button.af_setImage(for: [.disabled], url: url) + button.af.setImage(for: [.disabled], url: url) button.imageObserver = { disabledStateImageDownloadComplete = true expectation4.fulfill() @@ -266,7 +266,7 @@ class UIButtonTests: BaseTestCase { // When let expectation1 = expectation(description: "background image should download successfully") var normalStateBackgroundImageDownloadComplete = false - button.af_setBackgroundImage(for: [], url: url) + button.af.setBackgroundImage(for: [], url: url) button.imageObserver = { normalStateBackgroundImageDownloadComplete = true expectation1.fulfill() @@ -277,7 +277,7 @@ class UIButtonTests: BaseTestCase { var selectedStateBackgroundImageDownloadComplete = false url = URL(string: "https://httpbin.org/image/jpeg?random=\(arc4random())")! - button.af_setBackgroundImage(for: [.selected], url: url) + button.af.setBackgroundImage(for: [.selected], url: url) button.imageObserver = { selectedStateBackgroundImageDownloadComplete = true expectation2.fulfill() @@ -289,7 +289,7 @@ class UIButtonTests: BaseTestCase { var highlightedStateBackgroundImageDownloadComplete = false url = URL(string: "https://httpbin.org/image/jpeg?random=\(arc4random())")! - button.af_setBackgroundImage(for: [.highlighted], url: url) + button.af.setBackgroundImage(for: [.highlighted], url: url) button.imageObserver = { highlightedStateBackgroundImageDownloadComplete = true expectation3.fulfill() @@ -301,7 +301,7 @@ class UIButtonTests: BaseTestCase { var disabledStateBackgroundImageDownloadComplete = false url = URL(string: "https://httpbin.org/image/jpeg?random=\(arc4random())")! - button.af_setBackgroundImage(for: [.disabled], url: url) + button.af.setBackgroundImage(for: [.disabled], url: url) button.imageObserver = { disabledStateBackgroundImageDownloadComplete = true expectation4.fulfill() @@ -337,17 +337,17 @@ class UIButtonTests: BaseTestCase { let configuration = URLSessionConfiguration.ephemeral let imageDownloader = ImageDownloader(configuration: configuration) - button.af_imageDownloader = imageDownloader + button.af.imageDownloader = imageDownloader // When - button.af_setImage(for: [], url: url) + button.af.setImage(for: [], url: url) let activeRequestCount = imageDownloader.activeRequestCount waitForExpectations(timeout: timeout, handler: nil) // Then XCTAssertTrue(imageDownloadComplete) - XCTAssertNil(button.imageRequestReceipt(for: []), "active request receipt should be nil after download completes") + XCTAssertNil(button.af.imageRequestReceipt(for: []), "active request receipt should be nil after download completes") XCTAssertEqual(activeRequestCount, 1, "active request count should be 1") } @@ -364,7 +364,7 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setImage( + button.af.setImage( for: .normal, url: url, serializer: ImageResponseSerializer(imageScale: 4.0, inflateResponseImage: false) @@ -375,7 +375,7 @@ class UIButtonTests: BaseTestCase { // Then XCTAssertTrue(imageDownloadComplete, "image download complete should be true") XCTAssertEqual(button.image(for: .normal)?.scale, 4.0) - XCTAssertEqual(button.image(for: .normal)?.af_inflated, false) + XCTAssertEqual(button.image(for: .normal)?.af.isInflated, false) } func testThatCustomImageSerializerCanBeUsedForBackgroundImage() { @@ -389,7 +389,7 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setBackgroundImage( + button.af.setBackgroundImage( for: .normal, url: url, serializer: ImageResponseSerializer(imageScale: 4.0, inflateResponseImage: false) @@ -400,7 +400,7 @@ class UIButtonTests: BaseTestCase { // Then XCTAssertTrue(imageDownloadComplete, "image download complete should be true") XCTAssertEqual(button.backgroundImage(for: .normal)?.scale, 4.0) - XCTAssertEqual(button.backgroundImage(for: .normal)?.af_inflated, false) + XCTAssertEqual(button.backgroundImage(for: .normal)?.af.isInflated, false) } // MARK: - Image Cache @@ -420,8 +420,8 @@ class UIButtonTests: BaseTestCase { waitForExpectations(timeout: timeout, handler: nil) // When - button.af_setImage(for: [], url: url) - button.af_cancelImageRequest(for: []) + button.af.setImage(for: [], url: url) + button.af.cancelImageRequest(for: []) // Then XCTAssertNotNil(button.image(for: []), "button image should not be nil") @@ -432,9 +432,9 @@ class UIButtonTests: BaseTestCase { let imageDownloader = ImageDownloader() // When - let firstEqualityCheck = UIButton.af_sharedImageDownloader === imageDownloader - UIButton.af_sharedImageDownloader = imageDownloader - let secondEqualityCheck = UIButton.af_sharedImageDownloader === imageDownloader + let firstEqualityCheck = UIButton.af.sharedImageDownloader === imageDownloader + UIButton.af.sharedImageDownloader = imageDownloader + let secondEqualityCheck = UIButton.af.sharedImageDownloader === imageDownloader // Then XCTAssertFalse(firstEqualityCheck, "first equality check should be false") @@ -456,8 +456,8 @@ class UIButtonTests: BaseTestCase { waitForExpectations(timeout: timeout, handler: nil) // When - button.af_setImage(for: .normal, url: url, filter: CircleFilter()) - button.af_cancelImageRequest(for: .normal) + button.af.setImage(for: .normal, url: url, filter: CircleFilter()) + button.af.cancelImageRequest(for: .normal) // Then XCTAssertNotNil(button.image(for: .normal), "button image should not be nil") @@ -475,7 +475,7 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setImage(for: .normal, url: url, cacheKey: cacheKey) + button.af.setImage(for: .normal, url: url, cacheKey: cacheKey) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -494,7 +494,7 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setBackgroundImage(for: .normal, url: url, cacheKey: cacheKey) + button.af.setBackgroundImage(for: .normal, url: url, cacheKey: cacheKey) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -514,7 +514,7 @@ class UIButtonTests: BaseTestCase { let button = TestButton() // When - button.af_setImage(for: [], url: url, placeholderImage: placeholderImage) + button.af.setImage(for: [], url: url, placeholderImage: placeholderImage) let initialImageEqualsPlaceholderImage = button.image(for:[]) === placeholderImage button.imageObserver = { @@ -542,7 +542,7 @@ class UIButtonTests: BaseTestCase { let button = TestButton () // When - button.af_setBackgroundImage(for: [], url: url, placeholderImage: placeholderImage) + button.af.setBackgroundImage(for: [], url: url, placeholderImage: placeholderImage) let initialImageEqualsPlaceholderImage = button.backgroundImage(for:[]) === placeholderImage button.imageObserver = { @@ -575,7 +575,7 @@ class UIButtonTests: BaseTestCase { waitForExpectations(timeout: timeout, handler: nil) // When - button.af_setImage(for: [], url: url, placeholderImage: placeholderImage) + button.af.setImage(for: [], url: url, placeholderImage: placeholderImage) // Then XCTAssertNotNil(button.image(for: []), "button image should not be nil") @@ -598,7 +598,7 @@ class UIButtonTests: BaseTestCase { waitForExpectations(timeout: timeout, handler: nil) // When - button.af_setBackgroundImage(for: [], url: url, placeholderImage: placeholderImage) + button.af.setBackgroundImage(for: [], url: url, placeholderImage: placeholderImage) // Then XCTAssertNotNil(button.backgroundImage(for: []), "button background image should not be nil") @@ -611,7 +611,7 @@ class UIButtonTests: BaseTestCase { let button = TestButton () // When - button.af_setImage(for: [], url: url, placeholderImage: placeholderImage) + button.af.setImage(for: [], url: url, placeholderImage: placeholderImage) let initialImageEqualsPlaceholderImage = button.image(for:[]) === placeholderImage // Then @@ -624,7 +624,7 @@ class UIButtonTests: BaseTestCase { let button = TestButton () // When - button.af_setBackgroundImage(for: [], url: url, placeholderImage: placeholderImage) + button.af.setBackgroundImage(for: [], url: url, placeholderImage: placeholderImage) let initialImageEqualsPlaceholderImage = button.backgroundImage(for:[]) === placeholderImage // Then @@ -648,7 +648,7 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setImage(for: .normal, url: url, filter: filter) + button.af.setImage(for: .normal, url: url, filter: filter) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -674,7 +674,7 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setBackgroundImage(for: .normal, url: url, filter: filter) + button.af.setBackgroundImage(for: .normal, url: url, filter: filter) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -704,7 +704,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in + button.af.setImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in completionHandlerCalled = true result = response.result expectation.fulfill() @@ -734,7 +734,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setBackgroundImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in + button.af.setBackgroundImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in completionHandlerCalled = true result = response.result expectation.fulfill() @@ -759,7 +759,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in + button.af.setImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in completionHandlerCalled = true result = response.result expectation.fulfill() @@ -784,7 +784,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setBackgroundImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in + button.af.setBackgroundImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in completionHandlerCalled = true result = response.result expectation.fulfill() @@ -809,7 +809,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in + button.af.setImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in completionHandlerCalled = true result = response.result expectation.fulfill() @@ -834,7 +834,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setBackgroundImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in + button.af.setBackgroundImage(for: [], urlRequest: urlRequest, placeholderImage: nil) { response in completionHandlerCalled = true result = response.result expectation.fulfill() @@ -861,7 +861,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setImage( + button.af.setImage( for: [], urlRequest: urlRequest, placeholderImage: nil, @@ -872,7 +872,7 @@ class UIButtonTests: BaseTestCase { } ) - button.af_cancelImageRequest(for: []) + button.af.cancelImageRequest(for: []) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -892,7 +892,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setBackgroundImage( + button.af.setBackgroundImage( for: [], urlRequest: urlRequest, placeholderImage: nil, @@ -903,7 +903,7 @@ class UIButtonTests: BaseTestCase { } ) - button.af_cancelBackgroundImageRequest(for: []) + button.af.cancelBackgroundImageRequest(for: []) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -922,7 +922,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setImage( + button.af.setImage( for: [], urlRequest: URLRequest(url: url), placeholderImage: nil, @@ -931,7 +931,7 @@ class UIButtonTests: BaseTestCase { } ) - button.af_setImage( + button.af.setImage( for: [], urlRequest: URLRequest(url: URL(string: "https://httpbin.org/image/png")!), placeholderImage: nil, @@ -961,7 +961,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setBackgroundImage( + button.af.setBackgroundImage( for: [], urlRequest: URLRequest(url: url), placeholderImage: nil, @@ -970,7 +970,7 @@ class UIButtonTests: BaseTestCase { } ) - button.af_setBackgroundImage( + button.af.setBackgroundImage( for: [], urlRequest: URLRequest(url: URL(string: "https://httpbin.org/image/png")!), placeholderImage: nil, @@ -1000,7 +1000,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setImage( + button.af.setImage( for: [], urlRequest: URLRequest(url: url), placeholderImage: nil, @@ -1009,9 +1009,9 @@ class UIButtonTests: BaseTestCase { } ) - button.af_cancelImageRequest(for: []) + button.af.cancelImageRequest(for: []) - button.af_setImage( + button.af.setImage( for: [], urlRequest: URLRequest(url: url), placeholderImage: nil, @@ -1041,7 +1041,7 @@ class UIButtonTests: BaseTestCase { var result: AFIResult? // When - button.af_setBackgroundImage( + button.af.setBackgroundImage( for: [], urlRequest: URLRequest(url: url), placeholderImage: nil, @@ -1050,9 +1050,9 @@ class UIButtonTests: BaseTestCase { } ) - button.af_cancelBackgroundImageRequest(for: []) + button.af.cancelBackgroundImageRequest(for: []) - button.af_setBackgroundImage( + button.af.setBackgroundImage( for: [], urlRequest: URLRequest(url: url), placeholderImage: nil, @@ -1072,6 +1072,62 @@ class UIButtonTests: BaseTestCase { XCTAssertTrue(result?.isSuccess ?? false) } + func testThatImageRequestCanBeCancelledAndButtonIsDeallocated() { + // Given + var button: UIButton? = UIButton() + let expectation = self.expectation(description: "image download should succeed") + + var completionCalled: Bool? + var buttonReleased: Bool? + + // When + button?.af.setImage( + for: .normal, + urlRequest: URLRequest(url: url), + completion: { [weak button] _ in + completionCalled = true + buttonReleased = button == nil + + expectation.fulfill() + } + ) + + button = nil + waitForExpectations(timeout: timeout, handler: nil) + + // Then + XCTAssertEqual(completionCalled, true) + XCTAssertEqual(buttonReleased, true) + } + + func testThatBackgroundImageRequestCanBeCancelledAndButtonIsDeallocated() { + // Given + var button: UIButton? = UIButton() + let expectation = self.expectation(description: "image download should succeed") + + var completionCalled: Bool? + var buttonReleased: Bool? + + // When + button?.af.setBackgroundImage( + for: .normal, + urlRequest: URLRequest(url: url), + completion: { [weak button] _ in + completionCalled = true + buttonReleased = button == nil + + expectation.fulfill() + } + ) + + button = nil + waitForExpectations(timeout: timeout, handler: nil) + + // Then + XCTAssertEqual(completionCalled, true) + XCTAssertEqual(buttonReleased, true) + } + // MARK: - Redirects func testThatImageBehindRedirectCanBeDownloaded() { @@ -1088,7 +1144,7 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setImage(for: [], url: url) + button.af.setImage(for: [], url: url) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -1110,7 +1166,7 @@ class UIButtonTests: BaseTestCase { } // When - button.af_setBackgroundImage(for: [], url: url) + button.af.setBackgroundImage(for: [], url: url) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -1125,9 +1181,9 @@ class UIButtonTests: BaseTestCase { let button = UIButton() // When - button.af_setImage(for: [], url: url) - let acceptField = button.imageRequestReceipt(for: [])?.request.request?.allHTTPHeaderFields?["Accept"] - button.af_cancelImageRequest(for: []) + button.af.setImage(for: [], url: url) + let acceptField = button.af.imageRequestReceipt(for: [])?.request.request?.allHTTPHeaderFields?["Accept"] + button.af.cancelImageRequest(for: []) // Then XCTAssertNotNil(acceptField) diff --git a/Tests/UIImage+AlamofireImageTests.swift b/Tests/UIImage+AlamofireImageTests.swift index f94f72c9..f393f955 100644 --- a/Tests/UIImage+AlamofireImageTests.swift +++ b/Tests/UIImage+AlamofireImageTests.swift @@ -24,14 +24,16 @@ #if !os(macOS) +import Alamofire +@testable import AlamofireImage import UIKit -extension UIImage { - func af_isEqualToImage(_ image: UIImage, withinTolerance tolerance: UInt8 = 3) -> Bool { - guard size.equalTo(image.size) else { return false } +extension AlamofireExtension where ExtendedType: UIImage { + func isEqualToImage(_ image: UIImage, withinTolerance tolerance: UInt8 = 3) -> Bool { + guard type.size.equalTo(image.size) else { return false } - let image1 = af_imageWithPNGRepresentation().af_renderedImage() - let image2 = image.af_imageWithPNGRepresentation().af_renderedImage() + let image1 = imageWithPNGRepresentation().af.renderedImage() + let image2 = image.af.imageWithPNGRepresentation().af.renderedImage() guard let rendered1 = image1, let rendered2 = image2 else { return false } @@ -56,12 +58,12 @@ extension UIImage { return true } - public func af_renderedImage() -> UIImage? { + public func renderedImage() -> UIImage? { // Do not attempt to render animated images - guard images == nil else { return nil } + guard type.images == nil else { return nil } // Do not attempt to render if not backed by a CGImage - guard let image = cgImage?.copy() else { return nil } + guard let image = type.cgImage?.copy() else { return nil } let width = image.width let height = image.height @@ -101,7 +103,7 @@ extension UIImage { // Make sure the inflation was successful guard let renderedImage = context?.makeImage() else { return nil } - return UIImage(cgImage: renderedImage, scale: scale, orientation: imageOrientation) + return UIImage(cgImage: renderedImage, scale: type.scale, orientation: type.imageOrientation) } /** @@ -113,8 +115,8 @@ extension UIImage { - returns: The PNG representation image. */ - func af_imageWithPNGRepresentation() -> UIImage { - let data = pngData()! + func imageWithPNGRepresentation() -> UIImage { + let data = type.pngData()! let image = UIImage(data: data, scale: UIScreen.main.scale)! return image diff --git a/Tests/UIImageTests.swift b/Tests/UIImageTests.swift index dbd6c8d1..4174737e 100644 --- a/Tests/UIImageTests.swift +++ b/Tests/UIImageTests.swift @@ -92,8 +92,8 @@ class UIImageTestCase: BaseTestCase { let expectation = self.expectation(description: "image should be created successfully") DispatchQueue.global(qos: .utility).async { - let image = UIImage.af_threadSafeImage(with: data) - let imageWithScale = UIImage.af_threadSafeImage(with: data, scale: CGFloat(self.scale)) + let image = UIImage.af.threadSafeImage(with: data) + let imageWithScale = UIImage.af.threadSafeImage(with: data, scale: CGFloat(self.scale)) lock.lock() images.append(image) @@ -118,32 +118,32 @@ class UIImageTestCase: BaseTestCase { let unicornImage = image(forResource: "unicorn", withExtension: "png") // When, Then - rainbowImage.af_inflate() - unicornImage.af_inflate() + rainbowImage.af.inflate() + unicornImage.af.inflate() } func testThatImageThatHasAlreadyBeenInflatedIsNotInflatedAgain() { // Given let unicornImage = image(forResource: "unicorn", withExtension: "png") - unicornImage.af_inflate() + unicornImage.af.inflate() // When, Then - unicornImage.af_inflate() + unicornImage.af.inflate() } // MARK: - Alpha Tests func testThatImageAlphaComponentPropertiesReturnExpectedValues() { // Given, When, Then - XCTAssertTrue(appleImage.af_isOpaque) - XCTAssertTrue(pirateImage.af_isOpaque) - XCTAssertTrue(rainbowImage.af_isOpaque) - XCTAssertFalse(unicornImage.af_isOpaque) - - XCTAssertFalse(appleImage.af_containsAlphaComponent) - XCTAssertFalse(pirateImage.af_containsAlphaComponent) - XCTAssertFalse(rainbowImage.af_containsAlphaComponent) - XCTAssertTrue(unicornImage.af_containsAlphaComponent) + XCTAssertTrue(appleImage.af.isOpaque) + XCTAssertTrue(pirateImage.af.isOpaque) + XCTAssertTrue(rainbowImage.af.isOpaque) + XCTAssertFalse(unicornImage.af.isOpaque) + + XCTAssertFalse(appleImage.af.containsAlphaComponent) + XCTAssertFalse(pirateImage.af.containsAlphaComponent) + XCTAssertFalse(rainbowImage.af.containsAlphaComponent) + XCTAssertTrue(unicornImage.af.containsAlphaComponent) } // MARK: - Scaling Tests @@ -167,10 +167,10 @@ class UIImageTestCase: BaseTestCase { let h = Int(size.height.rounded()) // When - let scaledAppleImage = appleImage.af_imageScaled(to: size) - let scaledPirateImage = pirateImage.af_imageScaled(to: size) - let scaledRainbowImage = rainbowImage.af_imageScaled(to: size) - let scaledUnicornImage = unicornImage.af_imageScaled(to: size) + let scaledAppleImage = appleImage.af.imageScaled(to: size) + let scaledPirateImage = pirateImage.af.imageScaled(to: size) + let scaledRainbowImage = rainbowImage.af.imageScaled(to: size) + let scaledUnicornImage = unicornImage.af.imageScaled(to: size) // Then let expectedAppleImage = image(forResource: "apple-scaled-\(w)x\(h)-@\(scale)x", withExtension: "png") @@ -178,10 +178,10 @@ class UIImageTestCase: BaseTestCase { let expectedRainbowImage = image(forResource: "rainbow-scaled-\(w)x\(h)-@\(scale)x", withExtension: "png") let expectedUnicornImage = image(forResource: "unicorn-scaled-\(w)x\(h)-@\(scale)x", withExtension: "png") - XCTAssertTrue(scaledAppleImage.af_isEqualToImage(expectedAppleImage), "scaled apple image pixels do not match") - XCTAssertTrue(scaledPirateImage.af_isEqualToImage(expectedPirateImage), "scaled pirate image pixels do not match") - XCTAssertTrue(scaledRainbowImage.af_isEqualToImage(expectedRainbowImage), "scaled rainbow image pixels do not match") - XCTAssertTrue(scaledUnicornImage.af_isEqualToImage(expectedUnicornImage), "scaled unicorn image pixels do not match") + XCTAssertTrue(scaledAppleImage.af.isEqualToImage(expectedAppleImage), "scaled apple image pixels do not match") + XCTAssertTrue(scaledPirateImage.af.isEqualToImage(expectedPirateImage), "scaled pirate image pixels do not match") + XCTAssertTrue(scaledRainbowImage.af.isEqualToImage(expectedRainbowImage), "scaled rainbow image pixels do not match") + XCTAssertTrue(scaledUnicornImage.af.isEqualToImage(expectedUnicornImage), "scaled unicorn image pixels do not match") XCTAssertEqual(scaledAppleImage.scale, CGFloat(scale), "image scale should be equal to screen scale") XCTAssertEqual(scaledPirateImage.scale, CGFloat(scale), "image scale should be equal to screen scale") @@ -207,10 +207,10 @@ class UIImageTestCase: BaseTestCase { let h = Int(size.height.rounded()) // When - let scaledAppleImage = appleImage.af_imageAspectScaled(toFit: size) - let scaledPirateImage = pirateImage.af_imageAspectScaled(toFit: size) - let scaledRainbowImage = rainbowImage.af_imageAspectScaled(toFit: size) - let scaledUnicornImage = unicornImage.af_imageAspectScaled(toFit: size) + let scaledAppleImage = appleImage.af.imageAspectScaled(toFit: size) + let scaledPirateImage = pirateImage.af.imageAspectScaled(toFit: size) + let scaledRainbowImage = rainbowImage.af.imageAspectScaled(toFit: size) + let scaledUnicornImage = unicornImage.af.imageAspectScaled(toFit: size) // Then let expectedAppleImage = image(forResource: "apple-aspect.scaled.to.fit-\(w)x\(h)-@\(scale)x", withExtension: "png") @@ -218,10 +218,10 @@ class UIImageTestCase: BaseTestCase { let expectedRainbowImage = image(forResource: "rainbow-aspect.scaled.to.fit-\(w)x\(h)-@\(scale)x", withExtension: "png") let expectedUnicornImage = image(forResource: "unicorn-aspect.scaled.to.fit-\(w)x\(h)-@\(scale)x", withExtension: "png") - XCTAssertTrue(scaledAppleImage.af_isEqualToImage(expectedAppleImage, withinTolerance: 53), "scaled apple image pixels do not match") - XCTAssertTrue(scaledPirateImage.af_isEqualToImage(expectedPirateImage), "scaled pirate image pixels do not match") - XCTAssertTrue(scaledRainbowImage.af_isEqualToImage(expectedRainbowImage, withinTolerance: 46), "scaled rainbow image pixels do not match") - XCTAssertTrue(scaledUnicornImage.af_isEqualToImage(expectedUnicornImage, withinTolerance: 26), "scaled unicorn image pixels do not match") + XCTAssertTrue(scaledAppleImage.af.isEqualToImage(expectedAppleImage, withinTolerance: 53), "scaled apple image pixels do not match") + XCTAssertTrue(scaledPirateImage.af.isEqualToImage(expectedPirateImage), "scaled pirate image pixels do not match") + XCTAssertTrue(scaledRainbowImage.af.isEqualToImage(expectedRainbowImage, withinTolerance: 46), "scaled rainbow image pixels do not match") + XCTAssertTrue(scaledUnicornImage.af.isEqualToImage(expectedUnicornImage, withinTolerance: 26), "scaled unicorn image pixels do not match") XCTAssertEqual(scaledAppleImage.scale, CGFloat(scale), "image scale should be equal to screen scale") XCTAssertEqual(scaledPirateImage.scale, CGFloat(scale), "image scale should be equal to screen scale") @@ -247,10 +247,10 @@ class UIImageTestCase: BaseTestCase { let h = Int(size.height.rounded()) // When - let scaledAppleImage = appleImage.af_imageAspectScaled(toFill: size) - let scaledPirateImage = pirateImage.af_imageAspectScaled(toFill: size) - let scaledRainbowImage = rainbowImage.af_imageAspectScaled(toFill: size) - let scaledUnicornImage = unicornImage.af_imageAspectScaled(toFill: size) + let scaledAppleImage = appleImage.af.imageAspectScaled(toFill: size) + let scaledPirateImage = pirateImage.af.imageAspectScaled(toFill: size) + let scaledRainbowImage = rainbowImage.af.imageAspectScaled(toFill: size) + let scaledUnicornImage = unicornImage.af.imageAspectScaled(toFill: size) // Then let expectedAppleImage = image(forResource: "apple-aspect.scaled.to.fill-\(w)x\(h)-@\(scale)x", withExtension: "png") @@ -258,10 +258,10 @@ class UIImageTestCase: BaseTestCase { let expectedRainbowImage = image(forResource: "rainbow-aspect.scaled.to.fill-\(w)x\(h)-@\(scale)x", withExtension: "png") let expectedUnicornImage = image(forResource: "unicorn-aspect.scaled.to.fill-\(w)x\(h)-@\(scale)x", withExtension: "png") - XCTAssertTrue(scaledAppleImage.af_isEqualToImage(expectedAppleImage), "scaled apple image pixels do not match") - XCTAssertTrue(scaledPirateImage.af_isEqualToImage(expectedPirateImage), "scaled pirate image pixels do not match") - XCTAssertTrue(scaledRainbowImage.af_isEqualToImage(expectedRainbowImage), "scaled rainbow image pixels do not match") - XCTAssertTrue(scaledUnicornImage.af_isEqualToImage(expectedUnicornImage), "scaled unicorn image pixels do not match") + XCTAssertTrue(scaledAppleImage.af.isEqualToImage(expectedAppleImage), "scaled apple image pixels do not match") + XCTAssertTrue(scaledPirateImage.af.isEqualToImage(expectedPirateImage), "scaled pirate image pixels do not match") + XCTAssertTrue(scaledRainbowImage.af.isEqualToImage(expectedRainbowImage), "scaled rainbow image pixels do not match") + XCTAssertTrue(scaledUnicornImage.af.isEqualToImage(expectedUnicornImage), "scaled unicorn image pixels do not match") XCTAssertEqual(scaledAppleImage.scale, CGFloat(scale), "image scale should be equal to screen scale") XCTAssertEqual(scaledPirateImage.scale, CGFloat(scale), "image scale should be equal to screen scale") @@ -278,10 +278,10 @@ class UIImageTestCase: BaseTestCase { let r = Int(radius.rounded()) // When - let roundedAppleImage = appleImage.af_imageRounded(withCornerRadius: radius, divideRadiusByImageScale: true) - let roundedPirateImage = pirateImage.af_imageRounded(withCornerRadius: radius, divideRadiusByImageScale: true) - let roundedRainbowImage = rainbowImage.af_imageRounded(withCornerRadius: radius, divideRadiusByImageScale: true) - let roundedUnicornImage = unicornImage.af_imageRounded(withCornerRadius: radius, divideRadiusByImageScale: true) + let roundedAppleImage = appleImage.af.imageRounded(withCornerRadius: radius, divideRadiusByImageScale: true) + let roundedPirateImage = pirateImage.af.imageRounded(withCornerRadius: radius, divideRadiusByImageScale: true) + let roundedRainbowImage = rainbowImage.af.imageRounded(withCornerRadius: radius, divideRadiusByImageScale: true) + let roundedUnicornImage = unicornImage.af.imageRounded(withCornerRadius: radius, divideRadiusByImageScale: true) // Then let expectedAppleImage = image(forResource: "apple-radius-\(r)", withExtension: "png") @@ -289,10 +289,10 @@ class UIImageTestCase: BaseTestCase { let expectedRainbowImage = image(forResource: "rainbow-radius-\(r)", withExtension: "png") let expectedUnicornImage = image(forResource: "unicorn-radius-\(r)", withExtension: "png") - XCTAssertTrue(roundedAppleImage.af_isEqualToImage(expectedAppleImage), "rounded apple image pixels do not match") - XCTAssertTrue(roundedPirateImage.af_isEqualToImage(expectedPirateImage), "rounded pirate image pixels do not match") - XCTAssertTrue(roundedRainbowImage.af_isEqualToImage(expectedRainbowImage), "rounded rainbow image pixels do not match") - XCTAssertTrue(roundedUnicornImage.af_isEqualToImage(expectedUnicornImage), "rounded unicorn image pixels do not match") + XCTAssertTrue(roundedAppleImage.af.isEqualToImage(expectedAppleImage), "rounded apple image pixels do not match") + XCTAssertTrue(roundedPirateImage.af.isEqualToImage(expectedPirateImage), "rounded pirate image pixels do not match") + XCTAssertTrue(roundedRainbowImage.af.isEqualToImage(expectedRainbowImage), "rounded rainbow image pixels do not match") + XCTAssertTrue(roundedUnicornImage.af.isEqualToImage(expectedUnicornImage), "rounded unicorn image pixels do not match") XCTAssertEqual(roundedAppleImage.scale, CGFloat(scale), "image scale should be equal to screen scale") XCTAssertEqual(roundedPirateImage.scale, CGFloat(scale), "image scale should be equal to screen scale") @@ -302,10 +302,10 @@ class UIImageTestCase: BaseTestCase { func testThatImageIsRoundedIntoCircle() { // Given, When - let circularAppleImage = appleImage.af_imageRoundedIntoCircle() - let circularPirateImage = pirateImage.af_imageRoundedIntoCircle() - let circularRainbowImage = rainbowImage.af_imageRoundedIntoCircle() - let circularUnicornImage = unicornImage.af_imageRoundedIntoCircle() + let circularAppleImage = appleImage.af.imageRoundedIntoCircle() + let circularPirateImage = pirateImage.af.imageRoundedIntoCircle() + let circularRainbowImage = rainbowImage.af.imageRoundedIntoCircle() + let circularUnicornImage = unicornImage.af.imageRoundedIntoCircle() // Then let expectedAppleImage = image(forResource: "apple-circle", withExtension: "png") @@ -313,10 +313,10 @@ class UIImageTestCase: BaseTestCase { let expectedRainbowImage = image(forResource: "rainbow-circle", withExtension: "png") let expectedUnicornImage = image(forResource: "unicorn-circle", withExtension: "png") - XCTAssertTrue(circularAppleImage.af_isEqualToImage(expectedAppleImage), "rounded apple image pixels do not match") - XCTAssertTrue(circularPirateImage.af_isEqualToImage(expectedPirateImage), "rounded pirate image pixels do not match") - XCTAssertTrue(circularRainbowImage.af_isEqualToImage(expectedRainbowImage), "rounded rainbow image pixels do not match") - XCTAssertTrue(circularUnicornImage.af_isEqualToImage(expectedUnicornImage), "rounded unicorn image pixels do not match") + XCTAssertTrue(circularAppleImage.af.isEqualToImage(expectedAppleImage), "rounded apple image pixels do not match") + XCTAssertTrue(circularPirateImage.af.isEqualToImage(expectedPirateImage), "rounded pirate image pixels do not match") + XCTAssertTrue(circularRainbowImage.af.isEqualToImage(expectedRainbowImage), "rounded rainbow image pixels do not match") + XCTAssertTrue(circularUnicornImage.af.isEqualToImage(expectedUnicornImage), "rounded unicorn image pixels do not match") XCTAssertEqual(circularAppleImage.scale, CGFloat(scale), "image scale should be equal to screen scale") XCTAssertEqual(circularPirateImage.scale, CGFloat(scale), "image scale should be equal to screen scale") @@ -348,12 +348,12 @@ class UIImageTestCase: BaseTestCase { let parameters: [String: Any] = ["inputRadius": 8] // When - let blurredImage = unicornImage.af_imageFiltered(withCoreImageFilter: "CIGaussianBlur", parameters: parameters) + let blurredImage = unicornImage.af.imageFiltered(withCoreImageFilter: "CIGaussianBlur", parameters: parameters) // Then if let blurredImage = blurredImage { let expectedBlurredImage = image(forResource: "unicorn-blurred-8", withExtension: "png") - let pixelsMatch = blurredImage.af_isEqualToImage(expectedBlurredImage) + let pixelsMatch = blurredImage.af.isEqualToImage(expectedBlurredImage) XCTAssertTrue(pixelsMatch, "pixels match should be true") } else { @@ -365,12 +365,12 @@ class UIImageTestCase: BaseTestCase { guard #available(iOS 9.0, *) else { return } // Given, When - let sepiaImage = unicornImage.af_imageFiltered(withCoreImageFilter: "CISepiaTone") + let sepiaImage = unicornImage.af.imageFiltered(withCoreImageFilter: "CISepiaTone") // Then if let sepiaImage = sepiaImage { let expectedSepiaImage = image(forResource: "unicorn-sepia.tone", withExtension: "png") - XCTAssertTrue(sepiaImage.af_isEqualToImage(expectedSepiaImage), "sepia image pixels do not match") + XCTAssertTrue(sepiaImage.af.isEqualToImage(expectedSepiaImage), "sepia image pixels do not match") } else { XCTFail("sepia image should not be nil") } @@ -383,7 +383,7 @@ class UIImageTestCase: BaseTestCase { let filterName = "SomeFilterThatDoesNotExist" // When - let filteredImage = unicornImage.af_imageFiltered(withCoreImageFilter: filterName) + let filteredImage = unicornImage.af.imageFiltered(withCoreImageFilter: filterName) // Then XCTAssertNil(filteredImage, "filtered image should be nil") diff --git a/Tests/UIImageViewTests.swift b/Tests/UIImageViewTests.swift index c9b8f895..3a548393 100644 --- a/Tests/UIImageViewTests.swift +++ b/Tests/UIImageViewTests.swift @@ -69,7 +69,7 @@ class UIImageViewTestCase: BaseTestCase { ImageDownloader.defaultURLCache().removeAllCachedResponses() ImageDownloader.default.imageCache?.removeAllImages() - UIImageView.af_sharedImageDownloader = ImageDownloader.default + UIImageView.af.sharedImageDownloader = ImageDownloader.default } // MARK: - Image Download @@ -85,7 +85,7 @@ class UIImageViewTestCase: BaseTestCase { } // When - imageView.af_setImage(withURL: url) + imageView.af.setImage(withURL: url) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -103,8 +103,8 @@ class UIImageViewTestCase: BaseTestCase { } // When - imageView.af_setImage(withURL: url) - imageView.af_setImage(withURL: url) + imageView.af.setImage(withURL: url) + imageView.af.setImage(withURL: url) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -123,12 +123,12 @@ class UIImageViewTestCase: BaseTestCase { } // When - imageView.af_setImage(withURL: url) + imageView.af.setImage(withURL: url) waitForExpectations(timeout: timeout, handler: nil) // Then XCTAssertTrue(imageDownloadComplete, "image download complete should be true") - XCTAssertNil(imageView.af_activeRequestReceipt, "active request receipt should be nil after download completes") + XCTAssertNil(imageView.af.activeRequestReceipt, "active request receipt should be nil after download completes") } // MARK: - Image Downloaders @@ -145,17 +145,17 @@ class UIImageViewTestCase: BaseTestCase { let configuration = URLSessionConfiguration.ephemeral let imageDownloader = ImageDownloader(configuration: configuration) - imageView.af_imageDownloader = imageDownloader + imageView.af.imageDownloader = imageDownloader // When - imageView.af_setImage(withURL: url) + imageView.af.setImage(withURL: url) let activeRequestCount = imageDownloader.activeRequestCount waitForExpectations(timeout: timeout, handler: nil) // Then XCTAssertTrue(imageDownloadComplete, "image download complete should be true") - XCTAssertNil(imageView.af_activeRequestReceipt, "active request receipt should be nil after download completes") + XCTAssertNil(imageView.af.activeRequestReceipt, "active request receipt should be nil after download completes") XCTAssertEqual(activeRequestCount, 1, "active request count should be 1") } @@ -172,7 +172,7 @@ class UIImageViewTestCase: BaseTestCase { } // When - imageView.af_setImage( + imageView.af.setImage( withURL: url, serializer: ImageResponseSerializer(imageScale: 4.0, inflateResponseImage: false) ) @@ -182,7 +182,7 @@ class UIImageViewTestCase: BaseTestCase { // Then XCTAssertTrue(imageDownloadComplete, "image download complete should be true") XCTAssertEqual(imageView.image?.scale, 4.0) - XCTAssertEqual(imageView.image?.af_inflated, false) + XCTAssertEqual(imageView.image?.af.isInflated, false) } // MARK: - Image Cache @@ -202,8 +202,8 @@ class UIImageViewTestCase: BaseTestCase { waitForExpectations(timeout: timeout, handler: nil) // When - imageView.af_setImage(withURL: url) - imageView.af_cancelImageRequest() + imageView.af.setImage(withURL: url) + imageView.af.cancelImageRequest() // Then XCTAssertNotNil(imageView.image, "image view image should not be nil") @@ -224,8 +224,8 @@ class UIImageViewTestCase: BaseTestCase { waitForExpectations(timeout: timeout, handler: nil) // When - imageView.af_setImage(withURL: url, filter: CircleFilter()) - imageView.af_cancelImageRequest() + imageView.af.setImage(withURL: url, filter: CircleFilter()) + imageView.af.cancelImageRequest() // Then XCTAssertNotNil(imageView.image, "image view image should not be nil") @@ -236,9 +236,9 @@ class UIImageViewTestCase: BaseTestCase { let imageDownloader = ImageDownloader() // When - let firstEqualityCheck = UIImageView.af_sharedImageDownloader === imageDownloader - UIImageView.af_sharedImageDownloader = imageDownloader - let secondEqualityCheck = UIImageView.af_sharedImageDownloader === imageDownloader + let firstEqualityCheck = UIImageView.af.sharedImageDownloader === imageDownloader + UIImageView.af.sharedImageDownloader = imageDownloader + let secondEqualityCheck = UIImageView.af.sharedImageDownloader === imageDownloader // Then XCTAssertFalse(firstEqualityCheck, "first equality check should be false") @@ -257,7 +257,7 @@ class UIImageViewTestCase: BaseTestCase { } // When - imageView.af_setImage(withURL: url, cacheKey: cacheKey) + imageView.af.setImage(withURL: url, cacheKey: cacheKey) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -277,7 +277,7 @@ class UIImageViewTestCase: BaseTestCase { let imageView = TestImageView() // When - imageView.af_setImage(withURL: url, placeholderImage: placeholderImage) + imageView.af.setImage(withURL: url, placeholderImage: placeholderImage) let initialImageEqualsPlaceholderImage = imageView.image === placeholderImage imageView.imageObserver = { @@ -300,7 +300,7 @@ class UIImageViewTestCase: BaseTestCase { let imageView = TestImageView() // When - imageView.af_setImage(withURLRequest: ThrowingURLRequestConvertible(), placeholderImage: placeholderImage) + imageView.af.setImage(withURLRequest: ThrowingURLRequestConvertible(), placeholderImage: placeholderImage) let initialImageEqualsPlaceholderImage = imageView.image === placeholderImage // Then @@ -322,7 +322,7 @@ class UIImageViewTestCase: BaseTestCase { waitForExpectations(timeout: timeout, handler: nil) // When - imageView.af_setImage(withURL: url, placeholderImage: placeholderImage) + imageView.af.setImage(withURL: url, placeholderImage: placeholderImage) // Then XCTAssertNotNil(imageView.image, "image view image should not be nil") @@ -345,7 +345,7 @@ class UIImageViewTestCase: BaseTestCase { } // When - imageView.af_setImage(withURL: url, filter: filter) + imageView.af.setImage(withURL: url, filter: filter) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -370,7 +370,7 @@ class UIImageViewTestCase: BaseTestCase { } // When - imageView.af_setImage(withURL: url, placeholderImage: nil, filter: nil, imageTransition: .crossDissolve(0.5)) + imageView.af.setImage(withURL: url, placeholderImage: nil, filter: nil, imageTransition: .crossDissolve(0.5)) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -386,43 +386,43 @@ class UIImageViewTestCase: BaseTestCase { // When let expectation1 = expectation(description: "image download should succeed") imageView.imageObserver = { expectation1.fulfill() } - imageView.af_setImage(withURL: url, imageTransition: .noTransition) + imageView.af.setImage(withURL: url, imageTransition: .noTransition) waitForExpectations(timeout: timeout, handler: nil) let expectation2 = expectation(description: "image download should succeed") imageView.imageObserver = { expectation2.fulfill() } ImageDownloader.default.imageCache?.removeAllImages() - imageView.af_setImage(withURL: url, imageTransition: .crossDissolve(0.1)) + imageView.af.setImage(withURL: url, imageTransition: .crossDissolve(0.1)) waitForExpectations(timeout: timeout, handler: nil) let expectation3 = expectation(description: "image download should succeed") imageView.imageObserver = { expectation3.fulfill() } ImageDownloader.default.imageCache?.removeAllImages() - imageView.af_setImage(withURL: url, imageTransition: .curlDown(0.1)) + imageView.af.setImage(withURL: url, imageTransition: .curlDown(0.1)) waitForExpectations(timeout: timeout, handler: nil) let expectation4 = expectation(description: "image download should succeed") imageView.imageObserver = { expectation4.fulfill() } ImageDownloader.default.imageCache?.removeAllImages() - imageView.af_setImage(withURL: url, imageTransition: .curlUp(0.1)) + imageView.af.setImage(withURL: url, imageTransition: .curlUp(0.1)) waitForExpectations(timeout: timeout, handler: nil) let expectation5 = expectation(description: "image download should succeed") imageView.imageObserver = { expectation5.fulfill() } ImageDownloader.default.imageCache?.removeAllImages() - imageView.af_setImage(withURL: url, imageTransition: .flipFromBottom(0.1)) + imageView.af.setImage(withURL: url, imageTransition: .flipFromBottom(0.1)) waitForExpectations(timeout: timeout, handler: nil) let expectation6 = expectation(description: "image download should succeed") imageView.imageObserver = { expectation6.fulfill() } ImageDownloader.default.imageCache?.removeAllImages() - imageView.af_setImage(withURL: url, imageTransition: .flipFromLeft(0.1)) + imageView.af.setImage(withURL: url, imageTransition: .flipFromLeft(0.1)) waitForExpectations(timeout: timeout, handler: nil) let expectation7 = expectation(description: "image download should succeed") imageView.imageObserver = { expectation7.fulfill() } ImageDownloader.default.imageCache?.removeAllImages() - imageView.af_setImage(withURL: url, imageTransition: .flipFromRight(0.1)) + imageView.af.setImage(withURL: url, imageTransition: .flipFromRight(0.1)) waitForExpectations(timeout: timeout, handler: nil) let expectation8 = expectation(description: "image download should succeed") @@ -430,7 +430,7 @@ class UIImageViewTestCase: BaseTestCase { expectation8.fulfill() } ImageDownloader.default.imageCache?.removeAllImages() - imageView.af_setImage(withURL: url, imageTransition: .flipFromTop(0.1)) + imageView.af.setImage(withURL: url, imageTransition: .flipFromTop(0.1)) waitForExpectations(timeout: timeout, handler: nil) let expectation9 = expectation(description: "image download should succeed") @@ -439,7 +439,7 @@ class UIImageViewTestCase: BaseTestCase { expectation9.fulfill() } ImageDownloader.default.imageCache?.removeAllImages() - imageView.af_setImage( + imageView.af.setImage( withURL: url, imageTransition: .custom( duration: 0.5, @@ -473,7 +473,7 @@ class UIImageViewTestCase: BaseTestCase { var result: AFIResult? // When - imageView.af_setImage( + imageView.af.setImage( withURLRequest: urlRequest, placeholderImage: nil, filter: nil, @@ -504,7 +504,7 @@ class UIImageViewTestCase: BaseTestCase { var result: AFIResult? // When - imageView.af_setImage( + imageView.af.setImage( withURLRequest: urlRequest, placeholderImage: nil, filter: nil, @@ -535,7 +535,7 @@ class UIImageViewTestCase: BaseTestCase { var result: AFIResult? // When - imageView.af_setImage( + imageView.af.setImage( withURLRequest: urlRequest, placeholderImage: nil, filter: nil, @@ -568,7 +568,7 @@ class UIImageViewTestCase: BaseTestCase { var result: AFIResult? // When - imageView.af_setImage( + imageView.af.setImage( withURL: url, placeholderImage: nil, filter: nil, @@ -610,7 +610,7 @@ class UIImageViewTestCase: BaseTestCase { let downloadExpectation = expectation(description: "image download should succeed") // When - UIImageView.af_sharedImageDownloader.download(urlRequest) { _ in + UIImageView.af.sharedImageDownloader.download(urlRequest) { _ in downloadExpectation.fulfill() } @@ -619,7 +619,7 @@ class UIImageViewTestCase: BaseTestCase { let cachedExpectation = expectation(description: "image should be cached") var result: AFIResult? - imageView.af_setImage( + imageView.af.setImage( withURLRequest: urlRequest, placeholderImage: nil, filter: nil, @@ -650,7 +650,7 @@ class UIImageViewTestCase: BaseTestCase { var result: AFIResult? // When - imageView.af_setImage( + imageView.af.setImage( withURLRequest: urlRequest, placeholderImage: nil, filter: nil, @@ -662,7 +662,7 @@ class UIImageViewTestCase: BaseTestCase { } ) - imageView.af_cancelImageRequest() + imageView.af.cancelImageRequest() waitForExpectations(timeout: timeout, handler: nil) // Then @@ -681,7 +681,7 @@ class UIImageViewTestCase: BaseTestCase { var result: AFIResult? // When - imageView.af_setImage( + imageView.af.setImage( withURLRequest: URLRequest(url: url), placeholderImage: nil, filter: nil, @@ -691,7 +691,7 @@ class UIImageViewTestCase: BaseTestCase { } ) - imageView.af_setImage( + imageView.af.setImage( withURLRequest: URLRequest(url: URL(string: "https://httpbin.org/image/png")!), placeholderImage: nil, filter: nil, @@ -722,7 +722,7 @@ class UIImageViewTestCase: BaseTestCase { var result: AFIResult? // When - imageView.af_setImage( + imageView.af.setImage( withURLRequest: URLRequest(url: url), placeholderImage: nil, filter: nil, @@ -732,9 +732,9 @@ class UIImageViewTestCase: BaseTestCase { } ) - imageView.af_cancelImageRequest() + imageView.af.cancelImageRequest() - imageView.af_setImage( + imageView.af.setImage( withURLRequest: URLRequest(url: url), placeholderImage: nil, filter: nil, @@ -755,6 +755,33 @@ class UIImageViewTestCase: BaseTestCase { XCTAssertTrue(result?.isSuccess ?? false, "result should be a success case") } + func testThatActiveRequestCanBeCancelledAndImageViewIsDeallocated() { + // Given + var imageView: UIImageView? = UIImageView() + let expectation = self.expectation(description: "image download should succeed") + + var completionCalled: Bool? + var imageViewReleased: Bool? + + // When + imageView?.af.setImage( + withURLRequest: URLRequest(url: url), + completion: { [weak imageView] _ in + completionCalled = true + imageViewReleased = imageView == nil + + expectation.fulfill() + } + ) + + imageView = nil + waitForExpectations(timeout: timeout, handler: nil) + + // Then + XCTAssertEqual(completionCalled, true) + XCTAssertEqual(imageViewReleased, true) + } + // MARK: - Redirects func testThatImageBehindRedirectCanBeDownloaded() { @@ -771,7 +798,7 @@ class UIImageViewTestCase: BaseTestCase { } // When - imageView.af_setImage(withURL: url) + imageView.af.setImage(withURL: url) waitForExpectations(timeout: timeout, handler: nil) // Then @@ -786,9 +813,9 @@ class UIImageViewTestCase: BaseTestCase { let imageView = UIImageView() // When - imageView.af_setImage(withURL: url) - let acceptField = imageView.af_activeRequestReceipt?.request.request?.headers["Accept"] - imageView.af_cancelImageRequest() + imageView.af.setImage(withURL: url) + let acceptField = imageView.af.activeRequestReceipt?.request.request?.headers["Accept"] + imageView.af.cancelImageRequest() // Then XCTAssertNotNil(acceptField)