diff --git a/Chun.xcodeproj/project.pbxproj b/Chun.xcodeproj/project.pbxproj index a9a771f..4da1f72 100644 --- a/Chun.xcodeproj/project.pbxproj +++ b/Chun.xcodeproj/project.pbxproj @@ -187,7 +187,8 @@ 348D82C91AA9A71D00ABA121 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0630; + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0720; ORGANIZATIONNAME = "Chun Tips"; TargetAttributes = { 348D82D11AA9A71D00ABA121 = { diff --git a/Chun/Chun.swift b/Chun/Chun.swift index 74198d1..40f5c85 100644 --- a/Chun/Chun.swift +++ b/Chun/Chun.swift @@ -22,30 +22,30 @@ private var key = 0 public extension UIImageView { /** - Set the imageView image with image url. - We will fetch and cache the image asynchronous. - - :param: url the local or remote url for the image - */ + Set the imageView image with image url. + We will fetch and cache the image asynchronous. + + - parameter url: the local or remote url for the image + */ public func setImageWithURL(url: NSURL) { self.setImageWithURL(url, placeholderImage: nil) } /** - Set the imageView image with the image url - Before the url image load, will display placeholderImage - - :param: url the local or remote url for the image - :param: placeholderImage the image to be set initially - */ + Set the imageView image with the image url + Before the url image load, will display placeholderImage + + - parameter url: the local or remote url for the image + - parameter placeholderImage: the image to be set initially + */ public func setImageWithURL(url: NSURL, placeholderImage: UIImage?) { - + if let imageURLForChun = self.imageURLForChun { Chun.sharedInstance.cancelFetchWithURL(imageURLForChun) } - + self.imageURLForChun = url - + if let placeholderImage = placeholderImage { self.image = placeholderImage } @@ -54,7 +54,7 @@ public extension UIImageView { switch result { case let .Error(error): - println(error) + print(error) case let .Success(image, fetchedImageURL): if let strongSelf = self { if let imageURLForChun = strongSelf.imageURLForChun { @@ -68,7 +68,7 @@ public extension UIImageView { } } - }) + }) } private var imageURLForChun: NSURL? { @@ -76,7 +76,7 @@ public extension UIImageView { return objc_getAssociatedObject(self, &key) as? NSURL } set (url) { - objc_setAssociatedObject(self, &key, url, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)) + objc_setAssociatedObject(self, &key, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } @@ -99,10 +99,10 @@ public class Chun { private let cache = ImageCache() private lazy var fetchers = [String: ImageFetcher]() - + /** - Cancel and remove all fetch - */ + Cancel and remove all fetch + */ public func destroyAllFetch() { for fetcher in self.fetchers.values { fetcher.cancelFetch() @@ -111,11 +111,11 @@ public class Chun { } /** - Fetch image with local or remote url - - :param: url the image url - :param: complete callback when the fetch comlete - */ + Fetch image with local or remote url + + - parameter url: the image url + - parameter complete: callback when the fetch comlete + */ public func fetchImageWithURL(url: NSURL, complete: (Result) -> Void) { let key = cacheKeyForRemoteURL(url) @@ -129,7 +129,8 @@ public class Chun { if exist { fetchURL = diskURL! } - let fetcher = ImageFetcher.fetchImage(fetchURL, completion: { (result: FetcherResult) -> Void in + + let completionClosure = { (result: FetcherResult) -> Void in switch result { case let .Error(error): let result = Result.Error(error: error) @@ -139,19 +140,30 @@ public class Chun { complete(result) self.cache.storeImage(image, imageData: imageData, key: key) } - self.fetchers[key] = nil + + if self.fetchers[key]?.completions?.count == 0{ + self.fetchers[key] = nil + } + } + + let fetcher = ImageFetcher.fetchImage(fetchURL, completion: completionClosure) + + if let fetcherSameKey = self.fetchers[key]{ + fetcherSameKey.completions?.append(completionClosure) + } + else{ + self.fetchers[key] = fetcher + } }) - self.fetchers[key] = fetcher - }) } } } /** - cancel fetch with image url - - :param: url the url witch waht to cancel - */ + cancel fetch with image url + + - parameter url: the url witch waht to cancel + */ public func cancelFetchWithURL(url: NSURL) { let key = cacheKeyForRemoteURL(url) @@ -163,15 +175,15 @@ public class Chun { } /** - Clear all cached image files in the disk - */ + Clear all cached image files in the disk + */ public func clearDisk() { self.cache.clearDisk(){} } /** - Clear all cached images in the memory - */ + Clear all cached images in the memory + */ public func clearMemory() { self.cache.clearMemory() } @@ -216,7 +228,7 @@ func md5String(string: String) -> String { } func cacheKeyForRemoteURL(url: NSURL) -> String { - return url.absoluteString! + return url.absoluteString } func == (left: NSURL, right: NSURL) -> Bool { @@ -229,9 +241,14 @@ func scaledImage(image: UIImage) -> UIImage { if image.images != nil && image.images?.count > 0 { var scaledImages = [UIImage]() for tempImage in image.images! { - scaledImages.append(scaledImage(tempImage as! UIImage)) + scaledImages.append(scaledImage(tempImage)) + } + if let image = UIImage.animatedImageWithImages(scaledImages, duration: image.duration){ + return image + } + else{ + return image } - return UIImage.animatedImageWithImages(scaledImages, duration: image.duration) } else { return image @@ -253,20 +270,20 @@ func decodedImageWithImage(image: UIImage) -> UIImage { var bitmapInfo = originalBitmapInfo switch (alphaInfo) { case .None: - bitmapInfo &= ~CGBitmapInfo.AlphaInfoMask - bitmapInfo |= CGBitmapInfo(CGImageAlphaInfo.NoneSkipFirst.rawValue) + bitmapInfo = [.ByteOrder32Little, CGBitmapInfo(rawValue: ~CGBitmapInfo.AlphaInfoMask.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue)] case .PremultipliedFirst, .PremultipliedLast, .NoneSkipFirst, .NoneSkipLast: break case .Only, .Last, .First: return image } - if let context = CGBitmapContextCreate(nil, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef), CGImageGetBitsPerComponent(imageRef), 0 , colorSpace, bitmapInfo) { + if let context = CGBitmapContextCreate(nil, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef), CGImageGetBitsPerComponent(imageRef), 0 , colorSpace, bitmapInfo.rawValue) { CGContextDrawImage(context, imageRect, imageRef) - let decompressedImageRef = CGBitmapContextCreateImage(context) - if let decompressedImage = UIImage(CGImage: decompressedImageRef, scale: image.scale, orientation: image.imageOrientation) { - return decompressedImage - } else { + + if let decompressedImageRef = CGBitmapContextCreateImage(context){ + return UIImage(CGImage: decompressedImageRef, scale: image.scale, orientation: image.imageOrientation) + } + else{ return image } } else { @@ -286,8 +303,8 @@ func imageWithData(data: NSData) -> UIImage? { image = UIImage(data: data) let orientation = imageOrientationFromImageData(data) if orientation != UIImageOrientation.Up { - if let tempImage = image { - image = UIImage(CGImage: tempImage.CGImage, scale: tempImage.scale, orientation: orientation) + if let tempImage = image, let tempCGImage = image?.CGImage{ + image = UIImage(CGImage: tempCGImage, scale: tempImage.scale, orientation: orientation) } } @@ -344,39 +361,39 @@ func exifOrientationToiOSOrientation(exifOrientation: Int) -> UIImageOrientation return orientation } -func animatedGIFWithData(data: NSData) -> UIImage { - let source = CGImageSourceCreateWithData(data, nil) - let count = CGImageSourceGetCount(source) - - var animatedImage: UIImage! - if count <= 1 { - animatedImage = UIImage(data: data) - } else { - var images = [UIImage]() - var duration: NSTimeInterval = 0.0 +func animatedGIFWithData(data: NSData) -> UIImage? { + if let source = CGImageSourceCreateWithData(data, nil){ + let count = CGImageSourceGetCount(source) - for index in 0.. NSTimeInterval { var frameDuration: NSTimeInterval = 0.1 - let frameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil) as NSDictionary + let frameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil) as! NSDictionary if let gifProperties = frameProperties[kCGImagePropertyGIFDictionary as String] as? NSDictionary { if let delay = gifProperties[kCGImagePropertyGIFUnclampedDelayTime as String] as? NSTimeInterval { frameDuration = delay @@ -447,12 +464,13 @@ class ImageFetcher { } deinit { - self.completion = nil + self.completions?.removeAll() + self.completions = nil } var cancelled = false - var completion: CompeltionClosure? + var completions: [CompeltionClosure?]? static func fetchImage(url: NSURL, completion: CompeltionClosure?) -> ImageFetcher { @@ -464,7 +482,10 @@ class ImageFetcher { fetcher = RemoteImageFetcher(imageURL: url) } - fetcher.completion = completion + if fetcher.completions == nil{ + fetcher.completions = [CompeltionClosure?]() + } + fetcher.completions?.append(completion) fetcher.startFetch() @@ -482,9 +503,12 @@ class ImageFetcher { final func failedWithError(error: NSError) { dispatch_main_async_safe { if !self.cancelled { - if let completionClosure = self.completion { + if var completionClosures = self.completions { let result = FetcherResult.Error(error: error) - completionClosure(result) + for _ in 0.. Void) { dispatch_async(self.ioQueue) { - self.fileManager.removeItemAtPath(ImageCache.basePath, error: nil) - self.fileManager.createDirectoryAtPath(ImageCache.basePath, withIntermediateDirectories: true, attributes: nil, error: nil) + do { + try self.fileManager.removeItemAtPath(ImageCache.basePath) + } catch _ { + } + do { + try self.fileManager.createDirectoryAtPath(ImageCache.basePath, withIntermediateDirectories: true, attributes: nil) + } catch _ { + } dispatch_main_async_safe { completion() } @@ -735,7 +774,7 @@ class ImageCache { dispatch_async(self.ioQueue) { let diskCacheURL = NSURL.fileURLWithPath(ImageCache.basePath) let resourceKeys = [NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey] - let fileEnumerator = self.fileManager.enumeratorAtURL(diskCacheURL!, includingPropertiesForKeys: resourceKeys, options: .SkipsHiddenFiles, errorHandler: nil) + let fileEnumerator = self.fileManager.enumeratorAtURL(diskCacheURL, includingPropertiesForKeys: resourceKeys, options: .SkipsHiddenFiles, errorHandler: nil) let expirationDate = NSDate(timeIntervalSinceNow: ImageCache.defaultCacheMaxAge) var cacheFiles = [NSURL: AnyObject]() @@ -744,7 +783,7 @@ class ImageCache { for fileURL in fileEnumerator!.allObjects { if let fileURL = fileURL as? NSURL { - if var resourceValues = fileURL.resourceValuesForKeys(resourceKeys, error: nil) { + if var resourceValues = try? fileURL.resourceValuesForKeys(resourceKeys) { let isDir = resourceValues[NSURLIsDirectoryKey] as! Bool if isDir { continue @@ -763,7 +802,10 @@ class ImageCache { } for fileUrl in urlsToDelete { - self.fileManager.removeItemAtURL(fileUrl, error: nil) + do { + try self.fileManager.removeItemAtURL(fileUrl) + } catch _ { + } } dispatch_main_async_safe(completion) @@ -771,7 +813,7 @@ class ImageCache { } private func diskPathForKey(key: String) -> String { - return ImageCache.basePath.stringByAppendingPathComponent(self.cacheFileNameForKey(key)) + return (ImageCache.basePath as NSString).stringByAppendingPathComponent(self.cacheFileNameForKey(key)) } private func cacheFileNameForKey(key: String) -> String { diff --git a/Chun/CryptoSwiftMD5.swift b/Chun/CryptoSwiftMD5.swift index 5c3d3fc..75df7e7 100755 --- a/Chun/CryptoSwiftMD5.swift +++ b/Chun/CryptoSwiftMD5.swift @@ -26,12 +26,11 @@ import Foundation /** array of bytes, little-endian representation */ func arrayOfBytes(value:T, length:Int? = nil) -> [UInt8] { let totalBytes = length ?? (sizeofValue(value) * 8) - var v = value - var valuePointer = UnsafeMutablePointer.alloc(1) + let valuePointer = UnsafeMutablePointer.alloc(1) valuePointer.memory = value - var bytesPointer = UnsafeMutablePointer(valuePointer) + let bytesPointer = UnsafeMutablePointer(valuePointer) var bytes = [UInt8](count: totalBytes, repeatedValue: 0) for j in 0..(value:T, length:Int? = nil) -> [UInt8] { extension Int { /** Array of bytes with optional padding (little-endian) */ - func bytes(_ totalBytes: Int = sizeof(UInt32)) -> [UInt8] { + func bytes(totalBytes: Int = sizeof(UInt32)) -> [UInt8] { return arrayOfBytes(self, length: totalBytes) } } @@ -68,8 +67,8 @@ class HashBase { } /** Common part for hash calculation. Prepare header data. */ - func prepare(_ len:Int = 64) -> NSMutableData { - var tmpMessage: NSMutableData = NSMutableData(data: self.message) + func prepare(len:Int = 64) -> NSMutableData { + let tmpMessage: NSMutableData = NSMutableData(data: self.message) // Step 1. Append Padding Bits tmpMessage.appendBytes([0x80]) // append one bit (UInt8 with one bit) to message @@ -81,7 +80,7 @@ class HashBase { counter++ msgLength++ } - var bufZeros = UnsafeMutablePointer(calloc(counter, sizeof(UInt8))) + let bufZeros = UnsafeMutablePointer(calloc(counter, sizeof(UInt8))) tmpMessage.appendBytes(bufZeros, length: counter) return tmpMessage @@ -120,22 +119,21 @@ class MD5 : HashBase { private let h:[UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] func calculate() -> NSData { - var tmpMessage = prepare() + let tmpMessage = prepare() // hash values var hh = h // Step 2. Append Length a 64-bit representation of lengthInBits - var lengthInBits = (message.length * 8) - var lengthBytes = lengthInBits.bytes(64 / 8) - tmpMessage.appendBytes(reverse(lengthBytes)); + let lengthInBits = (message.length * 8) + let lengthBytes = lengthInBits.bytes(64 / 8) + tmpMessage.appendBytes(Array(lengthBytes.reverse())); // Process the message in successive 512-bit chunks: let chunkSizeBytes = 512 / 8 // 64 var leftMessageBytes = tmpMessage.length for (var i = 0; i < tmpMessage.length; i = i + chunkSizeBytes, leftMessageBytes -= chunkSizeBytes) { let chunk = tmpMessage.subdataWithRange(NSRange(location: i, length: min(chunkSizeBytes,leftMessageBytes))) - let bytes = tmpMessage.bytes; // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15 var M:[UInt32] = [UInt32](count: 16, repeatedValue: 0) @@ -178,7 +176,7 @@ class MD5 : HashBase { dTemp = D D = C C = B - B = B &+ rotateLeft((A &+ F &+ k[j] &+ M[g]), s[j]) + B = B &+ rotateLeft((A &+ F &+ k[j] &+ M[g]), n: s[j]) A = dTemp } @@ -188,8 +186,8 @@ class MD5 : HashBase { hh[3] = hh[3] &+ D } - var buf: NSMutableData = NSMutableData(); - hh.map({ (item) -> () in + let buf: NSMutableData = NSMutableData(); + let _ = hh.map({ (item) -> () in var i:UInt32 = item.littleEndian buf.appendBytes(&i, length: sizeofValue(i)) }) diff --git a/ChunDemo/ImageTableViewCell.swift b/ChunDemo/ImageTableViewCell.swift index 98f8d5e..86854ee 100644 --- a/ChunDemo/ImageTableViewCell.swift +++ b/ChunDemo/ImageTableViewCell.swift @@ -17,15 +17,15 @@ class ImageTableViewCell: UITableViewCell { self.testImageView = UIImageView(frame: CGRectZero) self.testImageView.contentMode = .ScaleAspectFit - self.testImageView.setTranslatesAutoresizingMaskIntoConstraints(false) + self.testImageView.translatesAutoresizingMaskIntoConstraints = false self.contentView.addSubview(self.testImageView) let views = ["testImageView": self.testImageView] - self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[testImageView]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: views)) - self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-0-[testImageView]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: views)) + self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[testImageView]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)) + self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-0-[testImageView]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)) } - required init(coder aDecoder: NSCoder) { + required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } } diff --git a/ChunDemo/Info.plist b/ChunDemo/Info.plist index 152e470..7b3556e 100644 --- a/ChunDemo/Info.plist +++ b/ChunDemo/Info.plist @@ -2,6 +2,11 @@ + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + CFBundleDevelopmentRegion en CFBundleExecutable diff --git a/ChunDemo/ViewController.swift b/ChunDemo/ViewController.swift index 5dfebbe..08c485d 100644 --- a/ChunDemo/ViewController.swift +++ b/ChunDemo/ViewController.swift @@ -21,13 +21,13 @@ class ViewController: UIViewController { self.tableView = UITableView(frame: CGRectZero, style: .Plain) self.tableView.dataSource = self self.tableView.delegate = self - self.tableView.setTranslatesAutoresizingMaskIntoConstraints(false) + self.tableView.translatesAutoresizingMaskIntoConstraints = false self.tableView.registerClass(ImageTableViewCell.classForCoder(), forCellReuseIdentifier: "ImageTableViewCell") self.view.addSubview(self.tableView) let views = ["tableView": self.tableView] - self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[tableView]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: views)) - self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-0-[tableView]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: views)) + self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[tableView]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)) + self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-0-[tableView]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)) } loadTableView()