Selaa lähdekoodia

添加基础库

jsonwang 3 vuotta sitten
vanhempi
commit
798af72077
93 muutettua tiedostoa jossa 11202 lisäystä ja 399 poistoa
  1. 6 4
      BFFramework.podspec
  2. BIN
      BFFramework/Assets/Lark20210513-102008.png
  3. 8 4
      Example/BFFramework.xcodeproj/project.pbxproj
  4. 10 2
      Example/Podfile.lock
  5. 22 0
      Example/Pods/Kingfisher/LICENSE
  6. 116 0
      Example/Pods/Kingfisher/README.md
  7. 503 0
      Example/Pods/Kingfisher/Sources/AnimatedImageView.swift
  8. 34 0
      Example/Pods/Kingfisher/Sources/Box.swift
  9. 87 0
      Example/Pods/Kingfisher/Sources/CacheSerializer.swift
  10. 144 0
      Example/Pods/Kingfisher/Sources/Filter.swift
  11. 96 0
      Example/Pods/Kingfisher/Sources/FormatIndicatedCacheSerializer.swift
  12. 1045 0
      Example/Pods/Kingfisher/Sources/Image.swift
  13. 742 0
      Example/Pods/Kingfisher/Sources/ImageCache.swift
  14. 677 0
      Example/Pods/Kingfisher/Sources/ImageDownloader.swift
  15. 191 0
      Example/Pods/Kingfisher/Sources/ImageModifier.swift
  16. 277 0
      Example/Pods/Kingfisher/Sources/ImagePrefetcher.swift
  17. 713 0
      Example/Pods/Kingfisher/Sources/ImageProcessor.swift
  18. 128 0
      Example/Pods/Kingfisher/Sources/ImageTransition.swift
  19. 263 0
      Example/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift
  20. 203 0
      Example/Pods/Kingfisher/Sources/Indicator.swift
  21. 37 0
      Example/Pods/Kingfisher/Sources/Kingfisher.h
  22. 77 0
      Example/Pods/Kingfisher/Sources/Kingfisher.swift
  23. 297 0
      Example/Pods/Kingfisher/Sources/KingfisherManager.swift
  24. 364 0
      Example/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift
  25. 82 0
      Example/Pods/Kingfisher/Sources/Placeholder.swift
  26. 53 0
      Example/Pods/Kingfisher/Sources/RequestModifier.swift
  27. 74 0
      Example/Pods/Kingfisher/Sources/Resource.swift
  28. 285 0
      Example/Pods/Kingfisher/Sources/String+MD5.swift
  29. 40 0
      Example/Pods/Kingfisher/Sources/ThreadHelper.swift
  30. 274 0
      Example/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift
  31. 18 3
      Example/Pods/Local Podspecs/BFFramework.podspec.json
  32. 10 2
      Example/Pods/Manifest.lock
  33. 752 372
      Example/Pods/Pods.xcodeproj/project.pbxproj
  34. 19 0
      Example/Pods/SnapKit/LICENSE
  35. 129 0
      Example/Pods/SnapKit/README.md
  36. 306 0
      Example/Pods/SnapKit/Source/Constraint.swift
  37. 195 0
      Example/Pods/SnapKit/Source/ConstraintAttributes.swift
  38. 37 0
      Example/Pods/SnapKit/Source/ConstraintConfig.swift
  39. 147 0
      Example/Pods/SnapKit/Source/ConstraintConstantTarget.swift
  40. 185 0
      Example/Pods/SnapKit/Source/ConstraintDSL.swift
  41. 69 0
      Example/Pods/SnapKit/Source/ConstraintDescription.swift
  42. 72 0
      Example/Pods/SnapKit/Source/ConstraintInsetTarget.swift
  43. 35 0
      Example/Pods/SnapKit/Source/ConstraintInsets.swift
  44. 61 0
      Example/Pods/SnapKit/Source/ConstraintItem.swift
  45. 36 0
      Example/Pods/SnapKit/Source/ConstraintLayoutGuide+Extensions.swift
  46. 37 0
      Example/Pods/SnapKit/Source/ConstraintLayoutGuide.swift
  47. 66 0
      Example/Pods/SnapKit/Source/ConstraintLayoutGuideDSL.swift
  48. 36 0
      Example/Pods/SnapKit/Source/ConstraintLayoutSupport.swift
  49. 56 0
      Example/Pods/SnapKit/Source/ConstraintLayoutSupportDSL.swift
  50. 204 0
      Example/Pods/SnapKit/Source/ConstraintMaker.swift
  51. 56 0
      Example/Pods/SnapKit/Source/ConstraintMakerEditable.swift
  52. 169 0
      Example/Pods/SnapKit/Source/ConstraintMakerExtendable.swift
  53. 49 0
      Example/Pods/SnapKit/Source/ConstraintMakerFinalizable.swift
  54. 68 0
      Example/Pods/SnapKit/Source/ConstraintMakerPriortizable.swift
  55. 113 0
      Example/Pods/SnapKit/Source/ConstraintMakerRelatable.swift
  56. 75 0
      Example/Pods/SnapKit/Source/ConstraintMultiplierTarget.swift
  57. 69 0
      Example/Pods/SnapKit/Source/ConstraintOffsetTarget.swift
  58. 77 0
      Example/Pods/SnapKit/Source/ConstraintPriority.swift
  59. 85 0
      Example/Pods/SnapKit/Source/ConstraintPriorityTarget.swift
  60. 66 0
      Example/Pods/SnapKit/Source/ConstraintRelatableTarget.swift
  61. 48 0
      Example/Pods/SnapKit/Source/ConstraintRelation.swift
  62. 152 0
      Example/Pods/SnapKit/Source/ConstraintView+Extensions.swift
  63. 35 0
      Example/Pods/SnapKit/Source/ConstraintView.swift
  64. 101 0
      Example/Pods/SnapKit/Source/ConstraintViewDSL.swift
  65. 160 0
      Example/Pods/SnapKit/Source/Debugging.swift
  66. 57 0
      Example/Pods/SnapKit/Source/LayoutConstraint.swift
  67. 93 0
      Example/Pods/SnapKit/Source/LayoutConstraintItem.swift
  68. 42 0
      Example/Pods/SnapKit/Source/Typealiases.swift
  69. 36 0
      Example/Pods/SnapKit/Source/UILayoutSupport+Extensions.swift
  70. 1 0
      Example/Pods/Target Support Files/BFFramework/BFFramework.debug.xcconfig
  71. 1 0
      Example/Pods/Target Support Files/BFFramework/BFFramework.release.xcconfig
  72. 24 0
      Example/Pods/Target Support Files/BFFramework/ResourceBundle-BFFramework-BFFramework-Info.plist
  73. 26 0
      Example/Pods/Target Support Files/Kingfisher/Kingfisher-Info.plist
  74. 5 0
      Example/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m
  75. 12 0
      Example/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch
  76. 17 0
      Example/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h
  77. 13 0
      Example/Pods/Target Support Files/Kingfisher/Kingfisher.debug.xcconfig
  78. 6 0
      Example/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap
  79. 13 0
      Example/Pods/Target Support Files/Kingfisher/Kingfisher.release.xcconfig
  80. 49 0
      Example/Pods/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example-acknowledgements.markdown
  81. 61 0
      Example/Pods/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example-acknowledgements.plist
  82. 4 0
      Example/Pods/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example-frameworks.sh
  83. 3 3
      Example/Pods/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example.debug.xcconfig
  84. 3 3
      Example/Pods/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example.release.xcconfig
  85. 3 3
      Example/Pods/Target Support Files/Pods-BFFramework_Tests/Pods-BFFramework_Tests.debug.xcconfig
  86. 3 3
      Example/Pods/Target Support Files/Pods-BFFramework_Tests/Pods-BFFramework_Tests.release.xcconfig
  87. 26 0
      Example/Pods/Target Support Files/SnapKit/SnapKit-Info.plist
  88. 5 0
      Example/Pods/Target Support Files/SnapKit/SnapKit-dummy.m
  89. 12 0
      Example/Pods/Target Support Files/SnapKit/SnapKit-prefix.pch
  90. 16 0
      Example/Pods/Target Support Files/SnapKit/SnapKit-umbrella.h
  91. 12 0
      Example/Pods/Target Support Files/SnapKit/SnapKit.debug.xcconfig
  92. 6 0
      Example/Pods/Target Support Files/SnapKit/SnapKit.modulemap
  93. 12 0
      Example/Pods/Target Support Files/SnapKit/SnapKit.release.xcconfig

+ 6 - 4
BFFramework.podspec

@@ -32,11 +32,13 @@ TODO: Add long description of the pod here.
 
   s.source_files = 'BFFramework/Classes/**/*'
 
-  # s.resource_bundles = {
-  #   'BFFramework' => ['BFFramework/Assets/*.png']
-  # }
+   s.resource_bundles = {
+     'BFFramework' => ['BFFramework/Assets/*.png']
+   }
 
   # s.public_header_files = 'Pod/Classes/**/*.h'
   # s.frameworks = 'UIKit', 'MapKit'
-  # s.dependency 'AFNetworking', '~> 2.3'
+#    s.dependency 'Alamofire','4.9.1' # 网络请求库
+    s.dependency 'SnapKit','4.2.0' # 布局库
+    s.dependency 'Kingfisher','4.10.1' # 图片加载库
 end

BIN
BFFramework/Assets/Lark20210513-102008.png


+ 8 - 4
Example/BFFramework.xcodeproj/project.pbxproj

@@ -29,7 +29,7 @@
 
 /* Begin PBXFileReference section */
 		0A776D860FF1D5C31F1EFED4 /* Pods-BFFramework_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BFFramework_Tests.debug.xcconfig"; path = "Target Support Files/Pods-BFFramework_Tests/Pods-BFFramework_Tests.debug.xcconfig"; sourceTree = "<group>"; };
-		4AFE6372AF8B96368545D2EC /* BFFramework.podspec */ = {isa = PBXFileReference; includeInIndex = 1; name = BFFramework.podspec; path = ../BFFramework.podspec; sourceTree = "<group>"; };
+		4AFE6372AF8B96368545D2EC /* BFFramework.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = BFFramework.podspec; path = ../BFFramework.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
 		607FACD01AFB9204008FA782 /* BFFramework_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BFFramework_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -41,10 +41,10 @@
 		607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = "<group>"; };
 		6A915303EE905EB336129B4A /* Pods_BFFramework_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BFFramework_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-		6E4851D661C9D481344DFF4B /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
+		6E4851D661C9D481344DFF4B /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
 		829C494BD0F0B8F588A124EE /* Pods-BFFramework_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BFFramework_Tests.release.xcconfig"; path = "Target Support Files/Pods-BFFramework_Tests/Pods-BFFramework_Tests.release.xcconfig"; sourceTree = "<group>"; };
 		9BB1EED7FC22B411C1A27C2A /* Pods_BFFramework_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BFFramework_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-		AA8A54E435E31352A12EFE1A /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; name = README.md; path = ../README.md; sourceTree = "<group>"; };
+		AA8A54E435E31352A12EFE1A /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
 		F1D5DDAED84A11983914AA81 /* Pods-BFFramework_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BFFramework_Example.debug.xcconfig"; path = "Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example.debug.xcconfig"; sourceTree = "<group>"; };
 		FA3ED99619248A3B8FB17EF0 /* Pods-BFFramework_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BFFramework_Example.release.xcconfig"; path = "Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example.release.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
@@ -156,7 +156,6 @@
 				0A776D860FF1D5C31F1EFED4 /* Pods-BFFramework_Tests.debug.xcconfig */,
 				829C494BD0F0B8F588A124EE /* Pods-BFFramework_Tests.release.xcconfig */,
 			);
-			name = Pods;
 			path = Pods;
 			sourceTree = "<group>";
 		};
@@ -228,6 +227,7 @@
 			developmentRegion = English;
 			hasScannedForEncodings = 0;
 			knownRegions = (
+				English,
 				en,
 				Base,
 			);
@@ -315,10 +315,14 @@
 			inputPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example-frameworks.sh",
 				"${BUILT_PRODUCTS_DIR}/BFFramework/BFFramework.framework",
+				"${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework",
+				"${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework",
 			);
 			name = "[CP] Embed Pods Frameworks";
 			outputPaths = (
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BFFramework.framework",
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework",
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;

+ 10 - 2
Example/Podfile.lock

@@ -1,7 +1,11 @@
 PODS:
-  - BFFramework (0.1.0)
+  - BFFramework (0.1.0):
+    - Kingfisher (= 4.10.1)
+    - SnapKit (= 4.2.0)
+  - Kingfisher (4.10.1)
   - Nimble (8.0.9)
   - Quick (2.2.1)
+  - SnapKit (4.2.0)
 
 DEPENDENCIES:
   - BFFramework (from `../`)
@@ -10,17 +14,21 @@ DEPENDENCIES:
 
 SPEC REPOS:
   trunk:
+    - Kingfisher
     - Nimble
     - Quick
+    - SnapKit
 
 EXTERNAL SOURCES:
   BFFramework:
     :path: "../"
 
 SPEC CHECKSUMS:
-  BFFramework: 08240bdb34be14676bd29d91e82050f4fad7b8f3
+  BFFramework: 2fa6044ae45950867e4bfcbbe88a3ed3cfda65ea
+  Kingfisher: c148cd7b47ebde9989f6bc7c27dcaa79d81279a0
   Nimble: 98b888285a615fd34f20e61753cf58ea1402bde4
   Quick: f5754d69b7013f5864c29aab9ae6f0c79c5bc200
+  SnapKit: fe8a619752f3f27075cc9a90244d75c6c3f27e2a
 
 PODFILE CHECKSUM: fbb4c1ccef26f44908c6add1513fff562052991a
 

+ 22 - 0
Example/Pods/Kingfisher/LICENSE

@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2018 Wei Wang
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+

+ 116 - 0
Example/Pods/Kingfisher/README.md

@@ -0,0 +1,116 @@
+<p align="center">
+
+<img src="https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/logo.png" alt="Kingfisher" title="Kingfisher" width="557"/>
+
+</p>
+
+<p align="center">
+<a href="https://travis-ci.org/onevcat/Kingfisher"><img src="https://img.shields.io/travis/onevcat/Kingfisher/master.svg"></a>
+<a href="https://github.com/Carthage/Carthage/"><img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat"></a>
+<a href="http://onevcat.github.io/Kingfisher/"><img src="https://img.shields.io/cocoapods/v/Kingfisher.svg?style=flat"></a>
+<a href="https://raw.githubusercontent.com/onevcat/Kingfisher/master/LICENSE"><img src="https://img.shields.io/cocoapods/l/Kingfisher.svg?style=flat"></a>
+<a href="http://onevcat.github.io/Kingfisher/"><img src="https://img.shields.io/cocoapods/p/Kingfisher.svg?style=flat"></a>
+<a href="https://codebeat.co/projects/github-com-onevcat-kingfisher"><img alt="codebeat badge" src="https://codebeat.co/assets/svg/badges/A-398b39-669406e9e1b136187b91af587d4092b0160370f271f66a651f444b990c2730e9.svg" /></a>
+<a href="#backers" alt="sponsors on Open Collective"><img src="https://opencollective.com/Kingfisher/backers/badge.svg" /></a>
+<a href="#sponsors" alt="Sponsors on Open Collective"><img src="https://opencollective.com/Kingfisher/sponsors/badge.svg" /></a>
+</p>
+
+Kingfisher is a lightweight, pure-Swift library for downloading and caching images from the web. This project is heavily inspired by the popular [SDWebImage](https://github.com/rs/SDWebImage). It provides you a chance to use a pure-Swift alternative in your next app.
+
+## Features
+
+- [x] Asynchronous image downloading and caching.
+- [x] `URLSession`-based networking. Basic image processors and filters supplied.
+- [x] Multiple-layer cache for both memory and disk.
+- [x] Cancelable downloading and processing tasks to improve performance.
+- [x] Independent components. Use the downloader or caching system separately as you need.
+- [x] Prefetching images and showing them from cache later when necessary.
+- [x] Extensions for `UIImageView`, `NSImage` and `UIButton` to directly set an image from a URL.
+- [x] Built-in transition animation when setting images.
+- [x] Customizable placeholder while loading images.
+- [x] Extensible image processing and image format support.
+
+The simplest use-case is setting an image to an image view with the `UIImageView` extension:
+
+```swift
+let url = URL(string: "url_of_your_image")
+imageView.kf.setImage(with: url)
+```
+
+Kingfisher will download the image from `url`, send it to both the memory cache and the disk cache, and display it in `imageView`. When you use the same code later, the image will be retrieved from cache and shown immediately.
+
+For more examples of using Kingfisher, take a look at the [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet).
+
+## Requirements
+
+- iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
+- Swift 4 (Kingfisher 4.x), Swift 3 (Kingfisher 3.x)
+
+Main development of Kingfisher is based on Swift 4. Only critical bug fixes will be applied to Kingfisher 3.x.
+
+- Kingfisher 4.0 Migration - Kingfisher 3.x should be source compatible to Kingfisher 4. The reason for a major update is that we need to specify the Swift version explicitly for Xcode. All deprecated methods in Kingfisher 3 has been removed, so please ensure you have no warning left before you migrate from Kingfisher 3 to Kingfisher 4. If you have any trouble in migrating, please open an issue to discuss.
+- [Kingfisher 3.0 Migration Guide](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-3.0-Migration-Guide) - If you are upgrading to Kingfisher 3.x from an earlier version, please read this for more information.
+
+## Next Steps
+
+We prepared a [wiki page](https://github.com/onevcat/Kingfisher/wiki). You can find tons of useful things there.
+
+* [Installation Guide](https://github.com/onevcat/Kingfisher/wiki/Installation-Guide) - Follow it to integrate Kingfisher into your project.
+* [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet)- Curious about what Kingfisher could do and how would it look like when used in your project? See this page for useful code snippets. If you are already familiar with Kingfisher, you could also learn new tricks to improve the way you use Kingfisher! 
+* [API Reference](http://onevcat.github.io/Kingfisher/) - Lastly, please remember to read the full whenever you may need a more detailed reference.
+
+## Other
+
+### Future of Kingfisher
+
+I want to keep Kingfisher lightweight. This framework will focus on providing a simple solution for downloading and caching images. This doesn’t mean the framework can’t be improved. Kingfisher is far from perfect, so necessary and useful updates will be made to make it better.
+
+### Developments and Tests
+
+Any contributing and pull requests are warmly welcome. However, before you plan to implement some features or try to fix an uncertain issue, it is recommended to open a discussion first. 
+
+The test images are contained in another project to keep this project repo fast and slim. You could run `./setup.sh` in the root folder of Kingfisher to clone the test images when you need to run the tests target. It would be appreciated if your pull requests could build and with all tests green. :)
+
+### About the logo
+
+The logo of Kingfisher is inspired by [Tangram (七巧板)](http://en.wikipedia.org/wiki/Tangram), a dissection puzzle consisting of seven flat shapes from China. I believe she's a kingfisher bird instead of a swift, but someone insists that she is a pigeon. I guess I should give her a name. Hi, guys, do you have any suggestions?
+
+### Contact
+
+Follow and contact me on [Twitter](http://twitter.com/onevcat) or [Sina Weibo](http://weibo.com/onevcat). If you find an issue, just [open a ticket](https://github.com/onevcat/Kingfisher/issues/new). Pull requests are warmly welcome as well.
+
+## Contributors
+
+This project exists thanks to all the people who contribute. [[Contribute]](https://github.com/onevcat/Kingfisher/blob/master/CONTRIBUTING.md).
+<a href="https://github.com/onevcat/Kingfisher/graphs/contributors"><img src="https://opencollective.com/kingfisher/contributors.svg?width=890" /></a>
+
+
+## Backers
+
+Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/kingfisher#backer)]
+
+<a href="https://opencollective.com/kingfisher#backers" target="_blank"><img src="https://opencollective.com/kingfisher/backers.svg?width=890"></a>
+
+
+## Sponsors
+
+Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/kingfisher#sponsor)]
+
+<a href="https://opencollective.com/kingfisher/sponsor/0/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/0/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/1/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/1/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/2/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/2/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/3/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/3/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/4/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/4/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/5/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/5/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/6/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/6/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/7/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/7/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/8/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/8/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/9/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/9/avatar.svg"></a>
+
+
+
+### License
+
+Kingfisher is released under the MIT license. See LICENSE for details.
+
+

+ 503 - 0
Example/Pods/Kingfisher/Sources/AnimatedImageView.swift

@@ -0,0 +1,503 @@
+//
+//  AnimatableImageView.swift
+//  Kingfisher
+//
+//  Created by bl4ckra1sond3tre on 4/22/16.
+//
+//  The AnimatableImageView, AnimatedFrame and Animator is a modified version of 
+//  some classes from kaishin's Gifu project (https://github.com/kaishin/Gifu)
+//
+//  The MIT License (MIT)
+//
+//  Copyright (c) 2018 Reda Lemeden.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy of
+//  this software and associated documentation files (the "Software"), to deal in
+//  the Software without restriction, including without limitation the rights to
+//  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+//  the Software, and to permit persons to whom the Software is furnished to do so,
+//  subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+//  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+//  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+//  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//  The name and characters used in the demo of this software are property of their
+//  respective owners.
+
+import UIKit
+import ImageIO
+
+/// Protocol of `AnimatedImageView`.
+public protocol AnimatedImageViewDelegate: AnyObject {
+    /**
+     Called after the animatedImageView has finished each animation loop.
+
+     - parameter imageView: The animatedImageView that is being animated.
+     - parameter count: The looped count.
+     */
+    func animatedImageView(_ imageView: AnimatedImageView, didPlayAnimationLoops count: UInt)
+
+    /**
+     Called after the animatedImageView has reached the max repeat count.
+
+     - parameter imageView: The animatedImageView that is being animated.
+     */
+    func animatedImageViewDidFinishAnimating(_ imageView: AnimatedImageView)
+}
+
+extension AnimatedImageViewDelegate {
+    public func animatedImageView(_ imageView: AnimatedImageView, didPlayAnimationLoops count: UInt) {}
+    public func animatedImageViewDidFinishAnimating(_ imageView: AnimatedImageView) {}
+}
+
+/// `AnimatedImageView` is a subclass of `UIImageView` for displaying animated image.
+open class AnimatedImageView: UIImageView {
+    
+    /// Proxy object for prevending a reference cycle between the CADDisplayLink and AnimatedImageView.
+    class TargetProxy {
+        private weak var target: AnimatedImageView?
+        
+        init(target: AnimatedImageView) {
+            self.target = target
+        }
+        
+        @objc func onScreenUpdate() {
+            target?.updateFrame()
+        }
+    }
+
+    /// Enumeration that specifies repeat count of GIF
+    public enum RepeatCount: Equatable {
+        case once
+        case finite(count: UInt)
+        case infinite
+
+        public static func ==(lhs: RepeatCount, rhs: RepeatCount) -> Bool {
+            switch (lhs, rhs) {
+            case let (.finite(l), .finite(r)):
+                return l == r
+            case (.once, .once),
+                 (.infinite, .infinite):
+                return true
+            case (.once, .finite(let count)),
+                 (.finite(let count), .once):
+                return count == 1
+            case (.once, _),
+                 (.infinite, _),
+                 (.finite, _):
+                return false
+            }
+        }
+    }
+    
+    // MARK: - Public property
+    /// Whether automatically play the animation when the view become visible. Default is true.
+    public var autoPlayAnimatedImage = true
+    
+    /// The size of the frame cache.
+    public var framePreloadCount = 10
+    
+    /// Specifies whether the GIF frames should be pre-scaled to save memory. Default is true.
+    public var needsPrescaling = true
+    
+    /// The animation timer's run loop mode. Default is `NSRunLoopCommonModes`. Set this property to `NSDefaultRunLoopMode` will make the animation pause during UIScrollView scrolling.
+    #if swift(>=4.2)
+    public var runLoopMode = RunLoop.Mode.common {
+        willSet {
+            if runLoopMode == newValue {
+                return
+            } else {
+                stopAnimating()
+                displayLink.remove(from: .main, forMode: runLoopMode)
+                displayLink.add(to: .main, forMode: newValue)
+                startAnimating()
+            }
+        }
+    }
+    #else
+    public var runLoopMode = RunLoopMode.commonModes {
+        willSet {
+            if runLoopMode == newValue {
+                return
+            } else {
+                stopAnimating()
+                displayLink.remove(from: .main, forMode: runLoopMode)
+                displayLink.add(to: .main, forMode: newValue)
+                startAnimating()
+            }
+        }
+    }
+    #endif
+
+    /// The repeat count.
+    public var repeatCount = RepeatCount.infinite {
+        didSet {
+            if oldValue != repeatCount {
+                reset()
+                setNeedsDisplay()
+                layer.setNeedsDisplay()
+            }
+        }
+    }
+
+    /// Delegate of this `AnimatedImageView` object. See `AnimatedImageViewDelegate` protocol for more.
+    public weak var delegate: AnimatedImageViewDelegate?
+    
+    // MARK: - Private property
+    /// `Animator` instance that holds the frames of a specific image in memory.
+    private var animator: Animator?
+    
+    /// A flag to avoid invalidating the displayLink on deinit if it was never created, because displayLink is so lazy. :D
+    private var isDisplayLinkInitialized: Bool = false
+    
+    /// A display link that keeps calling the `updateFrame` method on every screen refresh.
+    private lazy var displayLink: CADisplayLink = {
+        self.isDisplayLinkInitialized = true
+        let displayLink = CADisplayLink(target: TargetProxy(target: self), selector: #selector(TargetProxy.onScreenUpdate))
+        displayLink.add(to: .main, forMode: self.runLoopMode)
+        displayLink.isPaused = true
+        return displayLink
+    }()
+    
+    // MARK: - Override
+    override open var image: Image? {
+        didSet {
+            if image != oldValue {
+                reset()
+            }
+            setNeedsDisplay()
+            layer.setNeedsDisplay()
+        }
+    }
+    
+    deinit {
+        if isDisplayLinkInitialized {
+            displayLink.invalidate()
+        }
+    }
+    
+    override open var isAnimating: Bool {
+        if isDisplayLinkInitialized {
+            return !displayLink.isPaused
+        } else {
+            return super.isAnimating
+        }
+    }
+    
+    /// Starts the animation.
+    override open func startAnimating() {
+        if self.isAnimating {
+            return
+        } else {
+            if animator?.isReachMaxRepeatCount ?? false {
+                return
+            }
+
+            displayLink.isPaused = false
+        }
+    }
+    
+    /// Stops the animation.
+    override open func stopAnimating() {
+        super.stopAnimating()
+        if isDisplayLinkInitialized {
+            displayLink.isPaused = true
+        }
+    }
+    
+    override open func display(_ layer: CALayer) {
+        if let currentFrame = animator?.currentFrame {
+            layer.contents = currentFrame.cgImage
+        } else {
+            layer.contents = image?.cgImage
+        }
+    }
+    
+    override open func didMoveToWindow() {
+        super.didMoveToWindow()
+        didMove()
+    }
+    
+    override open func didMoveToSuperview() {
+        super.didMoveToSuperview()
+        didMove()
+    }
+
+    // This is for back compatibility that using regular UIImageView to show animated image.
+    override func shouldPreloadAllAnimation() -> Bool {
+        return false
+    }
+
+    // MARK: - Private method
+    /// Reset the animator.
+    private func reset() {
+        animator = nil
+        if let imageSource = image?.kf.imageSource?.imageRef {
+            animator = Animator(imageSource: imageSource,
+                                contentMode: contentMode,
+                                size: bounds.size,
+                                framePreloadCount: framePreloadCount,
+                                repeatCount: repeatCount)
+            animator?.delegate = self
+            animator?.needsPrescaling = needsPrescaling
+            animator?.prepareFramesAsynchronously()
+        }
+        didMove()
+    }
+    
+    private func didMove() {
+        if autoPlayAnimatedImage && animator != nil {
+            if let _ = superview, let _ = window {
+                startAnimating()
+            } else {
+                stopAnimating()
+            }
+        }
+    }
+    
+    /// Update the current frame with the displayLink duration.
+    private func updateFrame() {
+        let duration: CFTimeInterval
+
+        // CA based display link is opt-out from ProMotion by default.
+        // So the duration and its FPS might not match. 
+        // See [#718](https://github.com/onevcat/Kingfisher/issues/718)
+        if #available(iOS 10.0, tvOS 10.0, *) {
+            // By setting CADisableMinimumFrameDuration to YES in Info.plist may 
+            // cause the preferredFramesPerSecond being 0
+            if displayLink.preferredFramesPerSecond == 0 {
+                duration = displayLink.duration
+            } else {
+                // Some devices (like iPad Pro 10.5) will have a different FPS.
+                duration = 1.0 / Double(displayLink.preferredFramesPerSecond)
+            }
+        } else {
+            duration = displayLink.duration
+        }
+    
+        if animator?.updateCurrentFrame(duration: duration) ?? false {
+            layer.setNeedsDisplay()
+
+            if animator?.isReachMaxRepeatCount ?? false {
+                stopAnimating()
+                delegate?.animatedImageViewDidFinishAnimating(self)
+            }
+        }
+    }
+}
+
+extension AnimatedImageView: AnimatorDelegate {
+    func animator(_ animator: Animator, didPlayAnimationLoops count: UInt) {
+        delegate?.animatedImageView(self, didPlayAnimationLoops: count)
+    }
+}
+
+/// Keeps a reference to an `Image` instance and its duration as a GIF frame.
+struct AnimatedFrame {
+    var image: Image?
+    let duration: TimeInterval
+    
+    static let null: AnimatedFrame = AnimatedFrame(image: .none, duration: 0.0)
+}
+
+protocol AnimatorDelegate: AnyObject {
+    func animator(_ animator: Animator, didPlayAnimationLoops count: UInt)
+}
+
+// MARK: - Animator
+class Animator {
+    // MARK: Private property
+    fileprivate let size: CGSize
+    fileprivate let maxFrameCount: Int
+    fileprivate let imageSource: CGImageSource
+    fileprivate let maxRepeatCount: AnimatedImageView.RepeatCount
+    
+    fileprivate var animatedFrames = [AnimatedFrame]()
+    fileprivate let maxTimeStep: TimeInterval = 1.0
+    fileprivate var frameCount = 0
+    fileprivate var currentFrameIndex = 0
+    fileprivate var currentFrameIndexInBuffer = 0
+    fileprivate var currentPreloadIndex = 0
+    fileprivate var timeSinceLastFrameChange: TimeInterval = 0.0
+    fileprivate var needsPrescaling = true
+    fileprivate var currentRepeatCount: UInt = 0
+    fileprivate weak var delegate: AnimatorDelegate?
+    
+    /// Loop count of animated image.
+    private var loopCount = 0
+    
+    var currentFrame: UIImage? {
+        return frame(at: currentFrameIndexInBuffer)
+    }
+
+    var isReachMaxRepeatCount: Bool {
+        switch maxRepeatCount {
+        case .once:
+            return currentRepeatCount >= 1
+        case .finite(let maxCount):
+            return currentRepeatCount >= maxCount
+        case .infinite:
+            return false
+        }
+    }
+    
+    var contentMode = UIView.ContentMode.scaleToFill
+    
+    private lazy var preloadQueue: DispatchQueue = {
+        return DispatchQueue(label: "com.onevcat.Kingfisher.Animator.preloadQueue")
+    }()
+    
+    /**
+     Init an animator with image source reference.
+     
+     - parameter imageSource: The reference of animated image.
+     - parameter contentMode: Content mode of AnimatedImageView.
+     - parameter size: Size of AnimatedImageView.
+     - parameter framePreloadCount: Frame cache size.
+     
+     - returns: The animator object.
+     */
+    init(imageSource source: CGImageSource,
+         contentMode mode: UIView.ContentMode,
+         size: CGSize,
+         framePreloadCount count: Int,
+         repeatCount: AnimatedImageView.RepeatCount) {
+        self.imageSource = source
+        self.contentMode = mode
+        self.size = size
+        self.maxFrameCount = count
+        self.maxRepeatCount = repeatCount
+    }
+    
+    func frame(at index: Int) -> Image? {
+        return animatedFrames[safe: index]?.image
+    }
+    
+    func prepareFramesAsynchronously() {
+        preloadQueue.async { [weak self] in
+            self?.prepareFrames()
+        }
+    }
+    
+    private func prepareFrames() {
+        frameCount = CGImageSourceGetCount(imageSource)
+        
+        if let properties = CGImageSourceCopyProperties(imageSource, nil),
+            let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary,
+            let loopCount = gifInfo[kCGImagePropertyGIFLoopCount as String] as? Int
+        {
+            self.loopCount = loopCount
+        }
+        
+        let frameToProcess = min(frameCount, maxFrameCount)
+        animatedFrames.reserveCapacity(frameToProcess)
+        animatedFrames = (0..<frameToProcess).reduce([]) { $0 + pure(prepareFrame(at: $1))}
+        currentPreloadIndex = (frameToProcess + 1) % frameCount - 1
+    }
+    
+    private func prepareFrame(at index: Int) -> AnimatedFrame {
+        
+        guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else {
+            return AnimatedFrame.null
+        }
+        
+        let defaultGIFFrameDuration = 0.100
+        let frameDuration = imageSource.kf.gifProperties(at: index).map {
+            gifInfo -> Double in
+            
+            let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double?
+            let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as Double?
+            let duration = unclampedDelayTime ?? delayTime ?? 0.0
+            
+            /**
+             http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp
+             Many annoying ads specify a 0 duration to make an image flash as quickly as
+             possible. We follow Safari and Firefox's behavior and use a duration of 100 ms
+             for any frames that specify a duration of <= 10 ms.
+             See <rdar://problem/7689300> and <http://webkit.org/b/36082> for more information.
+             
+             See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser.
+             */
+            return duration > 0.011 ? duration : defaultGIFFrameDuration
+        } ?? defaultGIFFrameDuration
+        
+        let image = Image(cgImage: imageRef)
+        let scaledImage: Image?
+        
+        if needsPrescaling {
+            scaledImage = image.kf.resize(to: size, for: contentMode)
+        } else {
+            scaledImage = image
+        }
+        
+        return AnimatedFrame(image: scaledImage, duration: frameDuration)
+    }
+    
+    /**
+     Updates the current frame if necessary using the frame timer and the duration of each frame in `animatedFrames`.
+     */
+    func updateCurrentFrame(duration: CFTimeInterval) -> Bool {
+        timeSinceLastFrameChange += min(maxTimeStep, duration)
+        guard let frameDuration = animatedFrames[safe: currentFrameIndexInBuffer]?.duration, frameDuration <= timeSinceLastFrameChange else {
+            return false
+        }
+        
+        timeSinceLastFrameChange -= frameDuration
+        
+        let lastFrameIndex = currentFrameIndexInBuffer
+        currentFrameIndexInBuffer += 1
+        currentFrameIndexInBuffer = currentFrameIndexInBuffer % animatedFrames.count
+        
+        if animatedFrames.count < frameCount {
+            preloadFrameAsynchronously(at: lastFrameIndex)
+        }
+        
+        currentFrameIndex += 1
+        
+        if currentFrameIndex == frameCount {
+            currentFrameIndex = 0
+            currentRepeatCount += 1
+
+            delegate?.animator(self, didPlayAnimationLoops: currentRepeatCount)
+        }
+
+        return true
+    }
+    
+    private func preloadFrameAsynchronously(at index: Int) {
+        preloadQueue.async { [weak self] in
+            self?.preloadFrame(at: index)
+        }
+    }
+    
+    private func preloadFrame(at index: Int) {
+        animatedFrames[index] = prepareFrame(at: currentPreloadIndex)
+        currentPreloadIndex += 1
+        currentPreloadIndex = currentPreloadIndex % frameCount
+    }
+}
+
+extension CGImageSource: KingfisherCompatible { }
+extension Kingfisher where Base: CGImageSource {
+    func gifProperties(at index: Int) -> [String: Double]? {
+        let properties = CGImageSourceCopyPropertiesAtIndex(base, index, nil) as Dictionary?
+        return properties?[kCGImagePropertyGIFDictionary] as? [String: Double]
+    }
+}
+
+extension Array {
+    fileprivate subscript(safe index: Int) -> Element? {
+        return indices ~= index ? self[index] : nil
+    }
+}
+
+private func pure<T>(_ value: T) -> [T] {
+    return [value]
+}

+ 34 - 0
Example/Pods/Kingfisher/Sources/Box.swift

@@ -0,0 +1,34 @@
+//
+//  Box.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2018/3/17.
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+class Box<T> {
+    let value: T
+    
+    init(_ value: T) {
+        self.value = value
+    }
+}

+ 87 - 0
Example/Pods/Kingfisher/Sources/CacheSerializer.swift

@@ -0,0 +1,87 @@
+//
+//  CacheSerializer.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2016/09/02.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+/// An `CacheSerializer` would be used to convert some data to an image object for 
+/// retrieving from disk cache and vice versa for storing to disk cache.
+public protocol CacheSerializer {
+    
+    /// Get the serialized data from a provided image
+    /// and optional original data for caching to disk.
+    ///
+    ///
+    /// - parameter image:    The image needed to be serialized.
+    /// - parameter original: The original data which is just downloaded. 
+    ///                       If the image is retrieved from cache instead of
+    ///                       downloaded, it will be `nil`.
+    ///
+    /// - returns: A data which will be stored to cache, or `nil` when no valid
+    ///            data could be serialized.
+    func data(with image: Image, original: Data?) -> Data?
+    
+    /// Get an image deserialized from provided data.
+    ///
+    /// - parameter data:    The data from which an image should be deserialized.
+    /// - parameter options: Options for deserialization.
+    ///
+    /// - returns: An image deserialized or `nil` when no valid image 
+    ///            could be deserialized.
+    func image(with data: Data, options: KingfisherOptionsInfo?) -> Image?
+}
+
+
+/// `DefaultCacheSerializer` is a basic `CacheSerializer` used in default cache of
+/// Kingfisher. It could serialize and deserialize PNG, JEPG and GIF images. For 
+/// image other than these formats, a normalized `pngRepresentation` will be used.
+public struct DefaultCacheSerializer: CacheSerializer {
+    
+    public static let `default` = DefaultCacheSerializer()
+    private init() {}
+    
+    public func data(with image: Image, original: Data?) -> Data? {
+        let imageFormat = original?.kf.imageFormat ?? .unknown
+
+        let data: Data?
+        switch imageFormat {
+        case .PNG: data = image.kf.pngRepresentation()
+        case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0)
+        case .GIF: data = image.kf.gifRepresentation()
+        case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation()
+        }
+
+        return data
+    }
+    
+    public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? {
+        let options = options ?? KingfisherEmptyOptionsInfo
+        return Kingfisher<Image>.image(
+            data: data,
+            scale: options.scaleFactor,
+            preloadAllAnimationData: options.preloadAllAnimationData,
+            onlyFirstFrame: options.onlyLoadFirstFrame)
+    }
+}

+ 144 - 0
Example/Pods/Kingfisher/Sources/Filter.swift

@@ -0,0 +1,144 @@
+//
+//  Filter.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2016/08/31.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+
+
+import CoreImage
+import Accelerate
+
+// Reuse the same CI Context for all CI drawing.
+private let ciContext = CIContext(options: nil)
+
+/// Transformer method which will be used in to provide a `Filter`.
+public typealias Transformer = (CIImage) -> CIImage?
+
+/// Supply a filter to create an `ImageProcessor`.
+public protocol CIImageProcessor: ImageProcessor {
+    var filter: Filter { get }
+}
+
+extension CIImageProcessor {
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.apply(filter)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Wrapper for a `Transformer` of CIImage filters.
+public struct Filter {
+    
+    let transform: Transformer
+
+    public init(transform: @escaping Transformer) {
+        self.transform = transform
+    }
+    
+    /// Tint filter which will apply a tint color to images.
+    public static var tint: (Color) -> Filter = {
+        color in
+        Filter(transform: { input in
+            let colorFilter = CIFilter(name: "CIConstantColorGenerator")!
+            colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey)
+            
+            let colorImage = colorFilter.outputImage
+            let filter = CIFilter(name: "CISourceOverCompositing")!
+            filter.setValue(colorImage, forKey: kCIInputImageKey)
+            filter.setValue(input, forKey: kCIInputBackgroundImageKey)
+            #if swift(>=4.0)
+            return filter.outputImage?.cropped(to: input.extent)
+            #else
+            return filter.outputImage?.cropping(to: input.extent)
+            #endif
+        })
+    }
+    
+    public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat)
+    
+    /// Color control filter which will apply color control change to images.
+    public static var colorControl: (ColorElement) -> Filter = { arg -> Filter in
+        let (brightness, contrast, saturation, inputEV) = arg
+        return Filter(transform: { input in
+            let paramsColor = [kCIInputBrightnessKey: brightness,
+                               kCIInputContrastKey: contrast,
+                               kCIInputSaturationKey: saturation]
+            
+            let paramsExposure = [kCIInputEVKey: inputEV]
+            #if swift(>=4.0)
+            let blackAndWhite = input.applyingFilter("CIColorControls", parameters: paramsColor)
+            return blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure)
+            #else
+            let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor)
+            return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure)
+            #endif
+        })
+    }
+}
+
+// MARK: - Deprecated
+extension Filter {
+    @available(*, deprecated, message: "Use init(transform:) instead.", renamed: "init(transform:)")
+    public init(tranform: @escaping Transformer) {
+        self.transform = tranform
+    }
+}
+
+extension Kingfisher where Base: Image {
+    /// Apply a `Filter` containing `CIImage` transformer to `self`.
+    ///
+    /// - parameter filter: The filter used to transform `self`.
+    ///
+    /// - returns: A transformed image by input `Filter`.
+    ///
+    /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned.
+    public func apply(_ filter: Filter) -> Image {
+        
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Tint image only works for CG-based image.")
+            return base
+        }
+        
+        let inputImage = CIImage(cgImage: cgImage)
+        guard let outputImage = filter.transform(inputImage) else {
+            return base
+        }
+        
+        guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else {
+            assertionFailure("[Kingfisher] Can not make an tint image within context.")
+            return base
+        }
+        
+        #if os(macOS)
+            return fixedForRetinaPixel(cgImage: result, to: size)
+        #else
+            return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation)
+        #endif
+    }
+
+}

+ 96 - 0
Example/Pods/Kingfisher/Sources/FormatIndicatedCacheSerializer.swift

@@ -0,0 +1,96 @@
+//
+//  RequestModifier.swift
+//  Kingfisher
+//
+//  Created by Junyu Kuang on 5/28/17.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+/// `FormatIndicatedCacheSerializer` let you indicate an image format for serialized caches.
+///
+/// It could serialize and deserialize PNG, JEPG and GIF images. For
+/// image other than these formats, a normalized `pngRepresentation` will be used.
+///
+/// Example:
+/// ````
+/// private let profileImageSize = CGSize(width: 44, height: 44)
+///
+/// private let imageProcessor = RoundCornerImageProcessor(
+///     cornerRadius: profileImageSize.width / 2, targetSize: profileImageSize)
+///
+/// private let optionsInfo: KingfisherOptionsInfo = [
+///     .cacheSerializer(FormatIndicatedCacheSerializer.png), 
+///     .backgroundDecode, .processor(imageProcessor), .scaleFactor(UIScreen.main.scale)]
+///
+/// extension UIImageView {
+///    func setProfileImage(with url: URL) {
+///        // Image will always cached as PNG format to preserve alpha channel for round rect.
+///        _ = kf.setImage(with: url, options: optionsInfo)
+///    }
+///}
+/// ````
+public struct FormatIndicatedCacheSerializer: CacheSerializer {
+    
+    public static let png = FormatIndicatedCacheSerializer(imageFormat: .PNG)
+    public static let jpeg = FormatIndicatedCacheSerializer(imageFormat: .JPEG)
+    public static let gif = FormatIndicatedCacheSerializer(imageFormat: .GIF)
+    
+    /// The indicated image format.
+    private let imageFormat: ImageFormat
+    
+    public func data(with image: Image, original: Data?) -> Data? {
+        
+        func imageData(withFormat imageFormat: ImageFormat) -> Data? {
+            switch imageFormat {
+            case .PNG: return image.kf.pngRepresentation()
+            case .JPEG: return image.kf.jpegRepresentation(compressionQuality: 1.0)
+            case .GIF: return image.kf.gifRepresentation()
+            case .unknown: return nil
+            }
+        }
+        
+        // generate data with indicated image format
+        if let data = imageData(withFormat: imageFormat) {
+            return data
+        }
+        
+        let originalFormat = original?.kf.imageFormat ?? .unknown
+        
+        // generate data with original image's format
+        if originalFormat != imageFormat, let data = imageData(withFormat: originalFormat) {
+            return data
+        }
+        
+        return original ?? image.kf.normalized.kf.pngRepresentation()
+    }
+    
+    /// Same implementation as `DefaultCacheSerializer`.
+    public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? {
+        let options = options ?? KingfisherEmptyOptionsInfo
+        return Kingfisher<Image>.image(
+            data: data,
+            scale: options.scaleFactor,
+            preloadAllAnimationData: options.preloadAllAnimationData,
+            onlyFirstFrame: options.onlyLoadFirstFrame)
+    }
+}

+ 1045 - 0
Example/Pods/Kingfisher/Sources/Image.swift

@@ -0,0 +1,1045 @@
+//
+//  Image.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 16/1/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+
+#if os(macOS)
+import AppKit
+private var imagesKey: Void?
+private var durationKey: Void?
+#else
+import UIKit
+import MobileCoreServices
+private var imageSourceKey: Void?
+#endif
+private var animatedImageDataKey: Void?
+
+import ImageIO
+import CoreGraphics
+
+#if !os(watchOS)
+import Accelerate
+import CoreImage
+#endif
+
+// MARK: - Image Properties
+extension Kingfisher where Base: Image {
+    fileprivate(set) var animatedImageData: Data? {
+        get {
+            return objc_getAssociatedObject(base, &animatedImageDataKey) as? Data
+        }
+        set {
+            objc_setAssociatedObject(base, &animatedImageDataKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    #if os(macOS)
+    var cgImage: CGImage? {
+        return base.cgImage(forProposedRect: nil, context: nil, hints: nil)
+    }
+    
+    var scale: CGFloat {
+        return 1.0
+    }
+    
+    fileprivate(set) var images: [Image]? {
+        get {
+            return objc_getAssociatedObject(base, &imagesKey) as? [Image]
+        }
+        set {
+            objc_setAssociatedObject(base, &imagesKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    fileprivate(set) var duration: TimeInterval {
+        get {
+            return objc_getAssociatedObject(base, &durationKey) as? TimeInterval ?? 0.0
+        }
+        set {
+            objc_setAssociatedObject(base, &durationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    var size: CGSize {
+        return base.representations.reduce(CGSize.zero, { size, rep in
+            return CGSize(width: max(size.width, CGFloat(rep.pixelsWide)), height: max(size.height, CGFloat(rep.pixelsHigh)))
+        })
+    }
+    
+    #else
+    var cgImage: CGImage? {
+        return base.cgImage
+    }
+    
+    var scale: CGFloat {
+        return base.scale
+    }
+    
+    var images: [Image]? {
+        return base.images
+    }
+    
+    var duration: TimeInterval {
+        return base.duration
+    }
+    
+    fileprivate(set) var imageSource: ImageSource? {
+        get {
+            return objc_getAssociatedObject(base, &imageSourceKey) as? ImageSource
+        }
+        set {
+            objc_setAssociatedObject(base, &imageSourceKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    var size: CGSize {
+        return base.size
+    }
+    #endif
+}
+
+// MARK: - Image Conversion
+extension Kingfisher where Base: Image {
+    #if os(macOS)
+    static func image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image {
+        return Image(cgImage: cgImage, size: CGSize.zero)
+    }
+    
+    /**
+     Normalize the image. This method does nothing in OS X.
+     
+     - returns: The image itself.
+     */
+    public var normalized: Image {
+        return base
+    }
+    
+    static func animated(with images: [Image], forDuration forDurationduration: TimeInterval) -> Image? {
+        return nil
+    }
+    #else
+    static func image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image {
+        if let refImage = refImage {
+            return Image(cgImage: cgImage, scale: scale, orientation: refImage.imageOrientation)
+        } else {
+            return Image(cgImage: cgImage, scale: scale, orientation: .up)
+        }
+    }
+    
+    /**
+     Normalize the image. This method will try to redraw an image with orientation and scale considered.
+     
+     - returns: The normalized image with orientation set to up and correct scale.
+     */
+    public var normalized: Image {
+        // prevent animated image (GIF) lose it's images
+        guard images == nil else { return base }
+        // No need to do anything if already up
+        guard base.imageOrientation != .up else { return base }
+    
+        return draw(cgImage: nil, to: size) {
+            base.draw(in: CGRect(origin: CGPoint.zero, size: size))
+        }
+    }
+    
+    static func animated(with images: [Image], forDuration duration: TimeInterval) -> Image? {
+        return .animatedImage(with: images, duration: duration)
+    }
+    #endif
+}
+
+// MARK: - Image Representation
+extension Kingfisher where Base: Image {
+    // MARK: - PNG
+    public func pngRepresentation() -> Data? {
+        #if os(macOS)
+            guard let cgimage = cgImage else {
+                return nil
+            }
+            let rep = NSBitmapImageRep(cgImage: cgimage)
+            return rep.representation(using: .png, properties: [:])
+        #else
+            #if swift(>=4.2)
+            return base.pngData()
+            #else
+            return UIImagePNGRepresentation(base)
+            #endif
+        #endif
+    }
+    
+    // MARK: - JPEG
+    public func jpegRepresentation(compressionQuality: CGFloat) -> Data? {
+        #if os(macOS)
+            guard let cgImage = cgImage else {
+                return nil
+            }
+            let rep = NSBitmapImageRep(cgImage: cgImage)
+            return rep.representation(using:.jpeg, properties: [.compressionFactor: compressionQuality])
+        #else
+            #if swift(>=4.2)
+            return base.jpegData(compressionQuality: compressionQuality)
+            #else
+            return UIImageJPEGRepresentation(base, compressionQuality)
+            #endif
+        #endif
+    }
+    
+    // MARK: - GIF
+    public func gifRepresentation() -> Data? {
+        return animatedImageData
+    }
+}
+
+// MARK: - Create images from data
+extension Kingfisher where Base: Image {
+    public static func animated(with data: Data, scale: CGFloat = 1.0, duration: TimeInterval = 0.0, preloadAll: Bool, onlyFirstFrame: Bool = false) -> Image? {
+        
+        func decode(from imageSource: CGImageSource, for options: NSDictionary) -> ([Image], TimeInterval)? {
+            
+            //Calculates frame duration for a gif frame out of the kCGImagePropertyGIFDictionary dictionary
+            func frameDuration(from gifInfo: NSDictionary?) -> Double {
+                let gifDefaultFrameDuration = 0.100
+                
+                guard let gifInfo = gifInfo else {
+                    return gifDefaultFrameDuration
+                }
+                
+                let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as? NSNumber
+                let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber
+                let duration = unclampedDelayTime ?? delayTime
+                
+                guard let frameDuration = duration else { return gifDefaultFrameDuration }
+                
+                return frameDuration.doubleValue > 0.011 ? frameDuration.doubleValue : gifDefaultFrameDuration
+            }
+            
+            let frameCount = CGImageSourceGetCount(imageSource)
+            var images = [Image]()
+            var gifDuration = 0.0
+            for i in 0 ..< frameCount {
+                
+                guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, options) else {
+                    return nil
+                }
+
+                if frameCount == 1 {
+                    // Single frame
+                    gifDuration = Double.infinity
+                } else {
+                    
+                    // Animated GIF
+                    guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil) else {
+                        return nil
+                    }
+
+                    let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary
+                    gifDuration += frameDuration(from: gifInfo)
+                }
+                
+                images.append(Kingfisher<Image>.image(cgImage: imageRef, scale: scale, refImage: nil))
+                
+                if onlyFirstFrame { break }
+            }
+            
+            return (images, gifDuration)
+        }
+        
+        // Start of kf.animatedImageWithGIFData
+        let options: NSDictionary = [kCGImageSourceShouldCache as String: true, kCGImageSourceTypeIdentifierHint as String: kUTTypeGIF]
+        guard let imageSource = CGImageSourceCreateWithData(data as CFData, options) else {
+            return nil
+        }
+        
+        #if os(macOS)
+            guard let (images, gifDuration) = decode(from: imageSource, for: options) else {
+                return nil
+            }
+            let image: Image?
+            if onlyFirstFrame {
+                image = images.first
+            } else {
+                image = Image(data: data)
+                image?.kf.images = images
+                image?.kf.duration = gifDuration
+            }
+            image?.kf.animatedImageData = data
+            return image
+        #else
+            
+            let image: Image?
+            if preloadAll || onlyFirstFrame {
+                guard let (images, gifDuration) = decode(from: imageSource, for: options) else { return nil }
+                image = onlyFirstFrame ? images.first : Kingfisher<Image>.animated(with: images, forDuration: duration <= 0.0 ? gifDuration : duration)
+            } else {
+                image = Image(data: data, scale: scale)
+                image?.kf.imageSource = ImageSource(ref: imageSource)
+            }
+            image?.kf.animatedImageData = data
+            return image
+        #endif
+    }
+
+    public static func image(data: Data, scale: CGFloat, preloadAllAnimationData: Bool, onlyFirstFrame: Bool) -> Image? {
+        var image: Image?
+
+        #if os(macOS)
+            switch data.kf.imageFormat {
+            case .JPEG:
+                image = Image(data: data)
+            case .PNG:
+                image = Image(data: data)
+            case .GIF:
+                image = Kingfisher<Image>.animated(
+                    with: data,
+                    scale: scale,
+                    duration: 0.0,
+                    preloadAll: preloadAllAnimationData,
+                    onlyFirstFrame: onlyFirstFrame)
+            case .unknown:
+                image = Image(data: data)
+            }
+        #else
+            switch data.kf.imageFormat {
+            case .JPEG:
+                image = Image(data: data, scale: scale)
+            case .PNG:
+                image = Image(data: data, scale: scale)
+            case .GIF:
+                image = Kingfisher<Image>.animated(
+                    with: data,
+                    scale: scale,
+                    duration: 0.0,
+                    preloadAll: preloadAllAnimationData,
+                    onlyFirstFrame: onlyFirstFrame)
+            case .unknown:
+                image = Image(data: data, scale: scale)
+            }
+        #endif
+
+        return image
+    }
+}
+
+// MARK: - Image Transforming
+extension Kingfisher where Base: Image {
+    // MARK: - Blend Mode
+    /// Create image based on `self` and apply blend mode.
+    ///
+    /// - parameter blendMode:       The blend mode of creating image.
+    /// - parameter alpha:           The alpha should be used for image.
+    /// - parameter backgroundColor: The background color for the output image.
+    ///
+    /// - returns: An image with blend mode applied.
+    ///
+    /// - Note: This method only works for CG-based image.
+    #if !os(macOS)
+    public func image(withBlendMode blendMode: CGBlendMode,
+                      alpha: CGFloat = 1.0,
+                      backgroundColor: Color? = nil) -> Image
+    {
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Blend mode image only works for CG-based image.")
+            return base
+        }
+
+        let rect = CGRect(origin: .zero, size: size)
+        return draw(cgImage: cgImage, to: rect.size) {
+            if let backgroundColor = backgroundColor {
+                backgroundColor.setFill()
+                UIRectFill(rect)
+            }
+
+            base.draw(in: rect, blendMode: blendMode, alpha: alpha)
+        }
+    }
+    #endif
+
+    // MARK: - Compositing Operation
+    /// Create image based on `self` and apply compositing operation.
+    ///
+    /// - parameter compositingOperation: The compositing operation of creating image.
+    /// - parameter alpha:                The alpha should be used for image.
+    /// - parameter backgroundColor:      The background color for the output image.
+    ///
+    /// - returns: An image with compositing operation applied.
+    ///
+    /// - Note: This method only works for CG-based image.
+    #if os(macOS)
+    public func image(withCompositingOperation compositingOperation: NSCompositingOperation,
+                      alpha: CGFloat = 1.0,
+                      backgroundColor: Color? = nil) -> Image
+    {
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Compositing Operation image only works for CG-based image.")
+            return base
+        }
+
+        let rect = CGRect(origin: .zero, size: size)
+        return draw(cgImage: cgImage, to: rect.size) {
+            if let backgroundColor = backgroundColor {
+                backgroundColor.setFill()
+                rect.fill()
+            }
+
+            base.draw(in: rect, from: NSRect.zero, operation: compositingOperation, fraction: alpha)
+        }
+    }
+    #endif
+
+    // MARK: - Round Corner
+    /// Create a round corner image based on `self`.
+    ///
+    /// - parameter radius:          The round corner radius of creating image.
+    /// - parameter size:            The target size of creating image.
+    /// - parameter corners:         The target corners which will be applied rounding.
+    /// - parameter backgroundColor: The background color for the output image
+    ///
+    /// - returns: An image with round corner of `self`.
+    ///
+    /// - Note: This method only works for CG-based image.
+    public func image(withRoundRadius radius: CGFloat,
+                      fit size: CGSize,
+                      roundingCorners corners: RectCorner = .all,
+                      backgroundColor: Color? = nil) -> Image
+    {   
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Round corner image only works for CG-based image.")
+            return base
+        }
+        
+        let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
+        return draw(cgImage: cgImage, to: size) {
+            #if os(macOS)
+                if let backgroundColor = backgroundColor {
+                    let rectPath = NSBezierPath(rect: rect)
+                    backgroundColor.setFill()
+                    rectPath.fill()
+                }
+
+                let path = NSBezierPath(roundedRect: rect, byRoundingCorners: corners, radius: radius)
+                #if swift(>=4.2)
+                path.windingRule = .evenOdd
+                #else
+                path.windingRule = .evenOddWindingRule
+                #endif
+                path.addClip()
+                base.draw(in: rect)
+            #else
+                guard let context = UIGraphicsGetCurrentContext() else {
+                    assertionFailure("[Kingfisher] Failed to create CG context for image.")
+                    return
+                }
+
+                if let backgroundColor = backgroundColor {
+                    let rectPath = UIBezierPath(rect: rect)
+                    backgroundColor.setFill()
+                    rectPath.fill()
+                }
+
+                let path = UIBezierPath(roundedRect: rect,
+                                        byRoundingCorners: corners.uiRectCorner,
+                                        cornerRadii: CGSize(width: radius, height: radius)).cgPath
+                context.addPath(path)
+                context.clip()
+                base.draw(in: rect)
+            #endif
+        }
+    }
+    
+    #if os(iOS) || os(tvOS)
+    func resize(to size: CGSize, for contentMode: UIView.ContentMode) -> Image {
+        switch contentMode {
+        case .scaleAspectFit:
+            return resize(to: size, for: .aspectFit)
+        case .scaleAspectFill:
+            return resize(to: size, for: .aspectFill)
+        default:
+            return resize(to: size)
+        }
+    }
+    #endif
+    
+    // MARK: - Resize
+    /// Resize `self` to an image of new size.
+    ///
+    /// - parameter size: The target size.
+    ///
+    /// - returns: An image with new size.
+    ///
+    /// - Note: This method only works for CG-based image.
+    public func resize(to size: CGSize) -> Image {
+        
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Resize only works for CG-based image.")
+            return base
+        }
+        
+        let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
+        return draw(cgImage: cgImage, to: size) {
+            #if os(macOS)
+                base.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0)
+            #else
+                base.draw(in: rect)
+            #endif
+        }
+    }
+    
+    /// Resize `self` to an image of new size, respecting the content mode.
+    ///
+    /// - Parameters:
+    ///   - size: The target size.
+    ///   - contentMode: Content mode of output image should be.
+    /// - Returns: An image with new size.
+    public func resize(to size: CGSize, for contentMode: ContentMode) -> Image {
+        switch contentMode {
+        case .aspectFit:
+            let newSize = self.size.kf.constrained(size)
+            return resize(to: newSize)
+        case .aspectFill:
+            let newSize = self.size.kf.filling(size)
+            return resize(to: newSize)
+        default:
+            return resize(to: size)
+        }
+    }
+    
+    public func crop(to size: CGSize, anchorOn anchor: CGPoint) -> Image {
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Crop only works for CG-based image.")
+            return base
+        }
+        
+        let rect = self.size.kf.constrainedRect(for: size, anchor: anchor)
+        guard let image = cgImage.cropping(to: rect.scaled(scale)) else {
+            assertionFailure("[Kingfisher] Cropping image failed.")
+            return base
+        }
+        
+        return Kingfisher.image(cgImage: image, scale: scale, refImage: base)
+    }
+    
+    // MARK: - Blur
+    
+    /// Create an image with blur effect based on `self`.
+    ///
+    /// - parameter radius: The blur radius should be used when creating blur effect.
+    ///
+    /// - returns: An image with blur effect applied.
+    ///
+    /// - Note: This method only works for CG-based image.
+    public func blurred(withRadius radius: CGFloat) -> Image {
+        #if os(watchOS)
+            return base
+        #else
+            guard let cgImage = cgImage else {
+                assertionFailure("[Kingfisher] Blur only works for CG-based image.")
+                return base
+            }
+            
+            // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
+            // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
+            // if d is odd, use three box-blurs of size 'd', centered on the output pixel.
+            let s = Float(max(radius, 2.0))
+            // We will do blur on a resized image (*0.5), so the blur radius could be half as well.
+            
+            // Fix the slow compiling time for Swift 3. 
+            // See https://github.com/onevcat/Kingfisher/issues/611
+            let pi2 = 2 * Float.pi
+            let sqrtPi2 = sqrt(pi2)
+            var targetRadius = floor(s * 3.0 * sqrtPi2 / 4.0 + 0.5)
+            
+            if targetRadius.isEven {
+                targetRadius += 1
+            }
+            
+            let iterations: Int
+            if radius < 0.5 {
+                iterations = 1
+            } else if radius < 1.5 {
+                iterations = 2
+            } else {
+                iterations = 3
+            }
+            
+            let w = Int(size.width)
+            let h = Int(size.height)
+            let rowBytes = Int(CGFloat(cgImage.bytesPerRow))
+            
+            func createEffectBuffer(_ context: CGContext) -> vImage_Buffer {
+                let data = context.data
+                let width = vImagePixelCount(context.width)
+                let height = vImagePixelCount(context.height)
+                let rowBytes = context.bytesPerRow
+                
+                return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
+            }
+
+            guard let context = beginContext(size: size, scale: scale) else {
+                assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
+                return base
+            }
+            defer { endContext() }
+
+            context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h))
+            
+            var inBuffer = createEffectBuffer(context)
+            
+            guard let outContext = beginContext(size: size, scale: scale) else {
+                assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
+                return base
+            }
+            defer { endContext() }
+            var outBuffer = createEffectBuffer(outContext)
+            
+            for _ in 0 ..< iterations {
+                vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, nil, 0, 0, UInt32(targetRadius), UInt32(targetRadius), nil, vImage_Flags(kvImageEdgeExtend))
+                (inBuffer, outBuffer) = (outBuffer, inBuffer)
+            }
+            
+            #if os(macOS)
+                let result = outContext.makeImage().flatMap { fixedForRetinaPixel(cgImage: $0, to: size) }
+            #else
+                let result = outContext.makeImage().flatMap { Image(cgImage: $0, scale: base.scale, orientation: base.imageOrientation) }
+            #endif
+            guard let blurredImage = result else {
+                assertionFailure("[Kingfisher] Can not make an blurred image within this context.")
+                return base
+            }
+            
+            return blurredImage
+        #endif
+    }
+    
+    // MARK: - Overlay
+    
+    /// Create an image from `self` with a color overlay layer.
+    ///
+    /// - parameter color:    The color should be use to overlay.
+    /// - parameter fraction: Fraction of input color. From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay.
+    ///
+    /// - returns: An image with a color overlay applied.
+    ///
+    /// - Note: This method only works for CG-based image.
+    public func overlaying(with color: Color, fraction: CGFloat) -> Image {
+        
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Overlaying only works for CG-based image.")
+            return base
+        }
+        
+        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
+        return draw(cgImage: cgImage, to: rect.size) {
+            #if os(macOS)
+                base.draw(in: rect)
+                if fraction > 0 {
+                    color.withAlphaComponent(1 - fraction).set()
+                    rect.fill(using: .sourceAtop)
+                }
+            #else
+                color.set()
+                UIRectFill(rect)
+                base.draw(in: rect, blendMode: .destinationIn, alpha: 1.0)
+                
+                if fraction > 0 {
+                    base.draw(in: rect, blendMode: .sourceAtop, alpha: fraction)
+                }
+            #endif
+        }
+    }
+    
+    // MARK: - Tint
+    
+    /// Create an image from `self` with a color tint.
+    ///
+    /// - parameter color: The color should be used to tint `self`
+    ///
+    /// - returns: An image with a color tint applied.
+    public func tinted(with color: Color) -> Image {
+        #if os(watchOS)
+            return base
+        #else
+            return apply(.tint(color))
+        #endif
+    }
+    
+    // MARK: - Color Control
+    
+    /// Create an image from `self` with color control.
+    ///
+    /// - parameter brightness: Brightness changing to image.
+    /// - parameter contrast:   Contrast changing to image.
+    /// - parameter saturation: Saturation changing to image.
+    /// - parameter inputEV:    InputEV changing to image.
+    ///
+    /// - returns: An image with color control applied.
+    public func adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image {
+        #if os(watchOS)
+            return base
+        #else
+            return apply(.colorControl((brightness, contrast, saturation, inputEV)))
+        #endif
+    }
+
+    /// Return an image with given scale.
+    ///
+    /// - Parameter scale: Target scale factor the new image should have.
+    /// - Returns: The image with target scale. If the base image is already in the scale, `base` will be returned.
+    public func scaled(to scale: CGFloat) -> Image {
+        guard scale != self.scale else {
+            return base
+        }
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Scaling only works for CG-based image.")
+            return base
+        }
+        return Kingfisher.image(cgImage: cgImage, scale: scale, refImage: base)
+    }
+}
+
+// MARK: - Decode
+extension Kingfisher where Base: Image {
+    public var decoded: Image {
+        return decoded(scale: scale)
+    }
+    
+    public func decoded(scale: CGFloat) -> Image {
+        // prevent animated image (GIF) lose it's images
+        #if os(iOS)
+            if imageSource != nil { return base }
+        #else
+            if images != nil { return base }
+        #endif
+        
+        guard let imageRef = self.cgImage else {
+            assertionFailure("[Kingfisher] Decoding only works for CG-based image.")
+            return base
+        }
+        
+        // Draw CGImage in a plain context with scale of 1.0.
+        guard let context = beginContext(size: CGSize(width: imageRef.width, height: imageRef.height), scale: 1.0) else {
+            assertionFailure("[Kingfisher] Decoding fails to create a valid context.")
+            return base
+        }
+        
+        defer { endContext() }
+        
+        let rect = CGRect(x: 0, y: 0, width: CGFloat(imageRef.width), height: CGFloat(imageRef.height))
+        context.draw(imageRef, in: rect)
+        let decompressedImageRef = context.makeImage()
+        return Kingfisher<Image>.image(cgImage: decompressedImageRef!, scale: scale, refImage: base)
+    }
+}
+
+/// Reference the source image reference
+final class ImageSource {
+    var imageRef: CGImageSource?
+    init(ref: CGImageSource) {
+        self.imageRef = ref
+    }
+}
+
+// MARK: - Image format
+private struct ImageHeaderData {
+    static var PNG: [UInt8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
+    static var JPEG_SOI: [UInt8] = [0xFF, 0xD8]
+    static var JPEG_IF: [UInt8] = [0xFF]
+    static var GIF: [UInt8] = [0x47, 0x49, 0x46]
+}
+
+public enum ImageFormat {
+    case unknown, PNG, JPEG, GIF
+}
+
+
+// MARK: - Misc Helpers
+public struct DataProxy {
+    fileprivate let base: Data
+    init(proxy: Data) {
+        base = proxy
+    }
+}
+
+extension Data: KingfisherCompatible {
+    public typealias CompatibleType = DataProxy
+    public var kf: DataProxy {
+        return DataProxy(proxy: self)
+    }
+}
+
+extension DataProxy {
+    public var imageFormat: ImageFormat {
+        var buffer = [UInt8](repeating: 0, count: 8)
+        (base as NSData).getBytes(&buffer, length: 8)
+        if buffer == ImageHeaderData.PNG {
+            return .PNG
+        } else if buffer[0] == ImageHeaderData.JPEG_SOI[0] &&
+            buffer[1] == ImageHeaderData.JPEG_SOI[1] &&
+            buffer[2] == ImageHeaderData.JPEG_IF[0]
+        {
+            return .JPEG
+        } else if buffer[0] == ImageHeaderData.GIF[0] &&
+            buffer[1] == ImageHeaderData.GIF[1] &&
+            buffer[2] == ImageHeaderData.GIF[2]
+        {
+            return .GIF
+        }
+
+        return .unknown
+    }
+}
+
+public struct CGSizeProxy {
+    fileprivate let base: CGSize
+    init(proxy: CGSize) {
+        base = proxy
+    }
+}
+
+extension CGSize: KingfisherCompatible {
+    public typealias CompatibleType = CGSizeProxy
+    public var kf: CGSizeProxy {
+        return CGSizeProxy(proxy: self)
+    }
+}
+
+extension CGSizeProxy {
+    
+    public func resize(to size: CGSize, for contentMode: ContentMode) -> CGSize {
+        switch contentMode {
+        case .aspectFit:
+            return constrained(size)
+        case .aspectFill:
+            return filling(size)
+        default:
+            return self.base
+        }
+    }
+    
+    public func constrained(_ size: CGSize) -> CGSize {
+        let aspectWidth = round(aspectRatio * size.height)
+        let aspectHeight = round(size.width / aspectRatio)
+
+        return aspectWidth > size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height)
+    }
+
+    public func filling(_ size: CGSize) -> CGSize {
+        let aspectWidth = round(aspectRatio * size.height)
+        let aspectHeight = round(size.width / aspectRatio)
+
+        return aspectWidth < size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height)
+    }
+
+    private var aspectRatio: CGFloat {
+        return base.height == 0.0 ? 1.0 : base.width / base.height
+    }
+    
+    
+    public func constrainedRect(for size: CGSize, anchor: CGPoint) -> CGRect {
+        
+        let unifiedAnchor = CGPoint(x: anchor.x.clamped(to: 0.0...1.0),
+                                    y: anchor.y.clamped(to: 0.0...1.0))
+        
+        let x = unifiedAnchor.x * base.width - unifiedAnchor.x * size.width
+        let y = unifiedAnchor.y * base.height - unifiedAnchor.y * size.height
+        let r = CGRect(x: x, y: y, width: size.width, height: size.height)
+        
+        let ori = CGRect(origin: CGPoint.zero, size: base)
+        return ori.intersection(r)
+    }
+}
+
+extension CGRect {
+    func scaled(_ scale: CGFloat) -> CGRect {
+        return CGRect(x: origin.x * scale, y: origin.y * scale,
+                      width: size.width * scale, height: size.height * scale)
+    }
+}
+
+extension Comparable {
+    func clamped(to limits: ClosedRange<Self>) -> Self {
+        return min(max(self, limits.lowerBound), limits.upperBound)
+    }
+}
+
+extension Kingfisher where Base: Image {
+    
+    func beginContext(size: CGSize, scale: CGFloat) -> CGContext? {
+        #if os(macOS)
+            guard let rep = NSBitmapImageRep(
+                bitmapDataPlanes: nil,
+                pixelsWide: Int(size.width),
+                pixelsHigh: Int(size.height),
+                bitsPerSample: cgImage?.bitsPerComponent ?? 8,
+                samplesPerPixel: 4,
+                hasAlpha: true,
+                isPlanar: false,
+                colorSpaceName: .calibratedRGB,
+                bytesPerRow: 0,
+                bitsPerPixel: 0) else
+            {
+                assertionFailure("[Kingfisher] Image representation cannot be created.")
+                return nil
+            }
+            rep.size = size
+            NSGraphicsContext.saveGraphicsState()
+            guard let context = NSGraphicsContext(bitmapImageRep: rep) else {
+                assertionFailure("[Kingfisher] Image contenxt cannot be created.")
+                return nil
+            }
+            
+            NSGraphicsContext.current = context
+            return context.cgContext
+        #else
+            UIGraphicsBeginImageContextWithOptions(size, false, scale)
+            let context = UIGraphicsGetCurrentContext()
+            context?.scaleBy(x: 1.0, y: -1.0)
+            context?.translateBy(x: 0, y: -size.height)
+            return context
+        #endif
+    }
+    
+    func endContext() {
+        #if os(macOS)
+            NSGraphicsContext.restoreGraphicsState()
+        #else
+            UIGraphicsEndImageContext()
+        #endif
+    }
+    
+    func draw(cgImage: CGImage?, to size: CGSize, draw: ()->()) -> Image {
+        #if os(macOS)
+        guard let rep = NSBitmapImageRep(
+            bitmapDataPlanes: nil,
+            pixelsWide: Int(size.width),
+            pixelsHigh: Int(size.height),
+            bitsPerSample: cgImage?.bitsPerComponent ?? 8,
+            samplesPerPixel: 4,
+            hasAlpha: true,
+            isPlanar: false,
+            colorSpaceName: .calibratedRGB,
+            bytesPerRow: 0,
+            bitsPerPixel: 0) else
+        {
+            assertionFailure("[Kingfisher] Image representation cannot be created.")
+            return base
+        }
+        rep.size = size
+        
+        NSGraphicsContext.saveGraphicsState()
+        
+        let context = NSGraphicsContext(bitmapImageRep: rep)
+        NSGraphicsContext.current = context
+        draw()
+        NSGraphicsContext.restoreGraphicsState()
+        
+        let outputImage = Image(size: size)
+        outputImage.addRepresentation(rep)
+        return outputImage
+        #else
+            
+        UIGraphicsBeginImageContextWithOptions(size, false, scale)
+        defer { UIGraphicsEndImageContext() }
+        draw()
+        return UIGraphicsGetImageFromCurrentImageContext() ?? base
+        
+        #endif
+    }
+    
+    #if os(macOS)
+    func fixedForRetinaPixel(cgImage: CGImage, to size: CGSize) -> Image {
+        
+        let image = Image(cgImage: cgImage, size: base.size)
+        let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
+        
+        return draw(cgImage: cgImage, to: self.size) {
+            image.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0)
+        }
+    }
+    #endif
+}
+
+extension Float {
+    var isEven: Bool {
+        return truncatingRemainder(dividingBy: 2.0) == 0
+    }
+}
+
+#if os(macOS)
+extension NSBezierPath {
+    convenience init(roundedRect rect: NSRect, topLeftRadius: CGFloat, topRightRadius: CGFloat,
+         bottomLeftRadius: CGFloat, bottomRightRadius: CGFloat)
+    {
+        self.init()
+        
+        let maxCorner = min(rect.width, rect.height) / 2
+        
+        let radiusTopLeft = min(maxCorner, max(0, topLeftRadius))
+        let radiusTopRight = min(maxCorner, max(0, topRightRadius))
+        let radiusBottomLeft = min(maxCorner, max(0, bottomLeftRadius))
+        let radiusBottomRight = min(maxCorner, max(0, bottomRightRadius))
+        
+        guard !NSIsEmptyRect(rect) else {
+            return
+        }
+        
+        let topLeft = NSMakePoint(NSMinX(rect), NSMaxY(rect));
+        let topRight = NSMakePoint(NSMaxX(rect), NSMaxY(rect));
+        let bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect));
+        
+        move(to: NSMakePoint(NSMidX(rect), NSMaxY(rect)))
+        appendArc(from: topLeft, to: rect.origin, radius: radiusTopLeft)
+        appendArc(from: rect.origin, to: bottomRight, radius: radiusBottomLeft)
+        appendArc(from: bottomRight, to: topRight, radius: radiusBottomRight)
+        appendArc(from: topRight, to: topLeft, radius: radiusTopRight)
+        close()
+    }
+    
+    convenience init(roundedRect rect: NSRect, byRoundingCorners corners: RectCorner, radius: CGFloat) {
+        let radiusTopLeft = corners.contains(.topLeft) ? radius : 0
+        let radiusTopRight = corners.contains(.topRight) ? radius : 0
+        let radiusBottomLeft = corners.contains(.bottomLeft) ? radius : 0
+        let radiusBottomRight = corners.contains(.bottomRight) ? radius : 0
+        
+        self.init(roundedRect: rect, topLeftRadius: radiusTopLeft, topRightRadius: radiusTopRight,
+                  bottomLeftRadius: radiusBottomLeft, bottomRightRadius: radiusBottomRight)
+    }
+}
+    
+#else
+extension RectCorner {
+    var uiRectCorner: UIRectCorner {
+        
+        var result: UIRectCorner = []
+        
+        if self.contains(.topLeft) { result.insert(.topLeft) }
+        if self.contains(.topRight) { result.insert(.topRight) }
+        if self.contains(.bottomLeft) { result.insert(.bottomLeft) }
+        if self.contains(.bottomRight) { result.insert(.bottomRight) }
+        
+        return result
+    }
+}
+#endif
+

+ 742 - 0
Example/Pods/Kingfisher/Sources/ImageCache.swift

@@ -0,0 +1,742 @@
+//
+//  ImageCache.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+
+extension Notification.Name {
+    /**
+     This notification will be sent when the disk cache got cleaned either there are cached files expired or the total size exceeding the max allowed size. The manually invoking of `clearDiskCache` method will not trigger this notification.
+     
+     The `object` of this notification is the `ImageCache` object which sends the notification.
+     
+     A list of removed hashes (files) could be retrieved by accessing the array under `KingfisherDiskCacheCleanedHashKey` key in `userInfo` of the notification object you received. By checking the array, you could know the hash codes of files are removed.
+     
+     The main purpose of this notification is supplying a chance to maintain some necessary information on the cached files. See [this wiki](https://github.com/onevcat/Kingfisher/wiki/How-to-implement-ETag-based-304-(Not-Modified)-handling-in-Kingfisher) for a use case on it.
+     */
+    public static let KingfisherDidCleanDiskCache = Notification.Name.init("com.onevcat.Kingfisher.KingfisherDidCleanDiskCache")
+}
+
+/**
+Key for array of cleaned hashes in `userInfo` of `KingfisherDidCleanDiskCacheNotification`.
+*/
+public let KingfisherDiskCacheCleanedHashKey = "com.onevcat.Kingfisher.cleanedHash"
+
+/// It represents a task of retrieving image. You can call `cancel` on it to stop the process.
+public typealias RetrieveImageDiskTask = DispatchWorkItem
+
+/**
+Cache type of a cached image.
+
+- None:   The image is not cached yet when retrieving it.
+- Memory: The image is cached in memory.
+- Disk:   The image is cached in disk.
+*/
+public enum CacheType {
+    case none, memory, disk
+    
+    public var cached: Bool {
+        switch self {
+        case .memory, .disk: return true
+        case .none: return false
+        }
+    }
+}
+
+/// `ImageCache` represents both the memory and disk cache system of Kingfisher. 
+/// While a default image cache object will be used if you prefer the extension methods of Kingfisher, 
+/// you can create your own cache object and configure it as your need. You could use an `ImageCache`
+/// object to manipulate memory and disk cache for Kingfisher.
+open class ImageCache {
+
+    //Memory
+    fileprivate let memoryCache = NSCache<NSString, AnyObject>()
+    
+    /// The largest cache cost of memory cache. The total cost is pixel count of 
+    /// all cached images in memory.
+    /// Default is unlimited. Memory cache will be purged automatically when a 
+    /// memory warning notification is received.
+    open var maxMemoryCost: UInt = 0 {
+        didSet {
+            self.memoryCache.totalCostLimit = Int(maxMemoryCost)
+        }
+    }
+    
+    //Disk
+    fileprivate let ioQueue: DispatchQueue
+    fileprivate var fileManager: FileManager!
+    
+    ///The disk cache location.
+    public let diskCachePath: String
+  
+    /// The default file extension appended to cached files.
+    open var pathExtension: String?
+    
+    /// The longest time duration in second of the cache being stored in disk. 
+    /// Default is 1 week (60 * 60 * 24 * 7 seconds).
+    /// Setting this to a negative value will make the disk cache never expiring.
+    open var maxCachePeriodInSecond: TimeInterval = 60 * 60 * 24 * 7 //Cache exists for 1 week
+    
+    /// The largest disk size can be taken for the cache. It is the total 
+    /// allocated size of cached files in bytes.
+    /// Default is no limit.
+    open var maxDiskCacheSize: UInt = 0
+    
+    fileprivate let processQueue: DispatchQueue
+    
+    /// The default cache.
+    public static let `default` = ImageCache(name: "default")
+    
+    /// Closure that defines the disk cache path from a given path and cacheName.
+    public typealias DiskCachePathClosure = (String?, String) -> String
+    
+    /// The default DiskCachePathClosure
+    public final class func defaultDiskCachePathClosure(path: String?, cacheName: String) -> String {
+        let dstPath = path ?? NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!
+        return (dstPath as NSString).appendingPathComponent(cacheName)
+    }
+    
+    /**
+    Init method. Passing a name for the cache. It represents a cache folder in the memory and disk.
+    
+    - parameter name: Name of the cache. It will be used as the memory cache name and the disk cache folder name 
+                      appending to the cache path. This value should not be an empty string.
+    - parameter path: Optional - Location of cache path on disk. If `nil` is passed in (the default value),
+                      the `.cachesDirectory` in of your app will be used.
+    - parameter diskCachePathClosure: Closure that takes in an optional initial path string and generates
+                      the final disk cache path. You could use it to fully customize your cache path.
+    */
+    public init(name: String,
+                path: String? = nil,
+                diskCachePathClosure: DiskCachePathClosure = ImageCache.defaultDiskCachePathClosure)
+    {
+        
+        if name.isEmpty {
+            fatalError("[Kingfisher] You should specify a name for the cache. A cache with empty name is not permitted.")
+        }
+        
+        let cacheName = "com.onevcat.Kingfisher.ImageCache.\(name)"
+        memoryCache.name = cacheName
+        
+        diskCachePath = diskCachePathClosure(path, cacheName)
+        
+        let ioQueueName = "com.onevcat.Kingfisher.ImageCache.ioQueue.\(name)"
+        ioQueue = DispatchQueue(label: ioQueueName)
+        
+        let processQueueName = "com.onevcat.Kingfisher.ImageCache.processQueue.\(name)"
+        processQueue = DispatchQueue(label: processQueueName, attributes: .concurrent)
+        
+        ioQueue.sync { fileManager = FileManager() }
+        
+#if !os(macOS) && !os(watchOS)
+        
+        #if swift(>=4.2)
+        let memoryNotification = UIApplication.didReceiveMemoryWarningNotification
+        let terminateNotification = UIApplication.willTerminateNotification
+        let enterbackgroundNotification = UIApplication.didEnterBackgroundNotification
+        #else
+        let memoryNotification = NSNotification.Name.UIApplicationDidReceiveMemoryWarning
+        let terminateNotification = NSNotification.Name.UIApplicationWillTerminate
+        let enterbackgroundNotification = NSNotification.Name.UIApplicationDidEnterBackground
+        #endif
+        
+        NotificationCenter.default.addObserver(
+            self, selector: #selector(clearMemoryCache), name: memoryNotification, object: nil)
+        NotificationCenter.default.addObserver(
+            self, selector: #selector(cleanExpiredDiskCache), name: terminateNotification, object: nil)
+        NotificationCenter.default.addObserver(
+            self, selector: #selector(backgroundCleanExpiredDiskCache), name: enterbackgroundNotification, object: nil)
+#endif
+    }
+    
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
+
+
+    // MARK: - Store & Remove
+
+    /**
+    Store an image to cache. It will be saved to both memory and disk. It is an async operation.
+    
+    - parameter image:             The image to be stored.
+    - parameter original:          The original data of the image.
+                                   Kingfisher will use it to check the format of the image and optimize cache size on disk.
+                                   If `nil` is supplied, the image data will be saved as a normalized PNG file.
+                                   It is strongly suggested to supply it whenever possible, to get a better performance and disk usage.
+    - parameter key:               Key for the image.
+    - parameter identifier:        The identifier of processor used. If you are using a processor for the image, pass the identifier of
+                                   processor to it.
+                                   This identifier will be used to generate a corresponding key for the combination of `key` and processor.
+    - parameter toDisk:            Whether this image should be cached to disk or not. If false, the image will be only cached in memory.
+    - parameter completionHandler: Called when store operation completes.
+    */
+    open func store(_ image: Image,
+                      original: Data? = nil,
+                      forKey key: String,
+                      processorIdentifier identifier: String = "",
+                      cacheSerializer serializer: CacheSerializer = DefaultCacheSerializer.default,
+                      toDisk: Bool = true,
+                      completionHandler: (() -> Void)? = nil)
+    {
+        
+        let computedKey = key.computedKey(with: identifier)
+        memoryCache.setObject(image, forKey: computedKey as NSString, cost: image.kf.imageCost)
+
+        func callHandlerInMainQueue() {
+            if let handler = completionHandler {
+                DispatchQueue.main.async {
+                    handler()
+                }
+            }
+        }
+        
+        if toDisk {
+            ioQueue.async {
+                
+                if let data = serializer.data(with: image, original: original) {
+                    if !self.fileManager.fileExists(atPath: self.diskCachePath) {
+                        do {
+                            try self.fileManager.createDirectory(atPath: self.diskCachePath, withIntermediateDirectories: true, attributes: nil)
+                        } catch _ {}
+                    }
+                    
+                    self.fileManager.createFile(atPath: self.cachePath(forComputedKey: computedKey), contents: data, attributes: nil)
+                }
+                callHandlerInMainQueue()
+            }
+        } else {
+            callHandlerInMainQueue()
+        }
+    }
+    
+    /**
+    Remove the image for key for the cache. It will be opted out from both memory and disk. 
+    It is an async operation.
+    
+    - parameter key:               Key for the image.
+    - parameter identifier:        The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it.
+                                   This identifier will be used to generate a corresponding key for the combination of `key` and processor.
+    - parameter fromMemory:        Whether this image should be removed from memory or not. If false, the image won't be removed from memory.
+    - parameter fromDisk:          Whether this image should be removed from disk or not. If false, the image won't be removed from disk.
+    - parameter completionHandler: Called when removal operation completes.
+    */
+    open func removeImage(forKey key: String,
+                          processorIdentifier identifier: String = "",
+                          fromMemory: Bool = true,
+                          fromDisk: Bool = true,
+                          completionHandler: (() -> Void)? = nil)
+    {
+        let computedKey = key.computedKey(with: identifier)
+
+        if fromMemory {
+            memoryCache.removeObject(forKey: computedKey as NSString)
+        }
+        
+        func callHandlerInMainQueue() {
+            if let handler = completionHandler {
+                DispatchQueue.main.async {
+                    handler()
+                }
+            }
+        }
+        
+        if fromDisk {
+            ioQueue.async{
+                do {
+                    try self.fileManager.removeItem(atPath: self.cachePath(forComputedKey: computedKey))
+                } catch _ {}
+                callHandlerInMainQueue()
+            }
+        } else {
+            callHandlerInMainQueue()
+        }
+    }
+
+    // MARK: - Get data from cache
+
+    /**
+    Get an image for a key from memory or disk.
+    
+    - parameter key:               Key for the image.
+    - parameter options:           Options of retrieving image. If you need to retrieve an image which was 
+                                   stored with a specified `ImageProcessor`, pass the processor in the option too.
+    - parameter completionHandler: Called when getting operation completes with image result and cached type of 
+                                   this image. If there is no such key cached, the image will be `nil`.
+    
+    - returns: The retrieving task.
+    */
+    @discardableResult
+    open func retrieveImage(forKey key: String,
+                               options: KingfisherOptionsInfo?,
+                     completionHandler: ((Image?, CacheType) -> Void)?) -> RetrieveImageDiskTask?
+    {
+        // No completion handler. Not start working and early return.
+        guard let completionHandler = completionHandler else {
+            return nil
+        }
+        
+        var block: RetrieveImageDiskTask?
+        let options = options ?? KingfisherEmptyOptionsInfo
+        let imageModifier = options.imageModifier
+
+        if let image = self.retrieveImageInMemoryCache(forKey: key, options: options) {
+            options.callbackDispatchQueue.safeAsync {
+                completionHandler(imageModifier.modify(image), .memory)
+            }
+        } else if options.fromMemoryCacheOrRefresh { // Only allows to get images from memory cache.
+            options.callbackDispatchQueue.safeAsync {
+                completionHandler(nil, .none)
+            }
+        } else {
+            var sSelf: ImageCache! = self
+            block = DispatchWorkItem(block: {
+                // Begin to load image from disk
+                if let image = sSelf.retrieveImageInDiskCache(forKey: key, options: options) {
+                    if options.backgroundDecode {
+                        sSelf.processQueue.async {
+
+                            let result = image.kf.decoded
+                            
+                            sSelf.store(result,
+                                        forKey: key,
+                                        processorIdentifier: options.processor.identifier,
+                                        cacheSerializer: options.cacheSerializer,
+                                        toDisk: false,
+                                        completionHandler: nil)
+                            options.callbackDispatchQueue.safeAsync {
+                                completionHandler(imageModifier.modify(result), .disk)
+                                sSelf = nil
+                            }
+                        }
+                    } else {
+                        sSelf.store(image,
+                                    forKey: key,
+                                    processorIdentifier: options.processor.identifier,
+                                    cacheSerializer: options.cacheSerializer,
+                                    toDisk: false,
+                                    completionHandler: nil
+                        )
+                        options.callbackDispatchQueue.safeAsync {
+                            completionHandler(imageModifier.modify(image), .disk)
+                            sSelf = nil
+                        }
+                    }
+                } else {
+                    // No image found from either memory or disk
+                    options.callbackDispatchQueue.safeAsync {
+                        completionHandler(nil, .none)
+                        sSelf = nil
+                    }
+                }
+            })
+            
+            sSelf.ioQueue.async(execute: block!)
+        }
+    
+        return block
+    }
+    
+    /**
+    Get an image for a key from memory.
+    
+    - parameter key:     Key for the image.
+    - parameter options: Options of retrieving image. If you need to retrieve an image which was 
+                         stored with a specified `ImageProcessor`, pass the processor in the option too.
+    - returns: The image object if it is cached, or `nil` if there is no such key in the cache.
+    */
+    open func retrieveImageInMemoryCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? {
+        
+        let options = options ?? KingfisherEmptyOptionsInfo
+        let computedKey = key.computedKey(with: options.processor.identifier)
+        
+        return memoryCache.object(forKey: computedKey as NSString) as? Image
+    }
+    
+    /**
+    Get an image for a key from disk.
+    
+    - parameter key:     Key for the image.
+    - parameter options: Options of retrieving image. If you need to retrieve an image which was
+                         stored with a specified `ImageProcessor`, pass the processor in the option too.
+
+    - returns: The image object if it is cached, or `nil` if there is no such key in the cache.
+    */
+    open func retrieveImageInDiskCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? {
+        
+        let options = options ?? KingfisherEmptyOptionsInfo
+        let computedKey = key.computedKey(with: options.processor.identifier)
+        
+        return diskImage(forComputedKey: computedKey, serializer: options.cacheSerializer, options: options)
+    }
+
+
+    // MARK: - Clear & Clean
+
+    /**
+    Clear memory cache.
+    */
+    @objc public func clearMemoryCache() {
+        memoryCache.removeAllObjects()
+    }
+    
+    /**
+    Clear disk cache. This is an async operation.
+    
+    - parameter completionHander: Called after the operation completes.
+    */
+    open func clearDiskCache(completion handler: (()->())? = nil) {
+        ioQueue.async {
+            do {
+                try self.fileManager.removeItem(atPath: self.diskCachePath)
+                try self.fileManager.createDirectory(atPath: self.diskCachePath, withIntermediateDirectories: true, attributes: nil)
+            } catch _ { }
+            
+            if let handler = handler {
+                DispatchQueue.main.async {
+                    handler()
+                }
+            }
+        }
+    }
+    
+    /**
+    Clean expired disk cache. This is an async operation.
+    */
+    @objc fileprivate func cleanExpiredDiskCache() {
+        cleanExpiredDiskCache(completion: nil)
+    }
+    
+    /**
+    Clean expired disk cache. This is an async operation.
+    
+    - parameter completionHandler: Called after the operation completes.
+    */
+    open func cleanExpiredDiskCache(completion handler: (()->())? = nil) {
+        
+        // Do things in concurrent io queue
+        ioQueue.async {
+            
+            var (URLsToDelete, diskCacheSize, cachedFiles) = self.travelCachedFiles(onlyForCacheSize: false)
+            
+            for fileURL in URLsToDelete {
+                do {
+                    try self.fileManager.removeItem(at: fileURL)
+                } catch _ { }
+            }
+                
+            if self.maxDiskCacheSize > 0 && diskCacheSize > self.maxDiskCacheSize {
+                let targetSize = self.maxDiskCacheSize / 2
+                    
+                // Sort files by last modify date. We want to clean from the oldest files.
+                let sortedFiles = cachedFiles.keysSortedByValue {
+                    resourceValue1, resourceValue2 -> Bool in
+                    
+                    if let date1 = resourceValue1.contentAccessDate,
+                       let date2 = resourceValue2.contentAccessDate
+                    {
+                        return date1.compare(date2) == .orderedAscending
+                    }
+                    
+                    // Not valid date information. This should not happen. Just in case.
+                    return true
+                }
+                
+                for fileURL in sortedFiles {
+                    
+                    do {
+                        try self.fileManager.removeItem(at: fileURL)
+                    } catch { }
+                        
+                    URLsToDelete.append(fileURL)
+                    
+                    if let fileSize = cachedFiles[fileURL]?.totalFileAllocatedSize {
+                        diskCacheSize -= UInt(fileSize)
+                    }
+                    
+                    if diskCacheSize < targetSize {
+                        break
+                    }
+                }
+            }
+                
+            DispatchQueue.main.async {
+                
+                if URLsToDelete.count != 0 {
+                    let cleanedHashes = URLsToDelete.map { $0.lastPathComponent }
+                    NotificationCenter.default.post(name: .KingfisherDidCleanDiskCache, object: self, userInfo: [KingfisherDiskCacheCleanedHashKey: cleanedHashes])
+                }
+                
+                handler?()
+            }
+        }
+    }
+    
+    fileprivate func travelCachedFiles(onlyForCacheSize: Bool) -> (urlsToDelete: [URL], diskCacheSize: UInt, cachedFiles: [URL: URLResourceValues]) {
+        
+        let diskCacheURL = URL(fileURLWithPath: diskCachePath)
+        let resourceKeys: Set<URLResourceKey> = [.isDirectoryKey, .contentAccessDateKey, .totalFileAllocatedSizeKey]
+        let expiredDate: Date? = (maxCachePeriodInSecond < 0) ? nil : Date(timeIntervalSinceNow: -maxCachePeriodInSecond)
+        
+        var cachedFiles = [URL: URLResourceValues]()
+        var urlsToDelete = [URL]()
+        var diskCacheSize: UInt = 0
+
+        for fileUrl in (try? fileManager.contentsOfDirectory(at: diskCacheURL, includingPropertiesForKeys: Array(resourceKeys), options: .skipsHiddenFiles)) ?? [] {
+
+            do {
+                let resourceValues = try fileUrl.resourceValues(forKeys: resourceKeys)
+                // If it is a Directory. Continue to next file URL.
+                if resourceValues.isDirectory == true {
+                    continue
+                }
+
+                // If this file is expired, add it to URLsToDelete
+                if !onlyForCacheSize,
+                    let expiredDate = expiredDate,
+                    let lastAccessData = resourceValues.contentAccessDate,
+                    (lastAccessData as NSDate).laterDate(expiredDate) == expiredDate
+                {
+                    urlsToDelete.append(fileUrl)
+                    continue
+                }
+
+                if let fileSize = resourceValues.totalFileAllocatedSize {
+                    diskCacheSize += UInt(fileSize)
+                    if !onlyForCacheSize {
+                        cachedFiles[fileUrl] = resourceValues
+                    }
+                }
+            } catch _ { }
+        }
+
+        return (urlsToDelete, diskCacheSize, cachedFiles)
+    }
+
+#if !os(macOS) && !os(watchOS)
+    /**
+    Clean expired disk cache when app in background. This is an async operation.
+    In most cases, you should not call this method explicitly. 
+    It will be called automatically when `UIApplicationDidEnterBackgroundNotification` received.
+    */
+    @objc public func backgroundCleanExpiredDiskCache() {
+        // if 'sharedApplication()' is unavailable, then return
+        guard let sharedApplication = Kingfisher<UIApplication>.shared else { return }
+
+        func endBackgroundTask(_ task: inout UIBackgroundTaskIdentifier) {
+            sharedApplication.endBackgroundTask(task)
+            #if swift(>=4.2)
+            task = UIBackgroundTaskIdentifier.invalid
+            #else
+            task = UIBackgroundTaskInvalid
+            #endif
+        }
+        
+        var backgroundTask: UIBackgroundTaskIdentifier!
+        backgroundTask = sharedApplication.beginBackgroundTask {
+            endBackgroundTask(&backgroundTask!)
+        }
+        
+        cleanExpiredDiskCache {
+            endBackgroundTask(&backgroundTask!)
+        }
+    }
+#endif
+
+
+    // MARK: - Check cache status
+    
+    /// Cache type for checking whether an image is cached for a key in current cache.
+    ///
+    /// - Parameters:
+    ///   - key: Key for the image.
+    ///   - identifier: Processor identifier which used for this image. Default is empty string.
+    /// - Returns: A `CacheType` instance which indicates the cache status. `.none` means the image is not in cache yet.
+    open func imageCachedType(forKey key: String, processorIdentifier identifier: String = "") -> CacheType {
+        let computedKey = key.computedKey(with: identifier)
+        
+        if memoryCache.object(forKey: computedKey as NSString) != nil {
+            return .memory
+        }
+        
+        let filePath = cachePath(forComputedKey: computedKey)
+        
+        var diskCached = false
+        ioQueue.sync {
+            diskCached = fileManager.fileExists(atPath: filePath)
+        }
+        
+        if diskCached {
+            return .disk
+        }
+        
+        return .none
+    }
+    
+    /**
+    Get the hash for the key. This could be used for matching files.
+    
+    - parameter key:        The key which is used for caching.
+    - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it.
+    
+     - returns: Corresponding hash.
+    */
+    open func hash(forKey key: String, processorIdentifier identifier: String = "") -> String {
+        let computedKey = key.computedKey(with: identifier)
+        return cacheFileName(forComputedKey: computedKey)
+    }
+    
+    /**
+    Calculate the disk size taken by cache. 
+    It is the total allocated size of the cached files in bytes.
+    
+    - parameter completionHandler: Called with the calculated size when finishes.
+    */
+    open func calculateDiskCacheSize(completion handler: @escaping ((_ size: UInt) -> Void)) {
+        ioQueue.async {
+            let (_, diskCacheSize, _) = self.travelCachedFiles(onlyForCacheSize: true)
+            DispatchQueue.main.async {
+                handler(diskCacheSize)
+            }
+        }
+    }
+    
+    /**
+    Get the cache path for the key.
+    It is useful for projects with UIWebView or anyone that needs access to the local file path.
+    
+    i.e. Replace the `<img src='path_for_key'>` tag in your HTML.
+     
+    - Note: This method does not guarantee there is an image already cached in the path. It just returns the path
+      that the image should be.
+      You could use `isImageCached(forKey:)` method to check whether the image is cached under that key.
+    */
+    open func cachePath(forKey key: String, processorIdentifier identifier: String = "") -> String {
+        let computedKey = key.computedKey(with: identifier)
+        return cachePath(forComputedKey: computedKey)
+    }
+
+    open func cachePath(forComputedKey key: String) -> String {
+        let fileName = cacheFileName(forComputedKey: key)
+        return (diskCachePath as NSString).appendingPathComponent(fileName)
+    }
+}
+
+// MARK: - Internal Helper
+extension ImageCache {
+  
+    func diskImage(forComputedKey key: String, serializer: CacheSerializer, options: KingfisherOptionsInfo) -> Image? {
+        if let data = diskImageData(forComputedKey: key) {
+            return serializer.image(with: data, options: options)
+        } else {
+            return nil
+        }
+    }
+    
+    func diskImageData(forComputedKey key: String) -> Data? {
+        let filePath = cachePath(forComputedKey: key)
+        return (try? Data(contentsOf: URL(fileURLWithPath: filePath)))
+    }
+    
+    func cacheFileName(forComputedKey key: String) -> String {
+        if let ext = self.pathExtension {
+          return (key.kf.md5 as NSString).appendingPathExtension(ext)!
+        }
+        return key.kf.md5
+    }
+}
+
+// MARK: - Deprecated
+extension ImageCache {
+    /**
+     *  Cache result for checking whether an image is cached for a key.
+     */
+    @available(*, deprecated,
+    message: "CacheCheckResult is deprecated. Use imageCachedType(forKey:processorIdentifier:) API instead.")
+    public struct CacheCheckResult {
+        public let cached: Bool
+        public let cacheType: CacheType?
+    }
+    
+    /**
+     Check whether an image is cached for a key.
+     
+     - parameter key: Key for the image.
+     
+     - returns: The check result.
+     */
+    @available(*, deprecated,
+    message: "Use imageCachedType(forKey:processorIdentifier:) instead. CacheCheckResult.none indicates not being cached.",
+    renamed: "imageCachedType(forKey:processorIdentifier:)")
+    open func isImageCached(forKey key: String, processorIdentifier identifier: String = "") -> CacheCheckResult {
+        let result = imageCachedType(forKey: key, processorIdentifier: identifier)
+        switch result {
+        case .memory, .disk:
+            return CacheCheckResult(cached: true, cacheType: result)
+        case .none:
+            return CacheCheckResult(cached: false, cacheType: nil)
+        }
+    }
+}
+
+extension Kingfisher where Base: Image {
+    var imageCost: Int {
+        return images == nil ?
+            Int(size.height * size.width * scale * scale) :
+            Int(size.height * size.width * scale * scale) * images!.count
+    }
+}
+
+extension Dictionary {
+    func keysSortedByValue(_ isOrderedBefore: (Value, Value) -> Bool) -> [Key] {
+        return Array(self).sorted{ isOrderedBefore($0.1, $1.1) }.map{ $0.0 }
+    }
+}
+
+#if !os(macOS) && !os(watchOS)
+// MARK: - For App Extensions
+extension UIApplication: KingfisherCompatible { }
+extension Kingfisher where Base: UIApplication {
+    public static var shared: UIApplication? {
+        let selector = NSSelectorFromString("sharedApplication")
+        guard Base.responds(to: selector) else { return nil }
+        return Base.perform(selector).takeUnretainedValue() as? UIApplication
+    }
+}
+#endif
+
+extension String {
+    func computedKey(with identifier: String) -> String {
+        if identifier.isEmpty {
+            return self
+        } else {
+            return appending("@\(identifier)")
+        }
+    }
+}

+ 677 - 0
Example/Pods/Kingfisher/Sources/ImageDownloader.swift

@@ -0,0 +1,677 @@
+//
+//  ImageDownloader.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+
+/// Progress update block of downloader.
+public typealias ImageDownloaderProgressBlock = DownloadProgressBlock
+
+/// Completion block of downloader.
+public typealias ImageDownloaderCompletionHandler = ((_ image: Image?, _ error: NSError?, _ url: URL?, _ originalData: Data?) -> Void)
+
+/// Download task.
+public struct RetrieveImageDownloadTask {
+    let internalTask: URLSessionDataTask
+    
+    /// Downloader by which this task is initialized.
+    public private(set) weak var ownerDownloader: ImageDownloader?
+
+    
+    /// Cancel this download task. It will trigger the completion handler with an NSURLErrorCancelled error.
+    /// If you want to cancel all downloading tasks, call `cancelAll()` of `ImageDownloader` instance.
+    public func cancel() {
+        ownerDownloader?.cancel(self)
+    }
+    
+    /// The original request URL of this download task.
+    public var url: URL? {
+        return internalTask.originalRequest?.url
+    }
+    
+    /// The relative priority of this download task. 
+    /// It represents the `priority` property of the internal `NSURLSessionTask` of this download task.
+    /// The value for it is between 0.0~1.0. Default priority is value of 0.5.
+    /// See documentation on `priority` of `NSURLSessionTask` for more about it.
+    public var priority: Float {
+        get {
+            return internalTask.priority
+        }
+        set {
+            internalTask.priority = newValue
+        }
+    }
+}
+
+///The code of errors which `ImageDownloader` might encountered.
+public enum KingfisherError: Int {
+    
+    /// badData: The downloaded data is not an image or the data is corrupted.
+    case badData = 10000
+    
+    /// notModified: The remote server responded a 304 code. No image data downloaded.
+    case notModified = 10001
+    
+    /// The HTTP status code in response is not valid. If an invalid
+    /// code error received, you could check the value under `KingfisherErrorStatusCodeKey` 
+    /// in `userInfo` to see the code.
+    case invalidStatusCode = 10002
+    
+    /// notCached: The image requested is not in cache but .onlyFromCache is activated.
+    case notCached = 10003
+    
+    /// The URL is invalid.
+    case invalidURL = 20000
+    
+    /// The downloading task is cancelled before started.
+    case downloadCancelledBeforeStarting = 30000
+}
+
+/// Key will be used in the `userInfo` of `.invalidStatusCode`
+public let KingfisherErrorStatusCodeKey = "statusCode"
+
+/// Protocol of `ImageDownloader`.
+public protocol ImageDownloaderDelegate: AnyObject {
+    /**
+     Called when the `ImageDownloader` object will start downloading an image from specified URL.
+     
+     - parameter downloader: The `ImageDownloader` object finishes the downloading.
+     - parameter url:        URL of the original request URL.
+     - parameter response:   The request object for the download process.
+     */
+    func imageDownloader(_ downloader: ImageDownloader, willDownloadImageForURL url: URL, with request: URLRequest?)
+    
+    /**
+     Called when the `ImageDownloader` completes a downloading request with success or failure.
+     
+     - parameter downloader: The `ImageDownloader` object finishes the downloading.
+     - parameter url:        URL of the original request URL.
+     - parameter response:   The response object of the downloading process.
+     - parameter error:      The error in case of failure.
+     */
+    func imageDownloader(_ downloader: ImageDownloader, didFinishDownloadingImageForURL url: URL, with response: URLResponse?, error: Error?)
+    
+    /**
+    Called when the `ImageDownloader` object successfully downloaded an image from specified URL.
+    
+    - parameter downloader: The `ImageDownloader` object finishes the downloading.
+    - parameter image:      Downloaded image.
+    - parameter url:        URL of the original request URL.
+    - parameter response:   The response object of the downloading process.
+    */
+    func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?)
+    
+    /**
+    Check if a received HTTP status code is valid or not. 
+    By default, a status code between 200 to 400 (excluded) is considered as valid.
+    If an invalid code is received, the downloader will raise an .invalidStatusCode error.
+    It has a `userInfo` which includes this statusCode and localizedString error message.
+     
+    - parameter code: The received HTTP status code.
+    - parameter downloader: The `ImageDownloader` object asking for validate status code.
+     
+    - returns: Whether this HTTP status code is valid or not.
+     
+    - Note: If the default 200 to 400 valid code does not suit your need, 
+            you can implement this method to change that behavior.
+    */
+    func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool
+    
+    /**
+     Called when the `ImageDownloader` object successfully downloaded image data from specified URL.
+     
+     - parameter downloader: The `ImageDownloader` object finishes data downloading.
+     - parameter data:       Downloaded data.
+     - parameter url:        URL of the original request URL.
+     
+     - returns: The data from which Kingfisher should use to create an image.
+     
+     - Note: This callback can be used to preprocess raw image data
+             before creation of UIImage instance (i.e. decrypting or verification).
+     */
+    func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data?
+}
+
+extension ImageDownloaderDelegate {
+    
+    public func imageDownloader(_ downloader: ImageDownloader, willDownloadImageForURL url: URL, with request: URLRequest?) {}
+    
+    public func imageDownloader(_ downloader: ImageDownloader, didFinishDownloadingImageForURL url: URL, with response: URLResponse?, error: Error?) {}
+    
+    public func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?) {}
+    
+    public func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool {
+        return (200..<400).contains(code)
+    }
+    public func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? {
+        return data
+    }
+}
+
+/// Protocol indicates that an authentication challenge could be handled.
+public protocol AuthenticationChallengeResponsable: AnyObject {
+    /**
+     Called when an session level authentication challenge is received.
+     This method provide a chance to handle and response to the authentication challenge before downloading could start.
+     
+     - parameter downloader:        The downloader which receives this challenge.
+     - parameter challenge:         An object that contains the request for authentication.
+     - parameter completionHandler: A handler that your delegate method must call.
+     
+     - Note: This method is a forward from `URLSessionDelegate.urlSession(:didReceiveChallenge:completionHandler:)`. Please refer to the document of it in `URLSessionDelegate`.
+     */
+    func downloader(_ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
+
+    /**
+     Called when an session level authentication challenge is received.
+     This method provide a chance to handle and response to the authentication challenge before downloading could start.
+     
+     - parameter downloader:        The downloader which receives this challenge.
+     - parameter task:              The task whose request requires authentication.
+     - parameter challenge:         An object that contains the request for authentication.
+     - parameter completionHandler: A handler that your delegate method must call.
+     
+     - Note: This method is a forward from `URLSessionTaskDelegate.urlSession(:task:didReceiveChallenge:completionHandler:)`. Please refer to the document of it in `URLSessionTaskDelegate`.
+     */
+    func downloader(_ downloader: ImageDownloader, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
+}
+
+extension AuthenticationChallengeResponsable {
+    
+    func downloader(_ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+    
+        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
+            if let trustedHosts = downloader.trustedHosts, trustedHosts.contains(challenge.protectionSpace.host) {
+                let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
+                completionHandler(.useCredential, credential)
+                return
+            }
+        }
+        
+        completionHandler(.performDefaultHandling, nil)
+    }
+    
+    func downloader(_ downloader: ImageDownloader, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+        
+        completionHandler(.performDefaultHandling, nil)
+    }
+
+}
+
+/// `ImageDownloader` represents a downloading manager for requesting the image with a URL from server.
+open class ImageDownloader {
+    
+    class ImageFetchLoad {
+        var contents = [(callback: CallbackPair, options: KingfisherOptionsInfo)]()
+        var responseData = NSMutableData()
+
+        var downloadTaskCount = 0
+        var downloadTask: RetrieveImageDownloadTask?
+        var cancelSemaphore: DispatchSemaphore?
+    }
+    
+    // MARK: - Public property
+    /// The duration before the download is timeout. Default is 15 seconds.
+    open var downloadTimeout: TimeInterval = 15.0
+    
+    /// A set of trusted hosts when receiving server trust challenges. A challenge with host name contained in this set will be ignored. 
+    /// You can use this set to specify the self-signed site. It only will be used if you don't specify the `authenticationChallengeResponder`. 
+    /// If `authenticationChallengeResponder` is set, this property will be ignored and the implementation of `authenticationChallengeResponder` will be used instead.
+    open var trustedHosts: Set<String>?
+    
+    /// Use this to set supply a configuration for the downloader. By default, NSURLSessionConfiguration.ephemeralSessionConfiguration() will be used. 
+    /// You could change the configuration before a downloading task starts. A configuration without persistent storage for caches is requested for downloader working correctly.
+    open var sessionConfiguration = URLSessionConfiguration.ephemeral {
+        didSet {
+            session?.invalidateAndCancel()
+            session = URLSession(configuration: sessionConfiguration, delegate: sessionHandler, delegateQueue: nil)
+        }
+    }
+    
+    /// Whether the download requests should use pipline or not. Default is false.
+    open var requestsUsePipelining = false
+    
+    fileprivate let sessionHandler: ImageDownloaderSessionHandler
+    fileprivate var session: URLSession?
+    
+    /// Delegate of this `ImageDownloader` object. See `ImageDownloaderDelegate` protocol for more.
+    open weak var delegate: ImageDownloaderDelegate?
+    
+    /// A responder for authentication challenge. 
+    /// Downloader will forward the received authentication challenge for the downloading session to this responder.
+    open weak var authenticationChallengeResponder: AuthenticationChallengeResponsable?
+    
+    // MARK: - Internal property
+    let barrierQueue: DispatchQueue
+    let processQueue: DispatchQueue
+    let cancelQueue: DispatchQueue
+    
+    typealias CallbackPair = (progressBlock: ImageDownloaderProgressBlock?, completionHandler: ImageDownloaderCompletionHandler?)
+    
+    var fetchLoads = [URL: ImageFetchLoad]()
+    
+    // MARK: - Public method
+    /// The default downloader.
+    public static let `default` = ImageDownloader(name: "default")
+    
+    /**
+    Init a downloader with name.
+    
+    - parameter name: The name for the downloader. It should not be empty.
+    */
+    public init(name: String) {
+        if name.isEmpty {
+            fatalError("[Kingfisher] You should specify a name for the downloader. A downloader with empty name is not permitted.")
+        }
+        
+        barrierQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Barrier.\(name)", attributes: .concurrent)
+        processQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Process.\(name)", attributes: .concurrent)
+        cancelQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Cancel.\(name)")
+        
+        sessionHandler = ImageDownloaderSessionHandler(name: name)
+
+        // Provide a default implement for challenge responder.
+        authenticationChallengeResponder = sessionHandler
+        session = URLSession(configuration: sessionConfiguration, delegate: sessionHandler, delegateQueue: .main)
+    }
+    
+    deinit {
+        session?.invalidateAndCancel()
+    }
+    
+    func fetchLoad(for url: URL) -> ImageFetchLoad? {
+        var fetchLoad: ImageFetchLoad?
+        barrierQueue.sync(flags: .barrier) { fetchLoad = fetchLoads[url] }
+        return fetchLoad
+    }
+    
+    /**
+     Download an image with a URL and option.
+     
+     - parameter url:               Target URL.
+     - parameter retrieveImageTask: The task to cooperate with cache. Pass `nil` if you are not trying to use downloader and cache.
+     - parameter options:           The options could control download behavior. See `KingfisherOptionsInfo`.
+     - parameter progressBlock:     Called when the download progress updated.
+     - parameter completionHandler: Called when the download progress finishes.
+     
+     - returns: A downloading task. You could call `cancel` on it to stop the downloading process.
+     */
+    @discardableResult
+    open func downloadImage(with url: URL,
+                       retrieveImageTask: RetrieveImageTask? = nil,
+                       options: KingfisherOptionsInfo? = nil,
+                       progressBlock: ImageDownloaderProgressBlock? = nil,
+                       completionHandler: ImageDownloaderCompletionHandler? = nil) -> RetrieveImageDownloadTask?
+    {
+        if let retrieveImageTask = retrieveImageTask, retrieveImageTask.cancelledBeforeDownloadStarting {
+            completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.downloadCancelledBeforeStarting.rawValue, userInfo: nil), nil, nil)
+            return nil
+        }
+        
+        let timeout = self.downloadTimeout == 0.0 ? 15.0 : self.downloadTimeout
+        
+        // We need to set the URL as the load key. So before setup progress, we need to ask the `requestModifier` for a final URL.
+        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: timeout)
+        request.httpShouldUsePipelining = requestsUsePipelining
+
+        if let modifier = options?.modifier {
+            guard let r = modifier.modified(for: request) else {
+                completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.downloadCancelledBeforeStarting.rawValue, userInfo: nil), nil, nil)
+                return nil
+            }
+            request = r
+        }
+        
+        // There is a possibility that request modifier changed the url to `nil` or empty.
+        guard let url = request.url, !url.absoluteString.isEmpty else {
+            completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.invalidURL.rawValue, userInfo: nil), nil, nil)
+            return nil
+        }
+        
+        var downloadTask: RetrieveImageDownloadTask?
+        setup(progressBlock: progressBlock, with: completionHandler, for: url, options: options) {(session, fetchLoad) -> Void in
+            if fetchLoad.downloadTask == nil {
+                let dataTask = session.dataTask(with: request)
+                
+                fetchLoad.downloadTask = RetrieveImageDownloadTask(internalTask: dataTask, ownerDownloader: self)
+                
+                dataTask.priority = options?.downloadPriority ?? URLSessionTask.defaultPriority
+                self.delegate?.imageDownloader(self, willDownloadImageForURL: url, with: request)
+                dataTask.resume()
+                
+                // Hold self while the task is executing.
+                self.sessionHandler.downloadHolder = self
+            }
+            
+            fetchLoad.downloadTaskCount += 1
+            downloadTask = fetchLoad.downloadTask
+            
+            retrieveImageTask?.downloadTask = downloadTask
+        }
+        return downloadTask
+    }
+    
+}
+
+// MARK: - Download method
+extension ImageDownloader {
+    
+    // A single key may have multiple callbacks. Only download once.
+    func setup(progressBlock: ImageDownloaderProgressBlock?, with completionHandler: ImageDownloaderCompletionHandler?, for url: URL, options: KingfisherOptionsInfo?, started: @escaping ((URLSession, ImageFetchLoad) -> Void)) {
+
+        func prepareFetchLoad() {
+            barrierQueue.sync(flags: .barrier) {
+                let loadObjectForURL = fetchLoads[url] ?? ImageFetchLoad()
+                let callbackPair = (progressBlock: progressBlock, completionHandler: completionHandler)
+                
+                loadObjectForURL.contents.append((callbackPair, options ?? KingfisherEmptyOptionsInfo))
+                
+                fetchLoads[url] = loadObjectForURL
+                
+                if let session = session {
+                    started(session, loadObjectForURL)
+                }
+            }
+        }
+        
+        if let fetchLoad = fetchLoad(for: url), fetchLoad.downloadTaskCount == 0 {
+            if fetchLoad.cancelSemaphore == nil {
+                fetchLoad.cancelSemaphore = DispatchSemaphore(value: 0)
+            }
+            cancelQueue.async {
+                _ = fetchLoad.cancelSemaphore?.wait(timeout: .distantFuture)
+                fetchLoad.cancelSemaphore = nil
+                prepareFetchLoad()
+            }
+        } else {
+            prepareFetchLoad()
+        }
+    }
+    
+    private func cancelTaskImpl(_ task: RetrieveImageDownloadTask, fetchLoad: ImageFetchLoad? = nil, ignoreTaskCount: Bool = false) {
+        
+        func getFetchLoad(from task: RetrieveImageDownloadTask) -> ImageFetchLoad? {
+            guard let URL = task.internalTask.originalRequest?.url,
+                  let imageFetchLoad = self.fetchLoads[URL] else
+            {
+                return nil
+            }
+            return imageFetchLoad
+        }
+        
+        guard let imageFetchLoad = fetchLoad ?? getFetchLoad(from: task) else {
+            return
+        }
+
+        imageFetchLoad.downloadTaskCount -= 1
+        if ignoreTaskCount || imageFetchLoad.downloadTaskCount == 0 {
+            task.internalTask.cancel()
+        }
+    }
+    
+    func cancel(_ task: RetrieveImageDownloadTask) {
+        barrierQueue.sync(flags: .barrier) { cancelTaskImpl(task) }
+    }
+    
+    /// Cancel all downloading tasks. It will trigger the completion handlers for all not-yet-finished
+    /// downloading tasks with an NSURLErrorCancelled error.
+    ///
+    /// If you need to only cancel a certain task, call `cancel()` on the `RetrieveImageDownloadTask`
+    /// returned by the downloading methods.
+    public func cancelAll() {
+        barrierQueue.sync(flags: .barrier) {
+            fetchLoads.forEach { v in
+                let fetchLoad = v.value
+                guard let task = fetchLoad.downloadTask else { return }
+                cancelTaskImpl(task, fetchLoad: fetchLoad, ignoreTaskCount: true)
+            }
+        }
+    }
+}
+
+// MARK: - NSURLSessionDataDelegate
+
+/// Delegate class for `NSURLSessionTaskDelegate`.
+/// The session object will hold its delegate until it gets invalidated.
+/// If we use `ImageDownloader` as the session delegate, it will not be released.
+/// So we need an additional handler to break the retain cycle.
+// See https://github.com/onevcat/Kingfisher/issues/235
+final class ImageDownloaderSessionHandler: NSObject, URLSessionDataDelegate, AuthenticationChallengeResponsable {
+
+    private let downloaderQueue: DispatchQueue
+
+    // The holder will keep downloader not released while a data task is being executed.
+    // It will be set when the task started, and reset when the task finished.
+    private var _downloadHolder: ImageDownloader?
+    var downloadHolder: ImageDownloader? {
+        get {
+            return downloaderQueue.sync { _downloadHolder }
+        }
+        set {
+            downloaderQueue.sync { _downloadHolder = newValue }
+        }
+    }
+
+    init(name: String) {
+        downloaderQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.SessionHandler.\(name)")
+        super.init()
+    }
+    
+    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
+        
+        guard let downloader = downloadHolder else {
+            completionHandler(.cancel)
+            return
+        }
+        
+        var disposition = URLSession.ResponseDisposition.allow
+        
+        if let statusCode = (response as? HTTPURLResponse)?.statusCode,
+           let url = dataTask.originalRequest?.url,
+            !(downloader.delegate ?? downloader).isValidStatusCode(statusCode, for: downloader)
+        {
+            let error = NSError(domain: KingfisherErrorDomain,
+                                code: KingfisherError.invalidStatusCode.rawValue,
+                                userInfo: [KingfisherErrorStatusCodeKey: statusCode, NSLocalizedDescriptionKey: HTTPURLResponse.localizedString(forStatusCode: statusCode)])
+            
+            // Needs to be called before callCompletionHandlerFailure() because it removes downloadHolder
+            if let downloader = downloadHolder {
+                downloader.delegate?.imageDownloader(downloader, didFinishDownloadingImageForURL: url, with: response, error: error)
+            }
+            
+            callCompletionHandlerFailure(error: error, url: url)
+            disposition = .cancel
+        }
+        
+        completionHandler(disposition)
+    }
+    
+    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
+
+        guard let downloader = downloadHolder else {
+            return
+        }
+
+        if let url = dataTask.originalRequest?.url, let fetchLoad = downloader.fetchLoad(for: url) {
+            fetchLoad.responseData.append(data)
+            
+            if let expectedLength = dataTask.response?.expectedContentLength {
+                for content in fetchLoad.contents {
+                    DispatchQueue.main.async {
+                        content.callback.progressBlock?(Int64(fetchLoad.responseData.length), expectedLength)
+                    }
+                }
+            }
+        }
+    }
+    
+    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
+        
+        guard let url = task.originalRequest?.url else {
+            return
+        }
+        
+        if let downloader = downloadHolder {
+            downloader.delegate?.imageDownloader(downloader, didFinishDownloadingImageForURL: url, with: task.response, error: error)
+        }
+        
+        guard error == nil else {
+            callCompletionHandlerFailure(error: error!, url: url)
+            return
+        }
+        
+        processImage(for: task, url: url)
+    }
+    
+    /**
+    This method is exposed since the compiler requests. Do not call it.
+    */
+    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+        guard let downloader = downloadHolder else {
+            return
+        }
+        
+        downloader.authenticationChallengeResponder?.downloader(downloader, didReceive: challenge, completionHandler: completionHandler)
+    }
+    
+    func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+        guard let downloader = downloadHolder else {
+            return
+        }
+        
+        downloader.authenticationChallengeResponder?.downloader(downloader, task: task, didReceive: challenge, completionHandler: completionHandler)
+    }
+    
+    private func cleanFetchLoad(for url: URL) {
+        guard let downloader = downloadHolder else {
+            return
+        }
+
+        downloader.barrierQueue.sync(flags: .barrier) {
+            downloader.fetchLoads.removeValue(forKey: url)
+            if downloader.fetchLoads.isEmpty {
+                downloadHolder = nil
+            }
+        }
+    }
+    
+    private func callCompletionHandlerFailure(error: Error, url: URL) {
+        guard let downloader = downloadHolder, let fetchLoad = downloader.fetchLoad(for: url) else {
+            return
+        }
+        
+        // We need to clean the fetch load first, before actually calling completion handler.
+        cleanFetchLoad(for: url)
+        
+        var leftSignal: Int
+        repeat {
+            leftSignal = fetchLoad.cancelSemaphore?.signal() ?? 0
+        } while leftSignal != 0
+        
+        for content in fetchLoad.contents {
+            content.options.callbackDispatchQueue.safeAsync {
+                content.callback.completionHandler?(nil, error as NSError, url, nil)
+            }
+        }
+    }
+    
+    private func processImage(for task: URLSessionTask, url: URL) {
+
+        guard let downloader = downloadHolder else {
+            return
+        }
+        
+        // We are on main queue when receiving this.
+        downloader.processQueue.async {
+            
+            guard let fetchLoad = downloader.fetchLoad(for: url) else {
+                return
+            }
+            
+            self.cleanFetchLoad(for: url)
+            
+            let data: Data?
+            let fetchedData = fetchLoad.responseData as Data
+            
+            if let delegate = downloader.delegate {
+                data = delegate.imageDownloader(downloader, didDownload: fetchedData, for: url)
+            } else {
+                data = fetchedData
+            }
+            
+            // Cache the processed images. So we do not need to re-process the image if using the same processor.
+            // Key is the identifier of processor.
+            var imageCache: [String: Image] = [:]
+            for content in fetchLoad.contents {
+                
+                let options = content.options
+                let completionHandler = content.callback.completionHandler
+                let callbackQueue = options.callbackDispatchQueue
+                
+                let processor = options.processor
+                var image = imageCache[processor.identifier]
+                if let data = data, image == nil {
+                    image = processor.process(item: .data(data), options: options)
+                    // Add the processed image to cache. 
+                    // If `image` is nil, nothing will happen (since the key is not existing before).
+                    imageCache[processor.identifier] = image
+                }
+                
+                if let image = image {
+
+                    downloader.delegate?.imageDownloader(downloader, didDownload: image, for: url, with: task.response)
+
+                    let imageModifier = options.imageModifier
+                    let finalImage = imageModifier.modify(image)
+
+                    if options.backgroundDecode {
+                        let decodedImage = finalImage.kf.decoded
+                        callbackQueue.safeAsync { completionHandler?(decodedImage, nil, url, data) }
+                    } else {
+                        callbackQueue.safeAsync { completionHandler?(finalImage, nil, url, data) }
+                    }
+                    
+                } else {
+                    if let res = task.response as? HTTPURLResponse , res.statusCode == 304 {
+                        let notModified = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notModified.rawValue, userInfo: nil)
+                        completionHandler?(nil, notModified, url, nil)
+                        continue
+                    }
+                    
+                    let badData = NSError(domain: KingfisherErrorDomain, code: KingfisherError.badData.rawValue, userInfo: nil)
+                    callbackQueue.safeAsync { completionHandler?(nil, badData, url, nil) }
+                }
+            }
+        }
+    }
+}
+
+// Placeholder. For retrieving extension methods of ImageDownloaderDelegate
+extension ImageDownloader: ImageDownloaderDelegate {}
+

+ 191 - 0
Example/Pods/Kingfisher/Sources/ImageModifier.swift

@@ -0,0 +1,191 @@
+//
+//  ImageModifier.swift
+//  Kingfisher
+//
+//  Created by Ethan Gill on 2017/11/28.
+//
+//  Copyright (c) 2018 Ethan Gill <ethan.gill@me.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+/// An `ImageModifier` can be used to change properties on an Image in between
+/// cache serialization and use of the image.
+public protocol ImageModifier {
+    /// Modify an input `Image`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: The return value will be unmodified if modifying is not possible on
+    ///         the current platform.
+    /// - Note: Most modifiers support UIImage or NSImage, but not CGImage.
+    func modify(_ image: Image) -> Image
+}
+
+extension ImageModifier {
+    func modify(_ image: Image?) -> Image? {
+        guard let image = image else {
+            return nil
+        }
+        return modify(image)
+    }
+}
+
+typealias ModifierImp = ((Image) -> Image)
+
+fileprivate struct GeneralModifier: ImageModifier {
+    let identifier: String
+    let m: ModifierImp
+    func modify(_ image: Image) -> Image {
+        return m(image)
+    }
+}
+
+/// The default modifier.
+/// Does nothing and returns the image it was given
+public struct DefaultImageModifier: ImageModifier {
+
+    /// A default `DefaultImageModifier` which can be used everywhere.
+    public static let `default` = DefaultImageModifier()
+
+    /// Initialize a `DefaultImageModifier`
+    private init() {}
+
+    /// Modify an input `Image`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: See documentation of `ImageModifier` protocol for more.
+    public func modify(_ image: Image) -> Image {
+        return image
+    }
+}
+
+/// A custom modifier.
+/// Can be initialized with a block to modify images in a custom way
+public struct AnyImageModifier: ImageModifier {
+
+    /// A block which modifies images, or returns the original image
+    /// if modification cannot be performed.
+    let block: (Image) -> Image
+
+    /// Initialize an `AnyImageModifier`
+    public init(modify: @escaping (Image) -> Image) {
+        block = modify
+    }
+
+    /// Modifies an input `Image` using this `AnyImageModifier`'s `block`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: See documentation of `ImageModifier` protocol for more.
+    public func modify(_ image: Image) -> Image {
+        return block(image)
+    }
+}
+
+#if os(iOS) || os(tvOS) || os(watchOS)
+import UIKit
+
+/// Modifier for setting the rendering mode of images.
+/// Only UI-based images are supported; if a non-UI image is passed in, the
+/// modifier will do nothing.
+public struct RenderingModeImageModifier: ImageModifier {
+
+    /// The rendering mode to apply to the image.
+    public let renderingMode: UIImage.RenderingMode
+
+    /// Initialize a `RenderingModeImageModifier`
+    ///
+    /// - parameter renderingMode: The rendering mode to apply to the image.
+    ///                            Default is .automatic
+    public init(renderingMode: UIImage.RenderingMode = .automatic) {
+        self.renderingMode = renderingMode
+    }
+
+    /// Modify an input `Image`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: See documentation of `ImageModifier` protocol for more.
+    public func modify(_ image: Image) -> Image {
+        return image.withRenderingMode(renderingMode)
+    }
+}
+
+/// Modifier for setting the `flipsForRightToLeftLayoutDirection` property of images.
+/// Only UI-based images are supported; if a non-UI image is passed in, the
+/// modifier will do nothing.
+public struct FlipsForRightToLeftLayoutDirectionImageModifier: ImageModifier {
+    /// Initialize a `FlipsForRightToLeftLayoutDirectionImageModifier`
+    ///
+    /// - Note: On versions of iOS lower than 9.0, the image will be returned
+    ///         unmodified.
+    public init() {}
+
+    /// Modify an input `Image`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: See documentation of `ImageModifier` protocol for more.
+    public func modify(_ image: Image) -> Image {
+        if #available(iOS 9.0, *) {
+            return image.imageFlippedForRightToLeftLayoutDirection()
+        } else {
+            return image
+        }
+    }
+}
+
+/// Modifier for setting the `alignmentRectInsets` property of images.
+/// Only UI-based images are supported; if a non-UI image is passed in, the
+/// modifier will do nothing.
+public struct AlignmentRectInsetsImageModifier: ImageModifier {
+
+    /// The alignment insets to apply to the image
+    public let alignmentInsets: UIEdgeInsets
+
+    /// Initialize a `AlignmentRectInsetsImageModifier`
+    public init(alignmentInsets: UIEdgeInsets) {
+        self.alignmentInsets = alignmentInsets
+    }
+
+    /// Modify an input `Image`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: See documentation of `ImageModifier` protocol for more.
+    public func modify(_ image: Image) -> Image {
+        return image.withAlignmentRectInsets(alignmentInsets)
+    }
+}
+#endif

+ 277 - 0
Example/Pods/Kingfisher/Sources/ImagePrefetcher.swift

@@ -0,0 +1,277 @@
+//
+//  ImagePrefetcher.swift
+//  Kingfisher
+//
+//  Created by Claire Knight <claire.knight@moggytech.co.uk> on 24/02/2016
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+
+#if os(macOS)
+    import AppKit
+#else
+    import UIKit
+#endif
+
+
+/// Progress update block of prefetcher. 
+///
+/// - `skippedResources`: An array of resources that are already cached before the prefetching starting.
+/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all.
+/// - `completedResources`: An array of resources that are downloaded and cached successfully.
+public typealias PrefetcherProgressBlock = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void)
+
+/// Completion block of prefetcher.
+///
+/// - `skippedResources`: An array of resources that are already cached before the prefetching starting.
+/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all.
+/// - `completedResources`: An array of resources that are downloaded and cached successfully.
+public typealias PrefetcherCompletionHandler = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void)
+
+/// `ImagePrefetcher` represents a downloading manager for requesting many images via URLs, then caching them.
+/// This is useful when you know a list of image resources and want to download them before showing.
+public class ImagePrefetcher {
+    
+    /// The maximum concurrent downloads to use when prefetching images. Default is 5.
+    public var maxConcurrentDownloads = 5
+    
+    /// The dispatch queue to use for handling resource process so downloading does not occur on the main thread
+    /// This prevents stuttering when preloading images in a collection view or table view
+    private var prefetchQueue: DispatchQueue
+    
+    private let prefetchResources: [Resource]
+    private let optionsInfo: KingfisherOptionsInfo
+    private var progressBlock: PrefetcherProgressBlock?
+    private var completionHandler: PrefetcherCompletionHandler?
+    
+    private var tasks = [URL: RetrieveImageDownloadTask]()
+    
+    private var pendingResources: ArraySlice<Resource>
+    private var skippedResources = [Resource]()
+    private var completedResources = [Resource]()
+    private var failedResources = [Resource]()
+    
+    private var stopped = false
+    
+    // The created manager used for prefetch. We will use the helper method in manager.
+    private let manager: KingfisherManager
+    
+    private var finished: Bool {
+        return failedResources.count + skippedResources.count + completedResources.count == prefetchResources.count && self.tasks.isEmpty
+    }
+    
+    /**
+     Init an image prefetcher with an array of URLs.
+     
+     The prefetcher should be initiated with a list of prefetching targets. The URLs list is immutable. 
+     After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process.
+     The images already cached will be skipped without downloading again.
+     
+     - parameter urls:              The URLs which should be prefetched.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called every time an resource is downloaded, skipped or cancelled.
+     - parameter completionHandler: Called when the whole prefetching process finished.
+     
+     - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as 
+     the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`.
+     Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method.
+     */
+    public convenience init(urls: [URL],
+                         options: KingfisherOptionsInfo? = nil,
+                   progressBlock: PrefetcherProgressBlock? = nil,
+               completionHandler: PrefetcherCompletionHandler? = nil)
+    {
+        let resources: [Resource] = urls.map { $0 }
+        self.init(resources: resources, options: options, progressBlock: progressBlock, completionHandler: completionHandler)
+    }
+    
+    /**
+     Init an image prefetcher with an array of resources.
+     
+     The prefetcher should be initiated with a list of prefetching targets. The resources list is immutable.
+     After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process.
+     The images already cached will be skipped without downloading again.
+     
+     - parameter resources:         The resources which should be prefetched. See `Resource` type for more.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called every time an resource is downloaded, skipped or cancelled.
+     - parameter completionHandler: Called when the whole prefetching process finished.
+
+     - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as
+     the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`.
+     Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method.
+     */
+    public init(resources: [Resource],
+                  options: KingfisherOptionsInfo? = nil,
+            progressBlock: PrefetcherProgressBlock? = nil,
+        completionHandler: PrefetcherCompletionHandler? = nil)
+    {
+        prefetchResources = resources
+        pendingResources = ArraySlice(resources)
+        
+        // Set up the dispatch queue that all our work should occur on.
+        let prefetchQueueName = "com.onevcat.Kingfisher.PrefetchQueue"
+        prefetchQueue = DispatchQueue(label: prefetchQueueName)
+        
+        // We want all callbacks from our prefetch queue, so we should ignore the call back queue in options
+        var optionsInfoWithoutQueue = options?.removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil)) ?? KingfisherEmptyOptionsInfo
+        
+        // Add our own callback dispatch queue to make sure all callbacks are coming back in our expected queue
+        optionsInfoWithoutQueue.append(.callbackDispatchQueue(prefetchQueue))
+        
+        self.optionsInfo = optionsInfoWithoutQueue
+        
+        let cache = self.optionsInfo.targetCache ?? .default
+        let downloader = self.optionsInfo.downloader ?? .default
+        manager = KingfisherManager(downloader: downloader, cache: cache)
+        
+        self.progressBlock = progressBlock
+        self.completionHandler = completionHandler
+    }
+    
+    /**
+     Start to download the resources and cache them. This can be useful for background downloading
+     of assets that are required for later use in an app. This code will not try and update any UI
+     with the results of the process.
+     */
+    public func start()
+    {
+        // Since we want to handle the resources cancellation in the prefetch queue only.
+        prefetchQueue.async {
+            
+            guard !self.stopped else {
+                assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.")
+                self.handleComplete()
+                return
+            }
+            
+            guard self.maxConcurrentDownloads > 0 else {
+                assertionFailure("There should be concurrent downloads value should be at least 1.")
+                self.handleComplete()
+                return
+            }
+            
+            guard self.prefetchResources.count > 0 else {
+                self.handleComplete()
+                return
+            }
+            
+            let initialConcurentDownloads = min(self.prefetchResources.count, self.maxConcurrentDownloads)
+            for _ in 0 ..< initialConcurentDownloads {
+                if let resource = self.pendingResources.popFirst() {
+                    self.startPrefetching(resource)
+                }
+            }
+        }
+    }
+
+   
+    /**
+     Stop current downloading progress, and cancel any future prefetching activity that might be occuring.
+     */
+    public func stop() {
+        prefetchQueue.async {
+            if self.finished { return }
+            self.stopped = true
+            self.tasks.values.forEach { $0.cancel() }
+        }
+    }
+    
+    func downloadAndCache(_ resource: Resource) {
+
+        let downloadTaskCompletionHandler: CompletionHandler = { (image, error, _, _) -> Void in
+            self.tasks.removeValue(forKey: resource.downloadURL)
+            if let _ = error {
+                self.failedResources.append(resource)
+            } else {
+                self.completedResources.append(resource)
+            }
+            
+            self.reportProgress()
+            if self.stopped {
+                if self.tasks.isEmpty {
+                    self.failedResources.append(contentsOf: self.pendingResources)
+                    self.handleComplete()
+                }
+            } else {
+                self.reportCompletionOrStartNext()
+            }
+        }
+        
+        let downloadTask = manager.downloadAndCacheImage(
+            with: resource.downloadURL,
+            forKey: resource.cacheKey,
+            retrieveImageTask: RetrieveImageTask(),
+            progressBlock: nil,
+            completionHandler: downloadTaskCompletionHandler,
+            options: optionsInfo)
+        
+        if let downloadTask = downloadTask {
+            tasks[resource.downloadURL] = downloadTask
+        }
+    }
+    
+    func append(cached resource: Resource) {
+        skippedResources.append(resource)
+ 
+        reportProgress()
+        reportCompletionOrStartNext()
+    }
+    
+    func startPrefetching(_ resource: Resource)
+    {
+        if optionsInfo.forceRefresh {
+            downloadAndCache(resource)
+        } else {
+            let alreadyInCache = manager.cache.imageCachedType(forKey: resource.cacheKey,
+                                                             processorIdentifier: optionsInfo.processor.identifier).cached
+            if alreadyInCache {
+                append(cached: resource)
+            } else {
+                downloadAndCache(resource)
+            }
+        }
+    }
+    
+    func reportProgress() {
+        progressBlock?(skippedResources, failedResources, completedResources)
+    }
+    
+    func reportCompletionOrStartNext() {
+        prefetchQueue.async {
+            if let resource = self.pendingResources.popFirst() {
+                self.startPrefetching(resource)
+            } else {
+                guard self.tasks.isEmpty else { return }
+                self.handleComplete()
+            }
+        }
+    }
+    
+    func handleComplete() {
+        // The completion handler should be called on the main thread
+        DispatchQueue.main.safeAsync {
+            self.completionHandler?(self.skippedResources, self.failedResources, self.completedResources)
+            self.completionHandler = nil
+            self.progressBlock = nil
+        }
+    }
+}

+ 713 - 0
Example/Pods/Kingfisher/Sources/ImageProcessor.swift

@@ -0,0 +1,713 @@
+//
+//  ImageProcessor.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2016/08/26.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+import CoreGraphics
+
+#if os(macOS)
+import AppKit
+#endif
+
+/// The item which could be processed by an `ImageProcessor`
+///
+/// - image: Input image
+/// - data:  Input data
+public enum ImageProcessItem {
+    case image(Image)
+    case data(Data)
+}
+
+/// An `ImageProcessor` would be used to convert some downloaded data to an image.
+public protocol ImageProcessor {
+    /// Identifier of the processor. It will be used to identify the processor when 
+    /// caching and retrieving an image. You might want to make sure that processors with
+    /// same properties/functionality have the same identifiers, so correct processed images
+    /// could be retrieved with proper key.
+    /// 
+    /// - Note: Do not supply an empty string for a customized processor, which is already taken by
+    /// the `DefaultImageProcessor`. It is recommended to use a reverse domain name notation
+    /// string of your own for the identifier.
+    var identifier: String { get }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: The return value will be `nil` if processing failed while converting data to image.
+    ///         If input item is already an image and there is any errors in processing, the input 
+    ///         image itself will be returned.
+    /// - Note: Most processor only supports CG-based images. 
+    ///         watchOS is not supported for processors containing filter, the input image will be returned directly on watchOS.
+    func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image?
+}
+
+typealias ProcessorImp = ((ImageProcessItem, KingfisherOptionsInfo) -> Image?)
+
+public extension ImageProcessor {
+    
+    /// Append an `ImageProcessor` to another. The identifier of the new `ImageProcessor` 
+    /// will be "\(self.identifier)|>\(another.identifier)".
+    ///
+    /// - parameter another: An `ImageProcessor` you want to append to `self`.
+    ///
+    /// - returns: The new `ImageProcessor` will process the image in the order
+    ///            of the two processors concatenated.
+    public func append(another: ImageProcessor) -> ImageProcessor {
+        let newIdentifier = identifier.appending("|>\(another.identifier)")
+        return GeneralProcessor(identifier: newIdentifier) {
+            item, options in
+            if let image = self.process(item: item, options: options) {
+                return another.process(item: .image(image), options: options)
+            } else {
+                return nil
+            }
+        }
+    }
+}
+
+func ==(left: ImageProcessor, right: ImageProcessor) -> Bool {
+    return left.identifier == right.identifier
+}
+
+func !=(left: ImageProcessor, right: ImageProcessor) -> Bool {
+    return !(left == right)
+}
+
+fileprivate struct GeneralProcessor: ImageProcessor {
+    let identifier: String
+    let p: ProcessorImp
+    func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        return p(item, options)
+    }
+}
+
+/// The default processor. It convert the input data to a valid image.
+/// Images of .PNG, .JPEG and .GIF format are supported.
+/// If an image is given, `DefaultImageProcessor` will do nothing on it and just return that image.
+public struct DefaultImageProcessor: ImageProcessor {
+    
+    /// A default `DefaultImageProcessor` could be used across.
+    public static let `default` = DefaultImageProcessor()
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier = ""
+    
+    /// Initialize a `DefaultImageProcessor`
+    public init() {}
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    /// 
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+        case .data(let data):
+            return Kingfisher<Image>.image(
+                data: data,
+                scale: options.scaleFactor,
+                preloadAllAnimationData: options.preloadAllAnimationData,
+                onlyFirstFrame: options.onlyLoadFirstFrame)
+        }
+    }
+}
+
+public struct RectCorner: OptionSet {
+    public let rawValue: Int
+    public static let topLeft = RectCorner(rawValue: 1 << 0)
+    public static let topRight = RectCorner(rawValue: 1 << 1)
+    public static let bottomLeft = RectCorner(rawValue: 1 << 2)
+    public static let bottomRight = RectCorner(rawValue: 1 << 3)
+    public static let all: RectCorner = [.topLeft, .topRight, .bottomLeft, .bottomRight]
+    
+    public init(rawValue: Int) {
+        self.rawValue = rawValue
+    }
+    
+    var cornerIdentifier: String {
+        if self == .all {
+            return ""
+        }
+        return "_corner(\(rawValue))"
+    }
+}
+
+#if !os(macOS)
+/// Processor for adding an blend mode to images. Only CG-based images are supported.
+public struct BlendImageProcessor: ImageProcessor {
+
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+
+    /// Blend Mode will be used to blend the input image.
+    public let blendMode: CGBlendMode
+    /// Alpha will be used when blend image.
+    public let alpha: CGFloat
+
+    /// Background color of the output image. If `nil`, it will stay transparent.
+    public let backgroundColor: Color?
+
+    /// Initialize an `BlendImageProcessor`
+    ///
+    /// - parameter blendMode:       Blend Mode will be used to blend the input image.
+    /// - parameter alpha:           Alpha will be used when blend image.
+    ///                              From 0.0 to 1.0. 1.0 means solid image, 0.0 means transparent image.
+    ///                              Default is 1.0.
+    /// - parameter backgroundColor: Background color to apply for the output image. Default is `nil`.
+    public init(blendMode: CGBlendMode, alpha: CGFloat = 1.0, backgroundColor: Color? = nil) {
+        self.blendMode = blendMode
+        self.alpha = alpha
+        self.backgroundColor = backgroundColor
+        var identifier = "com.onevcat.Kingfisher.BlendImageProcessor(\(blendMode.rawValue),\(alpha))"
+        if let color = backgroundColor {
+            identifier.append("_\(color.hex)")
+        }
+        self.identifier = identifier
+    }
+
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.image(withBlendMode: blendMode, alpha: alpha, backgroundColor: backgroundColor)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+#endif
+
+#if os(macOS)
+/// Processor for adding an compositing operation to images. Only CG-based images are supported in macOS.
+public struct CompositingImageProcessor: ImageProcessor {
+
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+
+    /// Compositing operation will be used to the input image.
+    public let compositingOperation: NSCompositingOperation
+
+    /// Alpha will be used when compositing image.
+    public let alpha: CGFloat
+
+    /// Background color of the output image. If `nil`, it will stay transparent.
+    public let backgroundColor: Color?
+
+    /// Initialize an `CompositingImageProcessor`
+    ///
+    /// - parameter compositingOperation: Compositing operation will be used to the input image.
+    /// - parameter alpha:                Alpha will be used when compositing image.
+    ///                                   From 0.0 to 1.0. 1.0 means solid image, 0.0 means transparent image.
+    ///                                   Default is 1.0.
+    /// - parameter backgroundColor:      Background color to apply for the output image. Default is `nil`.
+    public init(compositingOperation: NSCompositingOperation,
+                alpha: CGFloat = 1.0,
+                backgroundColor: Color? = nil)
+    {
+        self.compositingOperation = compositingOperation
+        self.alpha = alpha
+        self.backgroundColor = backgroundColor
+        var identifier = "com.onevcat.Kingfisher.CompositingImageProcessor(\(compositingOperation.rawValue),\(alpha))"
+        if let color = backgroundColor {
+            identifier.append("_\(color.hex)")
+        }
+        self.identifier = identifier
+    }
+
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.image(withCompositingOperation: compositingOperation, alpha: alpha, backgroundColor: backgroundColor)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+#endif
+
+/// Processor for making round corner images. Only CG-based images are supported in macOS, 
+/// if a non-CG image passed in, the processor will do nothing.
+public struct RoundCornerImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+
+    /// Corner radius will be applied in processing.
+    public let cornerRadius: CGFloat
+    
+    /// The target corners which will be applied rounding.
+    public let roundingCorners: RectCorner
+    
+    /// Target size of output image should be. If `nil`, the image will keep its original size after processing.
+    public let targetSize: CGSize?
+
+    /// Background color of the output image. If `nil`, it will stay transparent.
+    public let backgroundColor: Color?
+
+    /// Initialize a `RoundCornerImageProcessor`
+    ///
+    /// - parameter cornerRadius:    Corner radius will be applied in processing.
+    /// - parameter targetSize:      Target size of output image should be. If `nil`, 
+    ///                              the image will keep its original size after processing.
+    ///                              Default is `nil`.
+    /// - parameter corners:         The target corners which will be applied rounding. Default is `.all`.
+    /// - parameter backgroundColor: Background color to apply for the output image. Default is `nil`.
+    public init(cornerRadius: CGFloat, targetSize: CGSize? = nil, roundingCorners corners: RectCorner = .all, backgroundColor: Color? = nil) {
+        self.cornerRadius = cornerRadius
+        self.targetSize = targetSize
+        self.roundingCorners = corners
+        self.backgroundColor = backgroundColor
+
+        self.identifier = {
+            var identifier = ""
+
+            if let size = targetSize {
+                identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)_\(size)\(corners.cornerIdentifier))"
+            } else {
+                identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)\(corners.cornerIdentifier))"
+            }
+            if let backgroundColor = backgroundColor {
+                identifier += "_\(backgroundColor)"
+            }
+
+            return identifier
+        }()
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            let size = targetSize ?? image.kf.size
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.image(withRoundRadius: cornerRadius, fit: size, roundingCorners: roundingCorners, backgroundColor: backgroundColor)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+
+/// Specify how a size adjusts itself to fit a target size.
+///
+/// - none: Not scale the content.
+/// - aspectFit: Scale the content to fit the size of the view by maintaining the aspect ratio.
+/// - aspectFill: Scale the content to fill the size of the view
+public enum ContentMode {
+    case none
+    case aspectFit
+    case aspectFill
+}
+
+/// Processor for resizing images. Only CG-based images are supported in macOS.
+public struct ResizingImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// The reference size for resizing operation.
+    public let referenceSize: CGSize
+    
+    /// Target content mode of output image should be.
+    /// Default to ContentMode.none
+    public let targetContentMode: ContentMode
+    
+    /// Initialize a `ResizingImageProcessor`.
+    ///
+    /// - Parameters:
+    ///   - referenceSize: The reference size for resizing operation.
+    ///   - mode: Target content mode of output image should be.
+    ///
+    /// - Note:
+    ///   The instance of `ResizingImageProcessor` will follow its `mode` property
+    ///   and try to resizing the input images to fit or fill the `referenceSize`.
+    ///   That means if you are using a `mode` besides of `.none`, you may get an
+    ///   image with its size not be the same as the `referenceSize`.
+    ///
+    ///   **Example**: With input image size: {100, 200}, 
+    ///   `referenceSize`: {100, 100}, `mode`: `.aspectFit`,
+    ///   you will get an output image with size of {50, 100}, which "fit"s
+    ///   the `referenceSize`.
+    ///
+    ///   If you need an output image exactly to be a specified size, append or use
+    ///   a `CroppingImageProcessor`.
+    public init(referenceSize: CGSize, mode: ContentMode = .none) {
+        self.referenceSize = referenceSize
+        self.targetContentMode = mode
+        
+        if mode == .none {
+            self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(referenceSize))"
+        } else {
+            self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(referenceSize), \(mode))"
+        }
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.resize(to: referenceSize, for: targetContentMode)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Processor for adding blur effect to images. `Accelerate.framework` is used underhood for 
+/// a better performance. A simulated Gaussian blur with specified blur radius will be applied.
+public struct BlurImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// Blur radius for the simulated Gaussian blur.
+    public let blurRadius: CGFloat
+
+    /// Initialize a `BlurImageProcessor`
+    ///
+    /// - parameter blurRadius: Blur radius for the simulated Gaussian blur.
+    public init(blurRadius: CGFloat) {
+        self.blurRadius = blurRadius
+        self.identifier = "com.onevcat.Kingfisher.BlurImageProcessor(\(blurRadius))"
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            let radius = blurRadius * options.scaleFactor
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.blurred(withRadius: radius)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Processor for adding an overlay to images. Only CG-based images are supported in macOS.
+public struct OverlayImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// Overlay color will be used to overlay the input image.
+    public let overlay: Color
+    
+    /// Fraction will be used when overlay the color to image.
+    public let fraction: CGFloat
+    
+    /// Initialize an `OverlayImageProcessor`
+    ///
+    /// - parameter overlay:  Overlay color will be used to overlay the input image.
+    /// - parameter fraction: Fraction will be used when overlay the color to image. 
+    ///                       From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay.
+    public init(overlay: Color, fraction: CGFloat = 0.5) {
+        self.overlay = overlay
+        self.fraction = fraction
+        self.identifier = "com.onevcat.Kingfisher.OverlayImageProcessor(\(overlay.hex)_\(fraction))"
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.overlaying(with: overlay, fraction: fraction)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Processor for tint images with color. Only CG-based images are supported.
+public struct TintImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// Tint color will be used to tint the input image.
+    public let tint: Color
+    
+    /// Initialize a `TintImageProcessor`
+    ///
+    /// - parameter tint: Tint color will be used to tint the input image.
+    public init(tint: Color) {
+        self.tint = tint
+        self.identifier = "com.onevcat.Kingfisher.TintImageProcessor(\(tint.hex))"
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.tinted(with: tint)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Processor for applying some color control to images. Only CG-based images are supported.
+/// watchOS is not supported.
+public struct ColorControlsProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// Brightness changing to image.
+    public let brightness: CGFloat
+    
+    /// Contrast changing to image.
+    public let contrast: CGFloat
+    
+    /// Saturation changing to image.
+    public let saturation: CGFloat
+    
+    /// InputEV changing to image.
+    public let inputEV: CGFloat
+    
+    /// Initialize a `ColorControlsProcessor`
+    ///
+    /// - parameter brightness: Brightness changing to image.
+    /// - parameter contrast:   Contrast changing to image.
+    /// - parameter saturation: Saturation changing to image.
+    /// - parameter inputEV:    InputEV changing to image.
+    public init(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) {
+        self.brightness = brightness
+        self.contrast = contrast
+        self.saturation = saturation
+        self.inputEV = inputEV
+        self.identifier = "com.onevcat.Kingfisher.ColorControlsProcessor(\(brightness)_\(contrast)_\(saturation)_\(inputEV))"
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.adjusted(brightness: brightness, contrast: contrast, saturation: saturation, inputEV: inputEV)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Processor for applying black and white effect to images. Only CG-based images are supported.
+/// watchOS is not supported.
+public struct BlackWhiteProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier = "com.onevcat.Kingfisher.BlackWhiteProcessor"
+    
+    /// Initialize a `BlackWhiteProcessor`
+    public init() {}
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        return ColorControlsProcessor(brightness: 0.0, contrast: 1.0, saturation: 0.0, inputEV: 0.7)
+            .process(item: item, options: options)
+    }
+}
+
+/// Processor for cropping an image. Only CG-based images are supported.
+/// watchOS is not supported.
+public struct CroppingImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// Target size of output image should be.
+    public let size: CGSize
+    
+    /// Anchor point from which the output size should be calculate.
+    /// The anchor point is consisted by two values between 0.0 and 1.0.
+    /// It indicates a related point in current image. 
+    /// See `CroppingImageProcessor.init(size:anchor:)` for more.
+    public let anchor: CGPoint
+    
+    /// Initialize a `CroppingImageProcessor`
+    ///
+    /// - Parameters:
+    ///   - size: Target size of output image should be.
+    ///   - anchor: The anchor point from which the size should be calculated.
+    ///             Default is `CGPoint(x: 0.5, y: 0.5)`, which means the center of input image.
+    /// - Note:
+    ///   The anchor point is consisted by two values between 0.0 and 1.0.
+    ///   It indicates a related point in current image, eg: (0.0, 0.0) for top-left
+    ///   corner, (0.5, 0.5) for center and (1.0, 1.0) for bottom-right corner.
+    ///   The `size` property of `CroppingImageProcessor` will be used along with
+    ///   `anchor` to calculate a target rectangle in the size of image.
+    ///    
+    ///   The target size will be automatically calculated with a reasonable behavior.
+    ///   For example, when you have an image size of `CGSize(width: 100, height: 100)`,
+    ///   and a target size of `CGSize(width: 20, height: 20)`: 
+    ///   - with a (0.0, 0.0) anchor (top-left), the crop rect will be `{0, 0, 20, 20}`; 
+    ///   - with a (0.5, 0.5) anchor (center), it will be `{40, 40, 20, 20}`
+    ///   - while with a (1.0, 1.0) anchor (bottom-right), it will be `{80, 80, 20, 20}`
+    public init(size: CGSize, anchor: CGPoint = CGPoint(x: 0.5, y: 0.5)) {
+        self.size = size
+        self.anchor = anchor
+        self.identifier = "com.onevcat.Kingfisher.CroppingImageProcessor(\(size)_\(anchor))"
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.crop(to: size, anchorOn: anchor)
+        case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Concatenate two `ImageProcessor`s. `ImageProcessor.appen(another:)` is used internally.
+///
+/// - parameter left:  First processor.
+/// - parameter right: Second processor.
+///
+/// - returns: The concatenated processor.
+public func >>(left: ImageProcessor, right: ImageProcessor) -> ImageProcessor {
+    return left.append(another: right)
+}
+
+extension Color {
+    var hex: String {
+        var r: CGFloat = 0
+        var g: CGFloat = 0
+        var b: CGFloat = 0
+        var a: CGFloat = 0
+
+        #if os(macOS)
+        (usingColorSpace(.sRGB) ?? self).getRed(&r, green: &g, blue: &b, alpha: &a)
+        #else
+        getRed(&r, green: &g, blue: &b, alpha: &a)
+        #endif
+
+        let rInt = Int(r * 255) << 24
+        let gInt = Int(g * 255) << 16
+        let bInt = Int(b * 255) << 8
+        let aInt = Int(a * 255)
+        
+        let rgba = rInt | gInt | bInt | aInt
+        
+        return String(format:"#%08x", rgba)
+    }
+}

+ 128 - 0
Example/Pods/Kingfisher/Sources/ImageTransition.swift

@@ -0,0 +1,128 @@
+//
+//  ImageTransition.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/9/18.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+// Not implemented for macOS and watchOS yet.
+    
+import AppKit
+
+/// Image transition is not supported on macOS.
+public enum ImageTransition {
+    case none
+    var duration: TimeInterval {
+        return 0
+    }
+}
+
+#elseif os(watchOS)
+import UIKit
+/// Image transition is not supported on watchOS.
+public enum ImageTransition {
+    case none
+    var duration: TimeInterval {
+        return 0
+    }
+}
+#else
+import UIKit
+
+/**
+Transition effect which will be used when an image downloaded and set by `UIImageView` extension API in Kingfisher.
+You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo` 
+to enable the animation transition.
+
+Apple's UIViewAnimationOptions is used under the hood.
+For custom transition, you should specified your own transition options, animations and 
+completion handler as well.
+*/
+public enum ImageTransition {
+    ///  No animation transition.
+    case none
+    
+    /// Fade in the loaded image.
+    case fade(TimeInterval)
+
+    /// Flip from left transition.
+    case flipFromLeft(TimeInterval)
+
+    /// Flip from right transition.
+    case flipFromRight(TimeInterval)
+    
+    /// Flip from top transition.
+    case flipFromTop(TimeInterval)
+    
+    /// Flip from bottom transition.
+    case flipFromBottom(TimeInterval)
+    
+    /// Custom transition.
+    case custom(duration: TimeInterval,
+                 options: UIView.AnimationOptions,
+              animations: ((UIImageView, UIImage) -> Void)?,
+              completion: ((Bool) -> Void)?)
+    
+    var duration: TimeInterval {
+        switch self {
+        case .none:                          return 0
+        case .fade(let duration):            return duration
+            
+        case .flipFromLeft(let duration):    return duration
+        case .flipFromRight(let duration):   return duration
+        case .flipFromTop(let duration):     return duration
+        case .flipFromBottom(let duration):  return duration
+            
+        case .custom(let duration, _, _, _): return duration
+        }
+    }
+    
+    var animationOptions: UIView.AnimationOptions {
+        switch self {
+        case .none:                         return []
+        case .fade(_):                      return .transitionCrossDissolve
+            
+        case .flipFromLeft(_):              return .transitionFlipFromLeft
+        case .flipFromRight(_):             return .transitionFlipFromRight
+        case .flipFromTop(_):               return .transitionFlipFromTop
+        case .flipFromBottom(_):            return .transitionFlipFromBottom
+            
+        case .custom(_, let options, _, _): return options
+        }
+    }
+    
+    var animations: ((UIImageView, UIImage) -> Void)? {
+        switch self {
+        case .custom(_, _, let animations, _): return animations
+        default: return { $0.image = $1 }
+        }
+    }
+    
+    var completion: ((Bool) -> Void)? {
+        switch self {
+        case .custom(_, _, _, let completion): return completion
+        default: return nil
+        }
+    }
+}
+#endif

+ 263 - 0
Example/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift

@@ -0,0 +1,263 @@
+//
+//  ImageView+Kingfisher.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+
+#if os(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+
+// MARK: - Extension methods.
+/**
+ *    Set image to use from web.
+ */
+extension Kingfisher where Base: ImageView {
+    /**
+     Set an image with a resource, a placeholder image, options, progress handler and completion handler.
+     
+     - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
+     - parameter placeholder:       A placeholder image when retrieving the image at URL.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called when the image downloading progress gets updated.
+     - parameter completionHandler: Called when the image retrieved and set.
+     
+     - returns: A task represents the retrieving process.
+     
+     - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
+     The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
+     
+     If `resource` is `nil`, the `placeholder` image will be set and
+     `completionHandler` will be called with both `error` and `image` being `nil`.
+     */
+    @discardableResult
+    public func setImage(with resource: Resource?,
+                         placeholder: Placeholder? = nil,
+                         options: KingfisherOptionsInfo? = nil,
+                         progressBlock: DownloadProgressBlock? = nil,
+                         completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
+    {
+        guard let resource = resource else {
+            self.placeholder = placeholder
+            setWebURL(nil)
+            completionHandler?(nil, nil, .none, nil)
+            return .empty
+        }
+        
+        var options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
+        let noImageOrPlaceholderSet = base.image == nil && self.placeholder == nil
+        
+        if !options.keepCurrentImageWhileLoading || noImageOrPlaceholderSet { // Always set placeholder while there is no image/placehoer yet.
+            self.placeholder = placeholder
+        }
+
+        let maybeIndicator = indicator
+        maybeIndicator?.startAnimatingView()
+        
+        setWebURL(resource.downloadURL)
+
+        if base.shouldPreloadAllAnimation() {
+            options.append(.preloadAllAnimationData)
+        }
+        
+        let task = KingfisherManager.shared.retrieveImage(
+            with: resource,
+            options: options,
+            progressBlock: { receivedSize, totalSize in
+                guard resource.downloadURL == self.webURL else {
+                    return
+                }
+                if let progressBlock = progressBlock {
+                    progressBlock(receivedSize, totalSize)
+                }
+            },
+            completionHandler: {[weak base] image, error, cacheType, imageURL in
+                DispatchQueue.main.safeAsync {
+                    maybeIndicator?.stopAnimatingView()
+                    guard let strongBase = base, imageURL == self.webURL else {
+                        completionHandler?(image, error, cacheType, imageURL)
+                        return
+                    }
+                    
+                    self.setImageTask(nil)
+                    guard let image = image else {
+                        completionHandler?(nil, error, cacheType, imageURL)
+                        return
+                    }
+                    
+                    guard let transitionItem = options.lastMatchIgnoringAssociatedValue(.transition(.none)),
+                        case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else
+                    {
+                        self.placeholder = nil
+                        strongBase.image = image
+                        completionHandler?(image, error, cacheType, imageURL)
+                        return
+                    }
+                    
+                    #if !os(macOS)
+                        UIView.transition(with: strongBase, duration: 0.0, options: [],
+                                          animations: { maybeIndicator?.stopAnimatingView() },
+                                          completion: { _ in
+
+                                            self.placeholder = nil
+                                            UIView.transition(with: strongBase, duration: transition.duration,
+                                                              options: [transition.animationOptions, .allowUserInteraction],
+                                                              animations: {
+                                                                // Set image property in the animation.
+                                                                transition.animations?(strongBase, image)
+                                                              },
+                                                              completion: { finished in
+                                                                transition.completion?(finished)
+                                                                completionHandler?(image, error, cacheType, imageURL)
+                                                              })
+                                          })
+                    #endif
+                }
+            })
+        
+        setImageTask(task)
+        
+        return task
+    }
+    
+    /**
+     Cancel the image download task bounded to the image view if it is running.
+     Nothing will happen if the downloading has already finished.
+     */
+    public func cancelDownloadTask() {
+        imageTask?.cancel()
+    }
+}
+
+// MARK: - Associated Object
+private var lastURLKey: Void?
+private var indicatorKey: Void?
+private var indicatorTypeKey: Void?
+private var placeholderKey: Void?
+private var imageTaskKey: Void?
+
+extension Kingfisher where Base: ImageView {
+    /// Get the image URL binded to this image view.
+    public var webURL: URL? {
+        return objc_getAssociatedObject(base, &lastURLKey) as? URL
+    }
+    
+    fileprivate func setWebURL(_ url: URL?) {
+        objc_setAssociatedObject(base, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+    
+    /// Holds which indicator type is going to be used.
+    /// Default is .none, means no indicator will be shown.
+    public var indicatorType: IndicatorType {
+        get {
+            let indicator = objc_getAssociatedObject(base, &indicatorTypeKey) as? IndicatorType
+            return indicator ?? .none
+        }
+        
+        set {
+            switch newValue {
+            case .none:
+                indicator = nil
+            case .activity:
+                indicator = ActivityIndicator()
+            case .image(let data):
+                indicator = ImageIndicator(imageData: data)
+            case .custom(let anIndicator):
+                indicator = anIndicator
+            }
+            
+            objc_setAssociatedObject(base, &indicatorTypeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    /// Holds any type that conforms to the protocol `Indicator`.
+    /// The protocol `Indicator` has a `view` property that will be shown when loading an image.
+    /// It will be `nil` if `indicatorType` is `.none`.
+    public fileprivate(set) var indicator: Indicator? {
+        get {
+            guard let box = objc_getAssociatedObject(base, &indicatorKey) as? Box<Indicator> else {
+                return nil
+            }
+            return box.value
+        }
+        
+        set {
+            // Remove previous
+            if let previousIndicator = indicator {
+                previousIndicator.view.removeFromSuperview()
+            }
+            
+            // Add new
+            if var newIndicator = newValue {
+                // Set default indicator frame if the view's frame not set.
+                if newIndicator.view.frame == .zero {
+                    newIndicator.view.frame = base.frame
+                }
+                newIndicator.viewCenter = CGPoint(x: base.bounds.midX, y: base.bounds.midY)
+                newIndicator.view.isHidden = true
+                base.addSubview(newIndicator.view)
+            }
+            
+            // Save in associated object
+            // Wrap newValue with Box to workaround an issue that Swift does not recognize
+            // and casting protocol for associate object correctly. https://github.com/onevcat/Kingfisher/issues/872
+            objc_setAssociatedObject(base, &indicatorKey, newValue.map(Box.init), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    fileprivate var imageTask: RetrieveImageTask? {
+        return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask
+    }
+    
+    fileprivate func setImageTask(_ task: RetrieveImageTask?) {
+        objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+    
+    public fileprivate(set) var placeholder: Placeholder? {
+        get {
+            return objc_getAssociatedObject(base, &placeholderKey) as? Placeholder
+        }
+        
+        set {
+            if let previousPlaceholder = placeholder {
+                previousPlaceholder.remove(from: base)
+            }
+            
+            if let newPlaceholder = newValue {
+                newPlaceholder.add(to: base)
+            } else {
+                base.image = nil
+            }
+            
+            objc_setAssociatedObject(base, &placeholderKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+}
+
+
+@objc extension ImageView {
+    func shouldPreloadAllAnimation() -> Bool { return true }
+}

+ 203 - 0
Example/Pods/Kingfisher/Sources/Indicator.swift

@@ -0,0 +1,203 @@
+//
+//  Indicator.swift
+//  Kingfisher
+//
+//  Created by João D. Moreira on 30/08/16.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+    import AppKit
+#else
+    import UIKit
+#endif
+
+#if os(macOS)
+    public typealias IndicatorView = NSView
+#else
+    public typealias IndicatorView = UIView
+#endif
+
+public enum IndicatorType {
+    /// No indicator.
+    case none
+    /// Use system activity indicator.
+    case activity
+    /// Use an image as indicator. GIF is supported.
+    case image(imageData: Data)
+    /// Use a custom indicator, which conforms to the `Indicator` protocol.
+    case custom(indicator: Indicator)
+}
+
+// MARK: - Indicator Protocol
+public protocol Indicator {
+    func startAnimatingView()
+    func stopAnimatingView()
+
+    var viewCenter: CGPoint { get set }
+    var view: IndicatorView { get }
+}
+
+extension Indicator {
+    #if os(macOS)
+    public var viewCenter: CGPoint {
+        get {
+            let frame = view.frame
+            return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 )
+        }
+        set {
+            let frame = view.frame
+            let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0,
+                                  y: newValue.y - frame.size.height / 2.0,
+                                  width: frame.size.width,
+                                  height: frame.size.height)
+            view.frame = newFrame
+        }
+    }
+    #else
+    public var viewCenter: CGPoint {
+        get {
+            return view.center
+        }
+        set {
+            view.center = newValue
+        }
+    }
+    #endif
+}
+
+// MARK: - ActivityIndicator
+// Displays a NSProgressIndicator / UIActivityIndicatorView
+final class ActivityIndicator: Indicator {
+
+    #if os(macOS)
+    private let activityIndicatorView: NSProgressIndicator
+    #else
+    private let activityIndicatorView: UIActivityIndicatorView
+    #endif
+    private var animatingCount = 0
+
+    var view: IndicatorView {
+        return activityIndicatorView
+    }
+
+    func startAnimatingView() {
+        animatingCount += 1
+        // Already animating
+        if animatingCount == 1 {
+            #if os(macOS)
+                activityIndicatorView.startAnimation(nil)
+            #else
+                activityIndicatorView.startAnimating()
+            #endif
+            activityIndicatorView.isHidden = false
+        }
+    }
+
+    func stopAnimatingView() {
+        animatingCount = max(animatingCount - 1, 0)
+        if animatingCount == 0 {
+            #if os(macOS)
+                activityIndicatorView.stopAnimation(nil)
+            #else
+                activityIndicatorView.stopAnimating()
+            #endif
+            activityIndicatorView.isHidden = true
+        }
+    }
+
+    init() {
+        #if os(macOS)
+            activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
+            activityIndicatorView.controlSize = .small
+            activityIndicatorView.style = .spinning
+        #else
+            #if os(tvOS)
+                let indicatorStyle = UIActivityIndicatorView.Style.white
+            #else
+                let indicatorStyle = UIActivityIndicatorView.Style.gray
+            #endif
+        #if swift(>=4.2)
+            activityIndicatorView = UIActivityIndicatorView(style: indicatorStyle)
+        #else
+            activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: indicatorStyle)
+        #endif
+            activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin]
+        #endif
+    }
+}
+
+// MARK: - ImageIndicator
+// Displays an ImageView. Supports gif
+final class ImageIndicator: Indicator {
+    private let animatedImageIndicatorView: ImageView
+
+    var view: IndicatorView {
+        return animatedImageIndicatorView
+    }
+
+    init?(imageData data: Data, processor: ImageProcessor = DefaultImageProcessor.default, options: KingfisherOptionsInfo = KingfisherEmptyOptionsInfo) {
+
+        var options = options
+        // Use normal image view to show animations, so we need to preload all animation data.
+        if !options.preloadAllAnimationData {
+            options.append(.preloadAllAnimationData)
+        }
+        
+        guard let image = processor.process(item: .data(data), options: options) else {
+            return nil
+        }
+
+        animatedImageIndicatorView = ImageView()
+        animatedImageIndicatorView.image = image
+        animatedImageIndicatorView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
+        
+        #if os(macOS)
+            // Need for gif to animate on macOS
+            self.animatedImageIndicatorView.imageScaling = .scaleNone
+            self.animatedImageIndicatorView.canDrawSubviewsIntoLayer = true
+        #else
+            animatedImageIndicatorView.contentMode = .center
+            animatedImageIndicatorView.autoresizingMask = [.flexibleLeftMargin,
+                                                           .flexibleRightMargin,
+                                                           .flexibleBottomMargin,
+                                                           .flexibleTopMargin]
+        #endif
+    }
+
+    func startAnimatingView() {
+        #if os(macOS)
+            animatedImageIndicatorView.animates = true
+        #else
+            animatedImageIndicatorView.startAnimating()
+        #endif
+        animatedImageIndicatorView.isHidden = false
+    }
+
+    func stopAnimatingView() {
+        #if os(macOS)
+            animatedImageIndicatorView.animates = false
+        #else
+            animatedImageIndicatorView.stopAnimating()
+        #endif
+        animatedImageIndicatorView.isHidden = true
+    }
+}

+ 37 - 0
Example/Pods/Kingfisher/Sources/Kingfisher.h

@@ -0,0 +1,37 @@
+//
+//  Kingfisher.h
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for Kingfisher.
+FOUNDATION_EXPORT double KingfisherVersionNumber;
+
+//! Project version string for Kingfisher.
+FOUNDATION_EXPORT const unsigned char KingfisherVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <Kingfisher/PublicHeader.h>
+
+

+ 77 - 0
Example/Pods/Kingfisher/Sources/Kingfisher.swift

@@ -0,0 +1,77 @@
+//
+//  Kingfisher.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 16/9/14.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+import ImageIO
+
+#if os(macOS)
+    import AppKit
+    public typealias Image = NSImage
+    public typealias View = NSView
+    public typealias Color = NSColor
+    public typealias ImageView = NSImageView
+    public typealias Button = NSButton
+#else
+    import UIKit
+    public typealias Image = UIImage
+    public typealias Color = UIColor
+    #if !os(watchOS)
+    public typealias ImageView = UIImageView
+    public typealias View = UIView
+    public typealias Button = UIButton
+    #else
+    import WatchKit
+    #endif
+#endif
+
+public final class Kingfisher<Base> {
+    public let base: Base
+    public init(_ base: Base) {
+        self.base = base
+    }
+}
+
+/**
+ A type that has Kingfisher extensions.
+ */
+public protocol KingfisherCompatible {
+    associatedtype CompatibleType
+    var kf: CompatibleType { get }
+}
+
+public extension KingfisherCompatible {
+    public var kf: Kingfisher<Self> {
+        return Kingfisher(self)
+    }
+}
+
+extension Image: KingfisherCompatible { }
+#if !os(watchOS)
+extension ImageView: KingfisherCompatible { }
+extension Button: KingfisherCompatible { }
+#else
+extension WKInterfaceImage: KingfisherCompatible { }
+#endif

+ 297 - 0
Example/Pods/Kingfisher/Sources/KingfisherManager.swift

@@ -0,0 +1,297 @@
+//
+//  KingfisherManager.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+
+public typealias DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> Void)
+public typealias CompletionHandler = ((_ image: Image?, _ error: NSError?, _ cacheType: CacheType, _ imageURL: URL?) -> Void)
+
+/// RetrieveImageTask represents a task of image retrieving process.
+/// It contains an async task of getting image from disk and from network.
+public final class RetrieveImageTask {
+    
+    public static let empty = RetrieveImageTask()
+    
+    // If task is canceled before the download task started (which means the `downloadTask` is nil),
+    // the download task should not begin.
+    var cancelledBeforeDownloadStarting: Bool = false
+    
+    /// The network retrieve task in this image task.
+    public var downloadTask: RetrieveImageDownloadTask?
+    
+    /**
+    Cancel current task. If this task is already done, do nothing.
+    */
+    public func cancel() {
+        if let downloadTask = downloadTask {
+            downloadTask.cancel()
+        } else {
+            cancelledBeforeDownloadStarting = true
+        }
+    }
+}
+
+/// Error domain of Kingfisher
+public let KingfisherErrorDomain = "com.onevcat.Kingfisher.Error"
+
+/// Main manager class of Kingfisher. It connects Kingfisher downloader and cache.
+/// You can use this class to retrieve an image via a specified URL from web or cache.
+public class KingfisherManager {
+    
+    /// Shared manager used by the extensions across Kingfisher.
+    public static let shared = KingfisherManager()
+    
+    /// Cache used by this manager
+    public var cache: ImageCache
+    
+    /// Downloader used by this manager
+    public var downloader: ImageDownloader
+    
+    /// Default options used by the manager. This option will be used in 
+    /// Kingfisher manager related methods, including all image view and 
+    /// button extension methods. You can also passing the options per image by 
+    /// sending an `options` parameter to Kingfisher's APIs, the per image option 
+    /// will overwrite the default ones if exist.
+    ///
+    /// - Note: This option will not be applied to independent using of `ImageDownloader` or `ImageCache`.
+    public var defaultOptions = KingfisherEmptyOptionsInfo
+    
+    var currentDefaultOptions: KingfisherOptionsInfo {
+        return [.downloader(downloader), .targetCache(cache)] + defaultOptions
+    }
+
+    fileprivate let processQueue: DispatchQueue
+    
+    convenience init() {
+        self.init(downloader: .default, cache: .default)
+    }
+    
+    init(downloader: ImageDownloader, cache: ImageCache) {
+        self.downloader = downloader
+        self.cache = cache
+
+        let processQueueName = "com.onevcat.Kingfisher.KingfisherManager.processQueue.\(UUID().uuidString)"
+        processQueue = DispatchQueue(label: processQueueName, attributes: .concurrent)
+    }
+    
+    /**
+    Get an image with resource.
+    If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first.
+    If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`.
+    These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more.
+    
+    - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
+    - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+    - parameter progressBlock:     Called every time downloaded data changed. This could be used as a progress UI.
+    - parameter completionHandler: Called when the whole retrieving process finished.
+    
+    - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task.
+    */
+    @discardableResult
+    public func retrieveImage(with resource: Resource,
+        options: KingfisherOptionsInfo?,
+        progressBlock: DownloadProgressBlock?,
+        completionHandler: CompletionHandler?) -> RetrieveImageTask
+    {
+        let task = RetrieveImageTask()
+        let options = currentDefaultOptions + (options ?? KingfisherEmptyOptionsInfo)
+        if options.forceRefresh {
+            _ = downloadAndCacheImage(
+                with: resource.downloadURL,
+                forKey: resource.cacheKey,
+                retrieveImageTask: task,
+                progressBlock: progressBlock,
+                completionHandler: completionHandler,
+                options: options)
+        } else {
+            tryToRetrieveImageFromCache(
+                forKey: resource.cacheKey,
+                with: resource.downloadURL,
+                retrieveImageTask: task,
+                progressBlock: progressBlock,
+                completionHandler: completionHandler,
+                options: options)
+        }
+        
+        return task
+    }
+
+    @discardableResult
+    func downloadAndCacheImage(with url: URL,
+                             forKey key: String,
+                      retrieveImageTask: RetrieveImageTask,
+                          progressBlock: DownloadProgressBlock?,
+                      completionHandler: CompletionHandler?,
+                                options: KingfisherOptionsInfo) -> RetrieveImageDownloadTask?
+    {
+        let downloader = options.downloader ?? self.downloader
+        let processQueue = self.processQueue
+        return downloader.downloadImage(with: url, retrieveImageTask: retrieveImageTask, options: options,
+            progressBlock: { receivedSize, totalSize in
+                progressBlock?(receivedSize, totalSize)
+            },
+            completionHandler: { image, error, imageURL, originalData in
+
+                let targetCache = options.targetCache ?? self.cache
+                if let error = error, error.code == KingfisherError.notModified.rawValue {
+                    // Not modified. Try to find the image from cache.
+                    // (The image should be in cache. It should be guaranteed by the framework users.)
+                    targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> Void in
+                        completionHandler?(cacheImage, nil, cacheType, url)
+                    })
+                    return
+                }
+                
+                if let image = image, let originalData = originalData {
+                    targetCache.store(image,
+                                      original: originalData,
+                                      forKey: key,
+                                      processorIdentifier:options.processor.identifier,
+                                      cacheSerializer: options.cacheSerializer,
+                                      toDisk: !options.cacheMemoryOnly,
+                                      completionHandler: {
+                                        guard options.waitForCache else { return }
+                                        
+                                        let cacheType = targetCache.imageCachedType(forKey: key, processorIdentifier: options.processor.identifier)
+                                        completionHandler?(image, nil, cacheType, url)
+                    })
+                    
+                    if options.cacheOriginalImage && options.processor != DefaultImageProcessor.default {
+                        let originalCache = options.originalCache ?? targetCache
+                        let defaultProcessor = DefaultImageProcessor.default
+                        processQueue.async {
+                            if let originalImage = defaultProcessor.process(item: .data(originalData), options: options) {
+                                originalCache.store(originalImage,
+                                                    original: originalData,
+                                                    forKey: key,
+                                                    processorIdentifier: defaultProcessor.identifier,
+                                                    cacheSerializer: options.cacheSerializer,
+                                                    toDisk: !options.cacheMemoryOnly,
+                                                    completionHandler: nil)
+                            }
+                        }
+                    }
+                }
+
+                if options.waitForCache == false || image == nil {
+                    completionHandler?(image, error, .none, url)
+                }
+            })
+    }
+    
+    func tryToRetrieveImageFromCache(forKey key: String,
+                                       with url: URL,
+                              retrieveImageTask: RetrieveImageTask,
+                                  progressBlock: DownloadProgressBlock?,
+                              completionHandler: CompletionHandler?,
+                                        options: KingfisherOptionsInfo)
+    {
+
+        let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> Void in
+            completionHandler?(image, error, cacheType, imageURL)
+        }
+        
+        func handleNoCache() {
+            if options.onlyFromCache {
+                let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil)
+                diskTaskCompletionHandler(nil, error, .none, url)
+                return
+            }
+            self.downloadAndCacheImage(
+                with: url,
+                forKey: key,
+                retrieveImageTask: retrieveImageTask,
+                progressBlock: progressBlock,
+                completionHandler: diskTaskCompletionHandler,
+                options: options)
+            
+        }
+        
+        let targetCache = options.targetCache ?? self.cache
+        let processQueue = self.processQueue
+        // First, try to get the exactly image from cache
+        targetCache.retrieveImage(forKey: key, options: options) { image, cacheType in
+            // If found, we could finish now.
+            if image != nil {
+                diskTaskCompletionHandler(image, nil, cacheType, url)
+                return
+            }
+            
+            // If not found, and we are using a default processor, download it!
+            let processor = options.processor
+            guard processor != DefaultImageProcessor.default else {
+                handleNoCache()
+                return
+            }
+            
+            // If processor is not the default one, we have a chance to check whether
+            // the original image is already in cache.
+            let originalCache = options.originalCache ?? targetCache
+            let optionsWithoutProcessor = options.removeAllMatchesIgnoringAssociatedValue(.processor(processor))
+            originalCache.retrieveImage(forKey: key, options: optionsWithoutProcessor) { image, cacheType in
+                // If we found the original image, there is no need to download it again.
+                // We could just apply processor to it now.
+                guard let image = image else {
+                    handleNoCache()
+                    return
+                }
+
+                processQueue.async {
+                    guard let processedImage = processor.process(item: .image(image), options: options) else {
+                        options.callbackDispatchQueue.safeAsync {
+                            diskTaskCompletionHandler(nil, nil, .none, url)
+                        }
+                        return
+                    }
+                    targetCache.store(processedImage,
+                                      original: nil,
+                                      forKey: key,
+                                      processorIdentifier:options.processor.identifier,
+                                      cacheSerializer: options.cacheSerializer,
+                                      toDisk: !options.cacheMemoryOnly,
+                                      completionHandler: {
+                                        guard options.waitForCache else { return }
+
+                                        let cacheType = targetCache.imageCachedType(forKey: key, processorIdentifier: options.processor.identifier)
+                                        options.callbackDispatchQueue.safeAsync {
+                                            diskTaskCompletionHandler(processedImage, nil, cacheType, url)
+                                        }
+                    })
+
+                    if options.waitForCache == false {
+                        options.callbackDispatchQueue.safeAsync {
+                            diskTaskCompletionHandler(processedImage, nil, .none, url)
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 364 - 0
Example/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift

@@ -0,0 +1,364 @@
+//
+//  KingfisherOptionsInfo.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/23.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+    
+
+/**
+*	KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem]. You can use the enum of option item with value to control some behaviors of Kingfisher.
+*/
+public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem]
+let KingfisherEmptyOptionsInfo = [KingfisherOptionsInfoItem]()
+
+/**
+Items could be added into KingfisherOptionsInfo.
+*/
+public enum KingfisherOptionsInfoItem {
+    /// The associated value of this member should be an ImageCache object. Kingfisher will use the specified
+    /// cache object when handling related operations, including trying to retrieve the cached images and store
+    /// the downloaded image to it.
+    case targetCache(ImageCache)
+    
+    /// Cache for storing and retrieving original image.
+    /// Preferred prior to targetCache for storing and retrieving original images if specified.
+    /// Only used if a non-default image processor is involved.
+    case originalCache(ImageCache)
+    
+    /// The associated value of this member should be an ImageDownloader object. Kingfisher will use this
+    /// downloader to download the images.
+    case downloader(ImageDownloader)
+    
+    /// Member for animation transition when using UIImageView. Kingfisher will use the `ImageTransition` of
+    /// this enum to animate the image in if it is downloaded from web. The transition will not happen when the
+    /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when
+    /// the image being retrieved from cache, set `ForceTransition` as well.
+    case transition(ImageTransition)
+    
+    /// Associated `Float` value will be set as the priority of image download task. The value for it should be
+    /// between 0.0~1.0. If this option not set, the default value (`NSURLSessionTaskPriorityDefault`) will be used.
+    case downloadPriority(Float)
+    
+    /// If set, `Kingfisher` will ignore the cache and try to fire a download task for the resource.
+    case forceRefresh
+
+    /// If set, `Kingfisher` will try to retrieve the image from memory cache first. If the image is not in memory
+    /// cache, then it will ignore the disk cache but download the image again from network. This is useful when
+    /// you want to display a changeable image behind the same url, while avoiding download it again and again.
+    case fromMemoryCacheOrRefresh
+    
+    /// If set, setting the image to an image view will happen with transition even when retrieved from cache.
+    /// See `Transition` option for more.
+    case forceTransition
+    
+    ///  If set, `Kingfisher` will only cache the value in memory but not in disk.
+    case cacheMemoryOnly
+    
+    ///  If set, `Kingfisher` will wait for caching operation to be completed before calling the completion block.
+    case waitForCache
+    
+    /// If set, `Kingfisher` will only try to retrieve the image from cache not from network.
+    case onlyFromCache
+    
+    /// Decode the image in background thread before using.
+    case backgroundDecode
+    
+    /// The associated value of this member will be used as the target queue of dispatch callbacks when
+    /// retrieving images from cache. If not set, `Kingfisher` will use main queue for callbacks.
+    case callbackDispatchQueue(DispatchQueue?)
+    
+    /// The associated value of this member will be used as the scale factor when converting retrieved data to an image.
+    /// It is the image scale, instead of your screen scale. You may need to specify the correct scale when you dealing 
+    /// with 2x or 3x retina images.
+    case scaleFactor(CGFloat)
+
+    /// Whether all the animated image data should be preloaded. Default it false, which means following frames will be
+    /// loaded on need. If true, all the animated image data will be loaded and decoded into memory. This option is mainly
+    /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload
+    /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use
+    /// corresponding image view type instead of setting this option.
+    case preloadAllAnimationData
+    
+    /// The `ImageDownloadRequestModifier` contained will be used to change the request before it being sent.
+    /// This is the last chance you can modify the request. You can modify the request for some customizing purpose,
+    /// such as adding auth token to the header, do basic HTTP auth or something like url mapping. The original request
+    /// will be sent without any modification by default.
+    case requestModifier(ImageDownloadRequestModifier)
+    
+    /// Processor for processing when the downloading finishes, a processor will convert the downloaded data to an image
+    /// and/or apply some filter on it. If a cache is connected to the downloader (it happens when you are using
+    /// KingfisherManager or the image extension methods), the converted image will also be sent to cache as well as the
+    /// image view. `DefaultImageProcessor.default` will be used by default.
+    case processor(ImageProcessor)
+    
+    /// Supply an `CacheSerializer` to convert some data to an image object for
+    /// retrieving from disk cache or vice versa for storing to disk cache.
+    /// `DefaultCacheSerializer.default` will be used by default.
+    case cacheSerializer(CacheSerializer)
+
+    /// Modifier for modifying an image right before it is used.
+    /// If the image was fetched directly from the downloader, the modifier will
+    /// run directly after the processor.
+    /// If the image is being fetched from a cache, the modifier will run after
+    /// the cacheSerializer.
+    /// Use `ImageModifier` when you need to set properties on a concrete type
+    /// of `Image`, such as a `UIImage`, that do not persist when caching the image.
+    case imageModifier(ImageModifier)
+    
+    /// Keep the existing image while setting another image to an image view.
+    /// By setting this option, the placeholder image parameter of imageview extension method
+    /// will be ignored and the current image will be kept while loading or downloading the new image.
+    case keepCurrentImageWhileLoading
+    
+    /// If set, Kingfisher will only load the first frame from a animated image data file as a single image.
+    /// Loading a lot of animated images may take too much memory. It will be useful when you want to display a
+    /// static preview of the first frame from a animated image.
+    /// This option will be ignored if the target image is not animated image data.
+    case onlyLoadFirstFrame
+    
+    /// If set and an `ImageProcessor` is used, Kingfisher will try to cache both 
+    /// the final result and original image. Kingfisher will have a chance to use 
+    /// the original image when another processor is applied to the same resource,
+    /// instead of downloading it again.
+    case cacheOriginalImage
+}
+
+precedencegroup ItemComparisonPrecedence {
+    associativity: none
+    higherThan: LogicalConjunctionPrecedence
+}
+
+infix operator <== : ItemComparisonPrecedence
+
+// This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values.
+func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool {
+    switch (lhs, rhs) {
+    case (.targetCache(_), .targetCache(_)): return true
+    case (.originalCache(_), .originalCache(_)): return true
+    case (.downloader(_), .downloader(_)): return true
+    case (.transition(_), .transition(_)): return true
+    case (.downloadPriority(_), .downloadPriority(_)): return true
+    case (.forceRefresh, .forceRefresh): return true
+    case (.fromMemoryCacheOrRefresh, .fromMemoryCacheOrRefresh): return true
+    case (.forceTransition, .forceTransition): return true
+    case (.cacheMemoryOnly, .cacheMemoryOnly): return true
+    case (.waitForCache, .waitForCache): return true
+    case (.onlyFromCache, .onlyFromCache): return true
+    case (.backgroundDecode, .backgroundDecode): return true
+    case (.callbackDispatchQueue(_), .callbackDispatchQueue(_)): return true
+    case (.scaleFactor(_), .scaleFactor(_)): return true
+    case (.preloadAllAnimationData, .preloadAllAnimationData): return true
+    case (.requestModifier(_), .requestModifier(_)): return true
+    case (.processor(_), .processor(_)): return true
+    case (.cacheSerializer(_), .cacheSerializer(_)): return true
+    case (.imageModifier(_), .imageModifier(_)): return true
+    case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): return true
+    case (.onlyLoadFirstFrame, .onlyLoadFirstFrame): return true
+    case (.cacheOriginalImage, .cacheOriginalImage): return true
+    default: return false
+    }
+}
+
+
+extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
+    func lastMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? {
+        return reversed().first { $0 <== target }
+    }
+    
+    func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] {
+        return filter { !($0 <== target) }
+    }
+}
+
+public extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
+    /// The target `ImageCache` which is used.
+    public var targetCache: ImageCache? {
+        if let item = lastMatchIgnoringAssociatedValue(.targetCache(.default)),
+            case .targetCache(let cache) = item
+        {
+            return cache
+        }
+        return nil
+    }
+    
+    /// The original `ImageCache` which is used.
+    public var originalCache: ImageCache? {
+        if let item = lastMatchIgnoringAssociatedValue(.originalCache(.default)),
+            case .originalCache(let cache) = item
+        {
+            return cache
+        }
+        return targetCache
+    }
+    
+    /// The `ImageDownloader` which is specified.
+    public var downloader: ImageDownloader? {
+        if let item = lastMatchIgnoringAssociatedValue(.downloader(.default)),
+            case .downloader(let downloader) = item
+        {
+            return downloader
+        }
+        return nil
+    }
+    
+    /// Member for animation transition when using UIImageView.
+    public var transition: ImageTransition {
+        if let item = lastMatchIgnoringAssociatedValue(.transition(.none)),
+            case .transition(let transition) = item
+        {
+            return transition
+        }
+        return ImageTransition.none
+    }
+    
+    /// A `Float` value set as the priority of image download task. The value for it should be
+    /// between 0.0~1.0.
+    public var downloadPriority: Float {
+        if let item = lastMatchIgnoringAssociatedValue(.downloadPriority(0)),
+            case .downloadPriority(let priority) = item
+        {
+            return priority
+        }
+        return URLSessionTask.defaultPriority
+    }
+    
+    /// Whether an image will be always downloaded again or not.
+    public var forceRefresh: Bool {
+        return contains{ $0 <== .forceRefresh }
+    }
+
+    /// Whether an image should be got only from memory cache or download.
+    public var fromMemoryCacheOrRefresh: Bool {
+        return contains{ $0 <== .fromMemoryCacheOrRefresh }
+    }
+    
+    /// Whether the transition should always happen or not.
+    public var forceTransition: Bool {
+        return contains{ $0 <== .forceTransition }
+    }
+    
+    /// Whether cache the image only in memory or not.
+    public var cacheMemoryOnly: Bool {
+        return contains{ $0 <== .cacheMemoryOnly }
+    }
+    
+    /// Whether the caching operation will be waited or not.
+    public var waitForCache: Bool {
+        return contains{ $0 <== .waitForCache }
+    }
+    
+    /// Whether only load the images from cache or not.
+    public var onlyFromCache: Bool {
+        return contains{ $0 <== .onlyFromCache }
+    }
+    
+    /// Whether the image should be decoded in background or not.
+    public var backgroundDecode: Bool {
+        return contains{ $0 <== .backgroundDecode }
+    }
+
+    /// Whether the image data should be all loaded at once if it is an animated image.
+    public var preloadAllAnimationData: Bool {
+        return contains { $0 <== .preloadAllAnimationData }
+    }
+    
+    /// The queue of callbacks should happen from Kingfisher.
+    public var callbackDispatchQueue: DispatchQueue {
+        if let item = lastMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)),
+            case .callbackDispatchQueue(let queue) = item
+        {
+            return queue ?? DispatchQueue.main
+        }
+        return DispatchQueue.main
+    }
+    
+    /// The scale factor which should be used for the image.
+    public var scaleFactor: CGFloat {
+        if let item = lastMatchIgnoringAssociatedValue(.scaleFactor(0)),
+            case .scaleFactor(let scale) = item
+        {
+            return scale
+        }
+        return 1.0
+    }
+    
+    /// The `ImageDownloadRequestModifier` will be used before sending a download request.
+    public var modifier: ImageDownloadRequestModifier {
+        if let item = lastMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)),
+            case .requestModifier(let modifier) = item
+        {
+            return modifier
+        }
+        return NoModifier.default
+    }
+    
+    /// `ImageProcessor` for processing when the downloading finishes.
+    public var processor: ImageProcessor {
+        if let item = lastMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)),
+            case .processor(let processor) = item
+        {
+            return processor
+        }
+        return DefaultImageProcessor.default
+    }
+
+    /// `ImageModifier` for modifying right before the image is displayed.
+    public var imageModifier: ImageModifier {
+        if let item = lastMatchIgnoringAssociatedValue(.imageModifier(DefaultImageModifier.default)),
+            case .imageModifier(let imageModifier) = item
+        {
+            return imageModifier
+        }
+        return DefaultImageModifier.default
+    }
+    
+    /// `CacheSerializer` to convert image to data for storing in cache.
+    public var cacheSerializer: CacheSerializer {
+        if let item = lastMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)),
+            case .cacheSerializer(let cacheSerializer) = item
+        {
+            return cacheSerializer
+        }
+        return DefaultCacheSerializer.default
+    }
+    
+    /// Keep the existing image while setting another image to an image view. 
+    /// Or the placeholder will be used while downloading.
+    public var keepCurrentImageWhileLoading: Bool {
+        return contains { $0 <== .keepCurrentImageWhileLoading }
+    }
+    
+    public var onlyLoadFirstFrame: Bool {
+        return contains { $0 <== .onlyLoadFirstFrame }
+    }
+    
+    public var cacheOriginalImage: Bool {
+        return contains { $0 <== .cacheOriginalImage }
+    }
+}

+ 82 - 0
Example/Pods/Kingfisher/Sources/Placeholder.swift

@@ -0,0 +1,82 @@
+//
+//  Placeholder.swift
+//  Kingfisher
+//
+//  Created by Tieme van Veen on 28/08/2017.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+    import AppKit
+#else
+    import UIKit
+#endif
+
+
+/// Represent a placeholder type which could be set while loading as well as
+/// loading finished without getting an image.
+public protocol Placeholder {
+    
+    /// How the placeholder should be added to a given image view.
+    func add(to imageView: ImageView)
+    
+    /// How the placeholder should be removed from a given image view.
+    func remove(from imageView: ImageView)
+}
+
+/// Default implementation of an image placeholder. The image will be set or 
+/// reset directly for `image` property of the image view.
+extension Placeholder where Self: Image {
+    
+    /// How the placeholder should be added to a given image view.
+    public func add(to imageView: ImageView) { imageView.image = self }
+    
+    /// How the placeholder should be removed from a given image view.
+    public func remove(from imageView: ImageView) { imageView.image = nil }
+}
+
+extension Image: Placeholder {}
+
+/// Default implementation of an arbitrary view as placeholder. The view will be 
+/// added as a subview when adding and be removed from its super view when removing.
+///
+/// To use your customize View type as placeholder, simply let it conforming to 
+/// `Placeholder` by `extension MyView: Placeholder {}`.
+extension Placeholder where Self: View {
+    
+    /// How the placeholder should be added to a given image view.
+    public func add(to imageView: ImageView) {
+        imageView.addSubview(self)
+
+        self.translatesAutoresizingMaskIntoConstraints = false
+        NSLayoutConstraint.activate([
+            NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: imageView, attribute: .centerX, multiplier: 1, constant: 0),
+            NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: imageView, attribute: .centerY, multiplier: 1, constant: 0),
+            NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: 1, constant: 0),
+            NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .width, multiplier: 1, constant: 0)
+            ])
+    }
+
+    /// How the placeholder should be removed from a given image view.
+    public func remove(from imageView: ImageView) {
+        self.removeFromSuperview()
+    }
+}

+ 53 - 0
Example/Pods/Kingfisher/Sources/RequestModifier.swift

@@ -0,0 +1,53 @@
+//
+//  RequestModifier.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2016/09/05.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+/// Request modifier of image downloader.
+public protocol ImageDownloadRequestModifier {
+    func modified(for request: URLRequest) -> URLRequest?
+}
+
+struct NoModifier: ImageDownloadRequestModifier {
+    static let `default` = NoModifier()
+    private init() {}
+    func modified(for request: URLRequest) -> URLRequest? {
+        return request
+    }
+}
+
+public struct AnyModifier: ImageDownloadRequestModifier {
+    
+    let block: (URLRequest) -> URLRequest?
+    
+    public func modified(for request: URLRequest) -> URLRequest? {
+        return block(request)
+    }
+    
+    public init(modify: @escaping (URLRequest) -> URLRequest? ) {
+        block = modify
+    }
+}

+ 74 - 0
Example/Pods/Kingfisher/Sources/Resource.swift

@@ -0,0 +1,74 @@
+//
+//  Resource.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+
+/// `Resource` protocol defines how to download and cache a resource from network.
+public protocol Resource {
+    /// The key used in cache.
+    var cacheKey: String { get }
+    
+    /// The target image URL.
+    var downloadURL: URL { get }
+}
+
+/**
+ ImageResource is a simple combination of `downloadURL` and `cacheKey`.
+ 
+ When passed to image view set methods, Kingfisher will try to download the target 
+ image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache.
+ */
+public struct ImageResource: Resource {
+    /// The key used in cache.
+    public let cacheKey: String
+    
+    /// The target image URL.
+    public let downloadURL: URL
+    
+    /**
+     Create a resource.
+     
+     - parameter downloadURL: The target image URL.
+     - parameter cacheKey:    The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key.
+     
+     - returns: A resource.
+     */
+    public init(downloadURL: URL, cacheKey: String? = nil) {
+        self.downloadURL = downloadURL
+        self.cacheKey = cacheKey ?? downloadURL.absoluteString
+    }
+}
+
+/**
+ URL conforms to `Resource` in Kingfisher.
+ The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`.
+ If you need customize the url and/or cache key, use `ImageResource` instead.
+ */
+extension URL: Resource {
+    public var cacheKey: String { return absoluteString }
+    public var downloadURL: URL { return self }
+}

+ 285 - 0
Example/Pods/Kingfisher/Sources/String+MD5.swift

@@ -0,0 +1,285 @@
+//
+//  String+MD5.swift
+//  Kingfisher
+//
+// To date, adding CommonCrypto to a Swift framework is problematic. See:
+// http://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework
+// We're using a subset and modified version of CryptoSwift as an alternative.
+// The following is an altered source version that only includes MD5. The original software can be found at:
+// https://github.com/krzyzanowskim/CryptoSwift
+// This is the original copyright notice:
+
+/*
+Copyright (C) 2014 Marcin Krzyżanowski <marcin.krzyzanowski@gmail.com>
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from the use of this software.
+Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+- This notice may not be removed or altered from any source or binary distribution.
+*/
+
+import Foundation
+
+extension String: KingfisherCompatible { }
+
+extension Kingfisher where Base == String {
+    public var md5: String {
+        if let data = base.data(using: .utf8, allowLossyConversion: true) {
+
+            let message = data.withUnsafeBytes { bytes -> [UInt8] in
+                return Array(UnsafeBufferPointer(start: bytes, count: data.count))
+            }
+
+            let MD5Calculator = MD5(message)
+            let MD5Data = MD5Calculator.calculate()
+
+            var MD5String = String()
+            for c in MD5Data {
+                MD5String += String(format: "%02x", c)
+            }
+            return MD5String
+
+        } else {
+            return base
+        }
+    }
+}
+
+
+/** array of bytes, little-endian representation */
+func arrayOfBytes<T>(_ value: T, length: Int? = nil) -> [UInt8] {
+    let totalBytes = length ?? (MemoryLayout<T>.size * 8)
+    
+    let valuePointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
+    valuePointer.pointee = value
+
+    let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { (bytesPointer) -> [UInt8] in
+        var bytes = [UInt8](repeating: 0, count: totalBytes)
+        for j in 0..<min(MemoryLayout<T>.size, totalBytes) {
+            bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee
+        }
+        return bytes
+    }
+
+    #if swift(>=4.1)
+    valuePointer.deinitialize(count: 1)
+    valuePointer.deallocate()
+    #else
+    valuePointer.deinitialize()
+    valuePointer.deallocate(capacity: 1)
+    #endif
+    
+    return bytes
+}
+
+extension Int {
+    /** Array of bytes with optional padding (little-endian) */
+    func bytes(_ totalBytes: Int = MemoryLayout<Int>.size) -> [UInt8] {
+        return arrayOfBytes(self, length: totalBytes)
+    }
+    
+}
+
+extension NSMutableData {
+    
+    /** Convenient way to append bytes */
+    func appendBytes(_ arrayOfBytes: [UInt8]) {
+        append(arrayOfBytes, length: arrayOfBytes.count)
+    }
+    
+}
+
+protocol HashProtocol {
+    var message: Array<UInt8> { get }
+    
+    /** Common part for hash calculation. Prepare header data. */
+    func prepare(_ len: Int) -> Array<UInt8>
+}
+
+extension HashProtocol {
+    
+    func prepare(_ len: Int) -> Array<UInt8> {
+        var tmpMessage = message
+        
+        // Step 1. Append Padding Bits
+        tmpMessage.append(0x80) // append one bit (UInt8 with one bit) to message
+        
+        // append "0" bit until message length in bits ≡ 448 (mod 512)
+        var msgLength = tmpMessage.count
+        var counter = 0
+        
+        while msgLength % len != (len - 8) {
+            counter += 1
+            msgLength += 1
+        }
+        
+        tmpMessage += Array<UInt8>(repeating: 0, count: counter)
+        return tmpMessage
+    }
+}
+
+func toUInt32Array(_ slice: ArraySlice<UInt8>) -> Array<UInt32> {
+    var result = Array<UInt32>()
+    result.reserveCapacity(16)
+    
+    for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout<UInt32>.size) {
+        let d0 = UInt32(slice[idx.advanced(by: 3)]) << 24
+        let d1 = UInt32(slice[idx.advanced(by: 2)]) << 16
+        let d2 = UInt32(slice[idx.advanced(by: 1)]) << 8
+        let d3 = UInt32(slice[idx])
+        let val: UInt32 = d0 | d1 | d2 | d3
+                         
+        result.append(val)
+    }
+    return result
+}
+
+struct BytesIterator: IteratorProtocol {
+    
+    let chunkSize: Int
+    let data: [UInt8]
+    
+    init(chunkSize: Int, data: [UInt8]) {
+        self.chunkSize = chunkSize
+        self.data = data
+    }
+    
+    var offset = 0
+    
+    mutating func next() -> ArraySlice<UInt8>? {
+        let end = min(chunkSize, data.count - offset)
+        let result = data[offset..<offset + end]
+        offset += result.count
+        return result.count > 0 ? result : nil
+    }
+}
+
+struct BytesSequence: Sequence {
+    let chunkSize: Int
+    let data: [UInt8]
+    
+    func makeIterator() -> BytesIterator {
+        return BytesIterator(chunkSize: chunkSize, data: data)
+    }
+}
+
+func rotateLeft(_ value: UInt32, bits: UInt32) -> UInt32 {
+    return ((value << bits) & 0xFFFFFFFF) | (value >> (32 - bits))
+}
+
+class MD5: HashProtocol {
+    
+    static let size = 16 // 128 / 8
+    let message: [UInt8]
+    
+    init (_ message: [UInt8]) {
+        self.message = message
+    }
+    
+    /** specifies the per-round shift amounts */
+    private let shifts: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+                                    5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
+                                    4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+                                    6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]
+    
+    /** binary integer part of the sines of integers (Radians) */
+    private let sines: [UInt32] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+                               0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+                               0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+                               0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+                               0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+                               0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+                               0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+                               0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+                               0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+                               0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+                               0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
+                               0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+                               0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+                               0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+                               0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+                               0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391]
+    
+    private let hashes: [UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
+    
+    func calculate() -> [UInt8] {
+        var tmpMessage = prepare(64)
+        tmpMessage.reserveCapacity(tmpMessage.count + 4)
+        
+        // hash values
+        var hh = hashes
+        
+        // Step 2. Append Length a 64-bit representation of lengthInBits
+        let lengthInBits = (message.count * 8)
+        let lengthBytes = lengthInBits.bytes(64 / 8)
+        tmpMessage += lengthBytes.reversed()
+
+        // Process the message in successive 512-bit chunks:
+        let chunkSizeBytes = 512 / 8 // 64
+
+        for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) {
+            // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
+            var M = toUInt32Array(chunk)
+            assert(M.count == 16, "Invalid array")
+            
+            // Initialize hash value for this chunk:
+            var A: UInt32 = hh[0]
+            var B: UInt32 = hh[1]
+            var C: UInt32 = hh[2]
+            var D: UInt32 = hh[3]
+            
+            var dTemp: UInt32 = 0
+            
+            // Main loop
+            for j in 0 ..< sines.count {
+                var g = 0
+                var F: UInt32 = 0
+                
+                switch j {
+                case 0...15:
+                    F = (B & C) | ((~B) & D)
+                    g = j
+                    break
+                case 16...31:
+                    F = (D & B) | (~D & C)
+                    g = (5 * j + 1) % 16
+                    break
+                case 32...47:
+                    F = B ^ C ^ D
+                    g = (3 * j + 5) % 16
+                    break
+                case 48...63:
+                    F = C ^ (B | (~D))
+                    g = (7 * j) % 16
+                    break
+                default:
+                    break
+                }
+                dTemp = D
+                D = C
+                C = B
+                B = B &+ rotateLeft((A &+ F &+ sines[j] &+ M[g]), bits: shifts[j])
+                A = dTemp
+            }
+            
+            hh[0] = hh[0] &+ A
+            hh[1] = hh[1] &+ B
+            hh[2] = hh[2] &+ C
+            hh[3] = hh[3] &+ D
+        }
+        
+        var result = [UInt8]()
+        result.reserveCapacity(hh.count / 4)
+        
+        hh.forEach {
+            let itemLE = $0.littleEndian
+            let r1 = UInt8(itemLE & 0xff)
+            let r2 = UInt8((itemLE >> 8) & 0xff)
+            let r3 = UInt8((itemLE >> 16) & 0xff)
+            let r4 = UInt8((itemLE >> 24) & 0xff)
+            result += [r1, r2, r3, r4]
+        }
+        return result
+    }
+}

+ 40 - 0
Example/Pods/Kingfisher/Sources/ThreadHelper.swift

@@ -0,0 +1,40 @@
+//
+//  ThreadHelper.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/10/9.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+extension DispatchQueue {
+    // This method will dispatch the `block` to self.
+    // If `self` is the main queue, and current thread is main thread, the block
+    // will be invoked immediately instead of being dispatched.
+    func safeAsync(_ block: @escaping ()->()) {
+        if self === DispatchQueue.main && Thread.isMainThread {
+            block()
+        } else {
+            async { block() }
+        }
+    }
+}

+ 274 - 0
Example/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift

@@ -0,0 +1,274 @@
+//
+//  UIButton+Kingfisher.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/13.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import UIKit
+
+// MARK: - Set Images
+/**
+ *	Set image to use in button from web for a specified state.
+ */
+extension Kingfisher where Base: UIButton {
+    /**
+     Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and
+     completion handler.
+     
+     - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
+     - parameter state:             The state that uses the specified image.
+     - parameter placeholder:       A placeholder image when retrieving the image at URL.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called when the image downloading progress gets updated.
+     - parameter completionHandler: Called when the image retrieved and set.
+     
+     - returns: A task represents the retrieving process.
+     
+     - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
+     The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
+     
+     If `resource` is `nil`, the `placeholder` image will be set and
+     `completionHandler` will be called with both `error` and `image` being `nil`.
+     */
+    @discardableResult
+    public func setImage(with resource: Resource?,
+                         for state: UIControl.State,
+                         placeholder: UIImage? = nil,
+                         options: KingfisherOptionsInfo? = nil,
+                         progressBlock: DownloadProgressBlock? = nil,
+                         completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
+    {
+        guard let resource = resource else {
+            base.setImage(placeholder, for: state)
+            setWebURL(nil, for: state)
+            completionHandler?(nil, nil, .none, nil)
+            return .empty
+        }
+        
+        let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
+        if !options.keepCurrentImageWhileLoading {
+            base.setImage(placeholder, for: state)
+        }
+        
+        setWebURL(resource.downloadURL, for: state)
+        let task = KingfisherManager.shared.retrieveImage(
+            with: resource,
+            options: options,
+            progressBlock: { receivedSize, totalSize in
+                guard resource.downloadURL == self.webURL(for: state) else {
+                    return
+                }
+                if let progressBlock = progressBlock {
+                    progressBlock(receivedSize, totalSize)
+                }
+            },
+            completionHandler: {[weak base] image, error, cacheType, imageURL in
+                DispatchQueue.main.safeAsync {
+                    guard let strongBase = base, imageURL == self.webURL(for: state) else {
+                        completionHandler?(image, error, cacheType, imageURL)
+                        return
+                    }
+                    self.setImageTask(nil)
+                    if image != nil {
+                        strongBase.setImage(image, for: state)
+                    }
+
+                    completionHandler?(image, error, cacheType, imageURL)
+                }
+            })
+        
+        setImageTask(task)
+        return task
+    }
+    
+    /**
+     Cancel the image download task bounded to the image view if it is running.
+     Nothing will happen if the downloading has already finished.
+     */
+    public func cancelImageDownloadTask() {
+        imageTask?.cancel()
+    }
+    
+    /**
+     Set the background image to use for a specified state with a resource,
+     a placeholder image, options progress handler and completion handler.
+     
+     - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
+     - parameter state:             The state that uses the specified image.
+     - parameter placeholder:       A placeholder image when retrieving the image at URL.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called when the image downloading progress gets updated.
+     - parameter completionHandler: Called when the image retrieved and set.
+     
+     - returns: A task represents the retrieving process.
+     
+     - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
+     The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
+     
+     If `resource` is `nil`, the `placeholder` image will be set and
+     `completionHandler` will be called with both `error` and `image` being `nil`.
+     */
+    @discardableResult
+    public func setBackgroundImage(with resource: Resource?,
+                                   for state: UIControl.State,
+                                   placeholder: UIImage? = nil,
+                                   options: KingfisherOptionsInfo? = nil,
+                                   progressBlock: DownloadProgressBlock? = nil,
+                                   completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
+    {
+        guard let resource = resource else {
+            base.setBackgroundImage(placeholder, for: state)
+            setBackgroundWebURL(nil, for: state)
+            completionHandler?(nil, nil, .none, nil)
+            return .empty
+        }
+        
+        let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
+        if !options.keepCurrentImageWhileLoading {
+            base.setBackgroundImage(placeholder, for: state)
+        }
+        
+        setBackgroundWebURL(resource.downloadURL, for: state)
+        let task = KingfisherManager.shared.retrieveImage(
+            with: resource,
+            options: options,
+            progressBlock: { receivedSize, totalSize in
+                guard resource.downloadURL == self.backgroundWebURL(for: state) else {
+                    return
+                }
+                if let progressBlock = progressBlock {
+                    progressBlock(receivedSize, totalSize)
+                }
+            },
+            completionHandler: { [weak base] image, error, cacheType, imageURL in
+                DispatchQueue.main.safeAsync {
+                    guard let strongBase = base, imageURL == self.backgroundWebURL(for: state) else {
+                        completionHandler?(image, error, cacheType, imageURL)
+                        return
+                    }
+                    self.setBackgroundImageTask(nil)
+                    if image != nil {
+                        strongBase.setBackgroundImage(image, for: state)
+                    }
+                    completionHandler?(image, error, cacheType, imageURL)
+                }
+            })
+        
+        setBackgroundImageTask(task)
+        return task
+    }
+    
+    /**
+     Cancel the background image download task bounded to the image view if it is running.
+     Nothing will happen if the downloading has already finished.
+     */
+    public func cancelBackgroundImageDownloadTask() {
+        backgroundImageTask?.cancel()
+    }
+
+}
+
+// MARK: - Associated Object
+private var lastURLKey: Void?
+private var imageTaskKey: Void?
+
+extension Kingfisher where Base: UIButton {
+    /**
+     Get the image URL binded to this button for a specified state.
+     
+     - parameter state: The state that uses the specified image.
+     
+     - returns: Current URL for image.
+     */
+    public func webURL(for state: UIControl.State) -> URL? {
+        return webURLs[NSNumber(value:state.rawValue)] as? URL
+    }
+    
+    fileprivate func setWebURL(_ url: URL?, for state: UIControl.State) {
+        webURLs[NSNumber(value:state.rawValue)] = url
+    }
+    
+    fileprivate var webURLs: NSMutableDictionary {
+        var dictionary = objc_getAssociatedObject(base, &lastURLKey) as? NSMutableDictionary
+        if dictionary == nil {
+            dictionary = NSMutableDictionary()
+            setWebURLs(dictionary!)
+        }
+        return dictionary!
+    }
+    
+    fileprivate func setWebURLs(_ URLs: NSMutableDictionary) {
+        objc_setAssociatedObject(base, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+    
+    fileprivate var imageTask: RetrieveImageTask? {
+        return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask
+    }
+    
+    fileprivate func setImageTask(_ task: RetrieveImageTask?) {
+        objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+}
+
+
+private var lastBackgroundURLKey: Void?
+private var backgroundImageTaskKey: Void?
+
+
+extension Kingfisher where Base: UIButton {
+    /**
+     Get the background image URL binded to this button for a specified state.
+     
+     - parameter state: The state that uses the specified background image.
+     
+     - returns: Current URL for background image.
+     */
+    public func backgroundWebURL(for state: UIControl.State) -> URL? {
+        return backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL
+    }
+    
+    fileprivate func setBackgroundWebURL(_ url: URL?, for state: UIControl.State) {
+        backgroundWebURLs[NSNumber(value:state.rawValue)] = url
+    }
+    
+    fileprivate var backgroundWebURLs: NSMutableDictionary {
+        var dictionary = objc_getAssociatedObject(base, &lastBackgroundURLKey) as? NSMutableDictionary
+        if dictionary == nil {
+            dictionary = NSMutableDictionary()
+            setBackgroundWebURLs(dictionary!)
+        }
+        return dictionary!
+    }
+    
+    fileprivate func setBackgroundWebURLs(_ URLs: NSMutableDictionary) {
+        objc_setAssociatedObject(base, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+    
+    fileprivate var backgroundImageTask: RetrieveImageTask? {
+        return objc_getAssociatedObject(base, &backgroundImageTaskKey) as? RetrieveImageTask
+    }
+    
+    fileprivate func setBackgroundImageTask(_ task: RetrieveImageTask?) {
+        objc_setAssociatedObject(base, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+}

+ 18 - 3
Example/Pods/Local Podspecs/BFFramework.podspec.json

@@ -1,7 +1,8 @@
 {
   "name": "BFFramework",
   "version": "0.1.0",
-  "summary": "A short description of BFFramework.",
+  "summary": "Byte fllow 基础组件库",
+  "swift_versions": "5.0",
   "description": "TODO: Add long description of the pod here.",
   "homepage": "https://github.com/287971051@qq.com/BFFramework",
   "license": {
@@ -12,11 +13,25 @@
     "287971051@qq.com": "287971051@qq.com"
   },
   "source": {
-    "git": "https://github.com/287971051@qq.com/BFFramework.git",
+    "git": "https://git.yishihui.comiOS/BFFramework.git",
     "tag": "0.1.0"
   },
   "platforms": {
     "ios": "9.0"
   },
-  "source_files": "BFFramework/Classes/**/*"
+  "source_files": "BFFramework/Classes/**/*",
+  "resource_bundles": {
+    "BFFramework": [
+      "BFFramework/Assets/*.png"
+    ]
+  },
+  "dependencies": {
+    "SnapKit": [
+      "4.2.0"
+    ],
+    "Kingfisher": [
+      "4.10.1"
+    ]
+  },
+  "swift_version": "5.0"
 }

+ 10 - 2
Example/Pods/Manifest.lock

@@ -1,7 +1,11 @@
 PODS:
-  - BFFramework (0.1.0)
+  - BFFramework (0.1.0):
+    - Kingfisher (= 4.10.1)
+    - SnapKit (= 4.2.0)
+  - Kingfisher (4.10.1)
   - Nimble (8.0.9)
   - Quick (2.2.1)
+  - SnapKit (4.2.0)
 
 DEPENDENCIES:
   - BFFramework (from `../`)
@@ -10,17 +14,21 @@ DEPENDENCIES:
 
 SPEC REPOS:
   trunk:
+    - Kingfisher
     - Nimble
     - Quick
+    - SnapKit
 
 EXTERNAL SOURCES:
   BFFramework:
     :path: "../"
 
 SPEC CHECKSUMS:
-  BFFramework: 08240bdb34be14676bd29d91e82050f4fad7b8f3
+  BFFramework: 2fa6044ae45950867e4bfcbbe88a3ed3cfda65ea
+  Kingfisher: c148cd7b47ebde9989f6bc7c27dcaa79d81279a0
   Nimble: 98b888285a615fd34f20e61753cf58ea1402bde4
   Quick: f5754d69b7013f5864c29aab9ae6f0c79c5bc200
+  SnapKit: fe8a619752f3f27075cc9a90244d75c6c3f27e2a
 
 PODFILE CHECKSUM: fbb4c1ccef26f44908c6add1513fff562052991a
 

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 752 - 372
Example/Pods/Pods.xcodeproj/project.pbxproj


+ 19 - 0
Example/Pods/SnapKit/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 129 - 0
Example/Pods/SnapKit/README.md

@@ -0,0 +1,129 @@
+<img src="http://snapkit.io/images/banner.jpg" alt="" />
+
+SnapKit is a DSL to make Auto Layout easy on both iOS and OS X.
+
+[![Build Status](https://travis-ci.org/SnapKit/SnapKit.svg)](https://travis-ci.org/SnapKit/SnapKit)
+[![Platform](https://img.shields.io/cocoapods/p/SnapKit.svg?style=flat)](https://github.com/SnapKit/SnapKit)
+[![Cocoapods Compatible](https://img.shields.io/cocoapods/v/SnapKit.svg)](https://cocoapods.org/pods/SnapKit)
+[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
+
+#### ⚠️ **To use with Swift 3.x please ensure you are using >= 3.0.0** ⚠️ 
+#### ⚠️ **To use with Swift 4.x please ensure you are using >= 4.0.0** ⚠️ 
+
+## Contents
+
+- [Requirements](#requirements)
+- [Migration Guides](#migration-guides)
+- [Communication](#communication)
+- [Installation](#installation)
+- [Usage](#usage)
+- [Credits](#credits)
+- [License](#license)
+
+## Requirements
+
+- iOS 8.0+ / Mac OS X 10.11+ / tvOS 9.0+
+- Xcode 9.0+
+- Swift 3.0+
+
+## Communication
+
+- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/snapkit). (Tag 'snapkit')
+- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/snapkit).
+- If you **found a bug**, open an issue.
+- If you **have a feature request**, open an issue.
+- If you **want to contribute**, submit a pull request.
+
+
+## Installation
+
+### CocoaPods
+
+[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
+
+```bash
+$ gem install cocoapods
+```
+
+> CocoaPods 1.1.0+ is required to build SnapKit 4.0.0+.
+
+To integrate SnapKit into your Xcode project using CocoaPods, specify it in your `Podfile`:
+
+```ruby
+source 'https://github.com/CocoaPods/Specs.git'
+platform :ios, '10.0'
+use_frameworks!
+
+target '<Your Target Name>' do
+    pod 'SnapKit', '~> 4.0.0'
+end
+```
+
+Then, run the following command:
+
+```bash
+$ pod install
+```
+
+### Carthage
+
+[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
+
+You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
+
+```bash
+$ brew update
+$ brew install carthage
+```
+
+To integrate SnapKit into your Xcode project using Carthage, specify it in your `Cartfile`:
+
+```ogdl
+github "SnapKit/SnapKit" ~> 4.0.0
+```
+
+Run `carthage update` to build the framework and drag the built `SnapKit.framework` into your Xcode project.
+
+### Manually
+
+If you prefer not to use either of the aforementioned dependency managers, you can integrate SnapKit into your project manually.
+
+---
+
+## Usage
+
+### Quick Start
+
+```swift
+import SnapKit
+
+class MyViewController: UIViewController {
+
+    lazy var box = UIView()
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        self.view.addSubview(box)
+        box.snp.makeConstraints { (make) -> Void in
+           make.width.height.equalTo(50)
+           make.center.equalTo(self.view)
+        }
+    }
+
+}
+```
+
+### Resources
+
+- [Documentation](http://snapkit.io/docs/)
+- [F.A.Q.](http://snapkit.io/faq/)
+
+## Credits
+
+- Robert Payne ([@robertjpayne](https://twitter.com/robertjpayne))
+- Many other contributors
+
+## License
+
+SnapKit is released under the MIT license. See LICENSE for details.

+ 306 - 0
Example/Pods/SnapKit/Source/Constraint.swift

@@ -0,0 +1,306 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+public final class Constraint {
+
+    internal let sourceLocation: (String, UInt)
+    internal let label: String?
+
+    private let from: ConstraintItem
+    private let to: ConstraintItem
+    private let relation: ConstraintRelation
+    private let multiplier: ConstraintMultiplierTarget
+    private var constant: ConstraintConstantTarget {
+        didSet {
+            self.updateConstantAndPriorityIfNeeded()
+        }
+    }
+    private var priority: ConstraintPriorityTarget {
+        didSet {
+          self.updateConstantAndPriorityIfNeeded()
+        }
+    }
+    public var layoutConstraints: [LayoutConstraint]
+    
+    public var isActive: Bool {
+        set {
+            if newValue {
+                activate()
+            }
+            else {
+                deactivate()
+            }
+        }
+        
+        get {
+            for layoutConstraint in self.layoutConstraints {
+                if layoutConstraint.isActive {
+                    return true
+                }
+            }
+            return false
+        }
+    }
+    
+    // MARK: Initialization
+
+    internal init(from: ConstraintItem,
+                  to: ConstraintItem,
+                  relation: ConstraintRelation,
+                  sourceLocation: (String, UInt),
+                  label: String?,
+                  multiplier: ConstraintMultiplierTarget,
+                  constant: ConstraintConstantTarget,
+                  priority: ConstraintPriorityTarget) {
+        self.from = from
+        self.to = to
+        self.relation = relation
+        self.sourceLocation = sourceLocation
+        self.label = label
+        self.multiplier = multiplier
+        self.constant = constant
+        self.priority = priority
+        self.layoutConstraints = []
+
+        // get attributes
+        let layoutFromAttributes = self.from.attributes.layoutAttributes
+        let layoutToAttributes = self.to.attributes.layoutAttributes
+
+        // get layout from
+        let layoutFrom = self.from.layoutConstraintItem!
+
+        // get relation
+        let layoutRelation = self.relation.layoutRelation
+
+        for layoutFromAttribute in layoutFromAttributes {
+            // get layout to attribute
+            let layoutToAttribute: LayoutAttribute
+            #if os(iOS) || os(tvOS)
+                if layoutToAttributes.count > 0 {
+                    if self.from.attributes == .edges && self.to.attributes == .margins {
+                        switch layoutFromAttribute {
+                        case .left:
+                            layoutToAttribute = .leftMargin
+                        case .right:
+                            layoutToAttribute = .rightMargin
+                        case .top:
+                            layoutToAttribute = .topMargin
+                        case .bottom:
+                            layoutToAttribute = .bottomMargin
+                        default:
+                            fatalError()
+                        }
+                    } else if self.from.attributes == .margins && self.to.attributes == .edges {
+                        switch layoutFromAttribute {
+                        case .leftMargin:
+                            layoutToAttribute = .left
+                        case .rightMargin:
+                            layoutToAttribute = .right
+                        case .topMargin:
+                            layoutToAttribute = .top
+                        case .bottomMargin:
+                            layoutToAttribute = .bottom
+                        default:
+                            fatalError()
+                        }
+                    } else if self.from.attributes == self.to.attributes {
+                        layoutToAttribute = layoutFromAttribute
+                    } else {
+                        layoutToAttribute = layoutToAttributes[0]
+                    }
+                } else {
+                    if self.to.target == nil && (layoutFromAttribute == .centerX || layoutFromAttribute == .centerY) {
+                        layoutToAttribute = layoutFromAttribute == .centerX ? .left : .top
+                    } else {
+                        layoutToAttribute = layoutFromAttribute
+                    }
+                }
+            #else
+                if self.from.attributes == self.to.attributes {
+                    layoutToAttribute = layoutFromAttribute
+                } else if layoutToAttributes.count > 0 {
+                    layoutToAttribute = layoutToAttributes[0]
+                } else {
+                    layoutToAttribute = layoutFromAttribute
+                }
+            #endif
+
+            // get layout constant
+            let layoutConstant: CGFloat = self.constant.constraintConstantTargetValueFor(layoutAttribute: layoutToAttribute)
+
+            // get layout to
+            var layoutTo: AnyObject? = self.to.target
+
+            // use superview if possible
+            if layoutTo == nil && layoutToAttribute != .width && layoutToAttribute != .height {
+                layoutTo = layoutFrom.superview
+            }
+
+            // create layout constraint
+            let layoutConstraint = LayoutConstraint(
+                item: layoutFrom,
+                attribute: layoutFromAttribute,
+                relatedBy: layoutRelation,
+                toItem: layoutTo,
+                attribute: layoutToAttribute,
+                multiplier: self.multiplier.constraintMultiplierTargetValue,
+                constant: layoutConstant
+            )
+
+            // set label
+            layoutConstraint.label = self.label
+
+            // set priority
+            layoutConstraint.priority = LayoutPriority(rawValue: self.priority.constraintPriorityTargetValue)
+
+            // set constraint
+            layoutConstraint.constraint = self
+
+            // append
+            self.layoutConstraints.append(layoutConstraint)
+        }
+    }
+
+    // MARK: Public
+
+    @available(*, deprecated:3.0, message:"Use activate().")
+    public func install() {
+        self.activate()
+    }
+
+    @available(*, deprecated:3.0, message:"Use deactivate().")
+    public func uninstall() {
+        self.deactivate()
+    }
+
+    public func activate() {
+        self.activateIfNeeded()
+    }
+
+    public func deactivate() {
+        self.deactivateIfNeeded()
+    }
+
+    @discardableResult
+    public func update(offset: ConstraintOffsetTarget) -> Constraint {
+        self.constant = offset.constraintOffsetTargetValue
+        return self
+    }
+
+    @discardableResult
+    public func update(inset: ConstraintInsetTarget) -> Constraint {
+        self.constant = inset.constraintInsetTargetValue
+        return self
+    }
+
+    @discardableResult
+    public func update(priority: ConstraintPriorityTarget) -> Constraint {
+        self.priority = priority.constraintPriorityTargetValue
+        return self
+    }
+
+    @discardableResult
+    public func update(priority: ConstraintPriority) -> Constraint {
+        self.priority = priority.value
+        return self
+    }
+
+    @available(*, deprecated:3.0, message:"Use update(offset: ConstraintOffsetTarget) instead.")
+    public func updateOffset(amount: ConstraintOffsetTarget) -> Void { self.update(offset: amount) }
+
+    @available(*, deprecated:3.0, message:"Use update(inset: ConstraintInsetTarget) instead.")
+    public func updateInsets(amount: ConstraintInsetTarget) -> Void { self.update(inset: amount) }
+
+    @available(*, deprecated:3.0, message:"Use update(priority: ConstraintPriorityTarget) instead.")
+    public func updatePriority(amount: ConstraintPriorityTarget) -> Void { self.update(priority: amount) }
+
+    @available(*, obsoleted:3.0, message:"Use update(priority: ConstraintPriorityTarget) instead.")
+    public func updatePriorityRequired() -> Void {}
+
+    @available(*, obsoleted:3.0, message:"Use update(priority: ConstraintPriorityTarget) instead.")
+    public func updatePriorityHigh() -> Void { fatalError("Must be implemented by Concrete subclass.") }
+
+    @available(*, obsoleted:3.0, message:"Use update(priority: ConstraintPriorityTarget) instead.")
+    public func updatePriorityMedium() -> Void { fatalError("Must be implemented by Concrete subclass.") }
+
+    @available(*, obsoleted:3.0, message:"Use update(priority: ConstraintPriorityTarget) instead.")
+    public func updatePriorityLow() -> Void { fatalError("Must be implemented by Concrete subclass.") }
+
+    // MARK: Internal
+
+    internal func updateConstantAndPriorityIfNeeded() {
+        for layoutConstraint in self.layoutConstraints {
+            let attribute = (layoutConstraint.secondAttribute == .notAnAttribute) ? layoutConstraint.firstAttribute : layoutConstraint.secondAttribute
+            layoutConstraint.constant = self.constant.constraintConstantTargetValueFor(layoutAttribute: attribute)
+
+            let requiredPriority = ConstraintPriority.required.value
+            if (layoutConstraint.priority.rawValue < requiredPriority), (self.priority.constraintPriorityTargetValue != requiredPriority) {
+                layoutConstraint.priority = LayoutPriority(rawValue: self.priority.constraintPriorityTargetValue)
+            }
+        }
+    }
+
+    internal func activateIfNeeded(updatingExisting: Bool = false) {
+        guard let item = self.from.layoutConstraintItem else {
+            print("WARNING: SnapKit failed to get from item from constraint. Activate will be a no-op.")
+            return
+        }
+        let layoutConstraints = self.layoutConstraints
+
+        if updatingExisting {
+            var existingLayoutConstraints: [LayoutConstraint] = []
+            for constraint in item.constraints {
+                existingLayoutConstraints += constraint.layoutConstraints
+            }
+
+            for layoutConstraint in layoutConstraints {
+                let existingLayoutConstraint = existingLayoutConstraints.first { $0 == layoutConstraint }
+                guard let updateLayoutConstraint = existingLayoutConstraint else {
+                    fatalError("Updated constraint could not find existing matching constraint to update: \(layoutConstraint)")
+                }
+
+                let updateLayoutAttribute = (updateLayoutConstraint.secondAttribute == .notAnAttribute) ? updateLayoutConstraint.firstAttribute : updateLayoutConstraint.secondAttribute
+                updateLayoutConstraint.constant = self.constant.constraintConstantTargetValueFor(layoutAttribute: updateLayoutAttribute)
+            }
+        } else {
+            NSLayoutConstraint.activate(layoutConstraints)
+            item.add(constraints: [self])
+        }
+    }
+
+    internal func deactivateIfNeeded() {
+        guard let item = self.from.layoutConstraintItem else {
+            print("WARNING: SnapKit failed to get from item from constraint. Deactivate will be a no-op.")
+            return
+        }
+        let layoutConstraints = self.layoutConstraints
+        NSLayoutConstraint.deactivate(layoutConstraints)
+        item.remove(constraints: [self])
+    }
+}

+ 195 - 0
Example/Pods/SnapKit/Source/ConstraintAttributes.swift

@@ -0,0 +1,195 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+internal struct ConstraintAttributes : OptionSet, ExpressibleByIntegerLiteral {
+    
+    typealias IntegerLiteralType = UInt
+    
+    internal init(rawValue: UInt) {
+        self.rawValue = rawValue
+    }
+    internal init(_ rawValue: UInt) {
+        self.init(rawValue: rawValue)
+    }
+    internal init(nilLiteral: ()) {
+        self.rawValue = 0
+    }
+    internal init(integerLiteral rawValue: IntegerLiteralType) {
+        self.init(rawValue: rawValue)
+    }
+    
+    internal private(set) var rawValue: UInt
+    internal static var allZeros: ConstraintAttributes { return 0 }
+    internal static func convertFromNilLiteral() -> ConstraintAttributes { return 0 }
+    internal var boolValue: Bool { return self.rawValue != 0 }
+    
+    internal func toRaw() -> UInt { return self.rawValue }
+    internal static func fromRaw(_ raw: UInt) -> ConstraintAttributes? { return self.init(raw) }
+    internal static func fromMask(_ raw: UInt) -> ConstraintAttributes { return self.init(raw) }
+    
+    // normal
+    
+    internal static var none: ConstraintAttributes { return 0 }
+    internal static var left: ConstraintAttributes { return 1 }
+    internal static var top: ConstraintAttributes {  return 2 }
+    internal static var right: ConstraintAttributes { return 4 }
+    internal static var bottom: ConstraintAttributes { return 8 }
+    internal static var leading: ConstraintAttributes { return 16 }
+    internal static var trailing: ConstraintAttributes { return 32 }
+    internal static var width: ConstraintAttributes { return 64 }
+    internal static var height: ConstraintAttributes { return 128 }
+    internal static var centerX: ConstraintAttributes { return 256 }
+    internal static var centerY: ConstraintAttributes { return 512 }
+    internal static var lastBaseline: ConstraintAttributes { return 1024 }
+    
+    @available(iOS 8.0, OSX 10.11, *)
+    internal static var firstBaseline: ConstraintAttributes { return 2048 }
+    
+    @available(iOS 8.0, *)
+    internal static var leftMargin: ConstraintAttributes { return 4096 }
+    
+    @available(iOS 8.0, *)
+    internal static var rightMargin: ConstraintAttributes { return 8192 }
+    
+    @available(iOS 8.0, *)
+    internal static var topMargin: ConstraintAttributes { return 16384 }
+    
+    @available(iOS 8.0, *)
+    internal static var bottomMargin: ConstraintAttributes { return 32768 }
+    
+    @available(iOS 8.0, *)
+    internal static var leadingMargin: ConstraintAttributes { return 65536 }
+    
+    @available(iOS 8.0, *)
+    internal static var trailingMargin: ConstraintAttributes { return 131072 }
+    
+    @available(iOS 8.0, *)
+    internal static var centerXWithinMargins: ConstraintAttributes { return 262144 }
+    
+    @available(iOS 8.0, *)
+    internal static var centerYWithinMargins: ConstraintAttributes { return 524288 }
+    
+    // aggregates
+    
+    internal static var edges: ConstraintAttributes { return 15 }
+    internal static var size: ConstraintAttributes { return 192 }
+    internal static var center: ConstraintAttributes { return 768 }
+    
+    @available(iOS 8.0, *)
+    internal static var margins: ConstraintAttributes { return 61440 }
+    
+    @available(iOS 8.0, *)
+    internal static var centerWithinMargins: ConstraintAttributes { return 786432 }
+    
+    internal var layoutAttributes:[LayoutAttribute] {
+        var attrs = [LayoutAttribute]()
+        if (self.contains(ConstraintAttributes.left)) {
+            attrs.append(.left)
+        }
+        if (self.contains(ConstraintAttributes.top)) {
+            attrs.append(.top)
+        }
+        if (self.contains(ConstraintAttributes.right)) {
+            attrs.append(.right)
+        }
+        if (self.contains(ConstraintAttributes.bottom)) {
+            attrs.append(.bottom)
+        }
+        if (self.contains(ConstraintAttributes.leading)) {
+            attrs.append(.leading)
+        }
+        if (self.contains(ConstraintAttributes.trailing)) {
+            attrs.append(.trailing)
+        }
+        if (self.contains(ConstraintAttributes.width)) {
+            attrs.append(.width)
+        }
+        if (self.contains(ConstraintAttributes.height)) {
+            attrs.append(.height)
+        }
+        if (self.contains(ConstraintAttributes.centerX)) {
+            attrs.append(.centerX)
+        }
+        if (self.contains(ConstraintAttributes.centerY)) {
+            attrs.append(.centerY)
+        }
+        if (self.contains(ConstraintAttributes.lastBaseline)) {
+            attrs.append(.lastBaseline)
+        }
+        
+        #if os(iOS) || os(tvOS)
+            if (self.contains(ConstraintAttributes.firstBaseline)) {
+                attrs.append(.firstBaseline)
+            }
+            if (self.contains(ConstraintAttributes.leftMargin)) {
+                attrs.append(.leftMargin)
+            }
+            if (self.contains(ConstraintAttributes.rightMargin)) {
+                attrs.append(.rightMargin)
+            }
+            if (self.contains(ConstraintAttributes.topMargin)) {
+                attrs.append(.topMargin)
+            }
+            if (self.contains(ConstraintAttributes.bottomMargin)) {
+                attrs.append(.bottomMargin)
+            }
+            if (self.contains(ConstraintAttributes.leadingMargin)) {
+                attrs.append(.leadingMargin)
+            }
+            if (self.contains(ConstraintAttributes.trailingMargin)) {
+                attrs.append(.trailingMargin)
+            }
+            if (self.contains(ConstraintAttributes.centerXWithinMargins)) {
+                attrs.append(.centerXWithinMargins)
+            }
+            if (self.contains(ConstraintAttributes.centerYWithinMargins)) {
+                attrs.append(.centerYWithinMargins)
+            }
+        #endif
+        
+        return attrs
+    }
+}
+
+internal func + (left: ConstraintAttributes, right: ConstraintAttributes) -> ConstraintAttributes {
+    return left.union(right)
+}
+
+internal func +=(left: inout ConstraintAttributes, right: ConstraintAttributes) {
+    left.formUnion(right)
+}
+
+internal func -=(left: inout ConstraintAttributes, right: ConstraintAttributes) {
+    left.subtract(right)
+}
+
+internal func ==(left: ConstraintAttributes, right: ConstraintAttributes) -> Bool {
+    return left.rawValue == right.rawValue
+}

+ 37 - 0
Example/Pods/SnapKit/Source/ConstraintConfig.swift

@@ -0,0 +1,37 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+    public typealias ConstraintInterfaceLayoutDirection = UIUserInterfaceLayoutDirection
+#else
+    import AppKit
+    public typealias ConstraintInterfaceLayoutDirection = NSUserInterfaceLayoutDirection
+#endif
+
+
+public struct ConstraintConfig {
+    
+    public static var interfaceLayoutDirection: ConstraintInterfaceLayoutDirection = .leftToRight
+    
+}

+ 147 - 0
Example/Pods/SnapKit/Source/ConstraintConstantTarget.swift

@@ -0,0 +1,147 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public protocol ConstraintConstantTarget {
+}
+
+extension CGPoint: ConstraintConstantTarget {
+}
+
+extension CGSize: ConstraintConstantTarget {    
+}
+
+extension ConstraintInsets: ConstraintConstantTarget {
+}
+
+extension ConstraintConstantTarget {
+    
+    internal func constraintConstantTargetValueFor(layoutAttribute: LayoutAttribute) -> CGFloat {
+        if let value = self as? CGFloat {
+            return value
+        }
+        
+        if let value = self as? Float {
+            return CGFloat(value)
+        }
+        
+        if let value = self as? Double {
+            return CGFloat(value)
+        }
+        
+        if let value = self as? Int {
+            return CGFloat(value)
+        }
+        
+        if let value = self as? UInt {
+            return CGFloat(value)
+        }
+        
+        if let value = self as? CGSize {
+            if layoutAttribute == .width {
+                return value.width
+            } else if layoutAttribute == .height {
+                return value.height
+            } else {
+                return 0.0
+            }
+        }
+        
+        if let value = self as? CGPoint {
+            #if os(iOS) || os(tvOS)
+                switch layoutAttribute {
+                case .left, .right, .leading, .trailing, .centerX, .leftMargin, .rightMargin, .leadingMargin, .trailingMargin, .centerXWithinMargins:
+                    return value.x
+                case .top, .bottom, .centerY, .topMargin, .bottomMargin, .centerYWithinMargins, .lastBaseline, .firstBaseline:
+                    return value.y
+                case .width, .height, .notAnAttribute:
+                    return 0.0
+                }
+            #else
+                switch layoutAttribute {
+                case .left, .right, .leading, .trailing, .centerX:
+                    return value.x
+                case .top, .bottom, .centerY, .lastBaseline, .firstBaseline:
+                    return value.y
+                case .width, .height, .notAnAttribute:
+                    return 0.0
+                }
+            #endif
+        }
+        
+        if let value = self as? ConstraintInsets {
+            #if os(iOS) || os(tvOS)
+                switch layoutAttribute {
+                case .left, .leftMargin, .centerX, .centerXWithinMargins:
+                    return value.left
+                case .top, .topMargin, .centerY, .centerYWithinMargins, .lastBaseline, .firstBaseline:
+                    return value.top
+                case .right, .rightMargin:
+                    return -value.right
+                case .bottom, .bottomMargin:
+                    return -value.bottom
+                case .leading, .leadingMargin:
+                    return (ConstraintConfig.interfaceLayoutDirection == .leftToRight) ? value.left : value.right
+                case .trailing, .trailingMargin:
+                    return (ConstraintConfig.interfaceLayoutDirection == .leftToRight) ? -value.right : -value.left
+                case .width:
+                    return -(value.left + value.right)
+                case .height:
+                    return -(value.top + value.bottom)
+                case .notAnAttribute:
+                    return 0.0
+                }
+            #else
+                switch layoutAttribute {
+                case .left, .centerX:
+                    return value.left
+                case .top, .centerY, .lastBaseline, .firstBaseline:
+                    return value.top
+                case .right:
+                    return -value.right
+                case .bottom:
+                    return -value.bottom
+                case .leading:
+                    return (ConstraintConfig.interfaceLayoutDirection == .leftToRight) ? value.left : value.right
+                case .trailing:
+                    return (ConstraintConfig.interfaceLayoutDirection == .leftToRight) ? -value.right : -value.left
+                case .width:
+                    return -(value.left + value.right)
+                case .height:
+                    return -(value.top + value.bottom)
+                case .notAnAttribute:
+                    return 0.0
+                }
+            #endif
+        }
+        
+        return 0.0
+    }
+    
+}

+ 185 - 0
Example/Pods/SnapKit/Source/ConstraintDSL.swift

@@ -0,0 +1,185 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public protocol ConstraintDSL {
+    
+    var target: AnyObject? { get }
+    
+    func setLabel(_ value: String?)
+    func label() -> String?
+    
+}
+extension ConstraintDSL {
+    
+    public func setLabel(_ value: String?) {
+        objc_setAssociatedObject(self.target as Any, &labelKey, value, .OBJC_ASSOCIATION_COPY_NONATOMIC)
+    }
+    public func label() -> String? {
+        return objc_getAssociatedObject(self.target as Any, &labelKey) as? String
+    }
+    
+}
+private var labelKey: UInt8 = 0
+
+
+public protocol ConstraintBasicAttributesDSL : ConstraintDSL {
+}
+extension ConstraintBasicAttributesDSL {
+    
+    // MARK: Basics
+    
+    public var left: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.left)
+    }
+    
+    public var top: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.top)
+    }
+    
+    public var right: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.right)
+    }
+    
+    public var bottom: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.bottom)
+    }
+    
+    public var leading: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.leading)
+    }
+    
+    public var trailing: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.trailing)
+    }
+    
+    public var width: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.width)
+    }
+    
+    public var height: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.height)
+    }
+    
+    public var centerX: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerX)
+    }
+    
+    public var centerY: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerY)
+    }
+    
+    public var edges: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.edges)
+    }
+    
+    public var size: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.size)
+    }
+    
+    public var center: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.center)
+    }
+    
+}
+
+public protocol ConstraintAttributesDSL : ConstraintBasicAttributesDSL {
+}
+extension ConstraintAttributesDSL {
+    
+    // MARK: Baselines
+    
+    @available(*, deprecated:3.0, message:"Use .lastBaseline instead")
+    public var baseline: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.lastBaseline)
+    }
+    
+    @available(iOS 8.0, OSX 10.11, *)
+    public var lastBaseline: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.lastBaseline)
+    }
+    
+    @available(iOS 8.0, OSX 10.11, *)
+    public var firstBaseline: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.firstBaseline)
+    }
+    
+    // MARK: Margins
+    
+    @available(iOS 8.0, *)
+    public var leftMargin: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.leftMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var topMargin: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.topMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var rightMargin: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.rightMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var bottomMargin: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.bottomMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var leadingMargin: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.leadingMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var trailingMargin: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.trailingMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var centerXWithinMargins: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerXWithinMargins)
+    }
+    
+    @available(iOS 8.0, *)
+    public var centerYWithinMargins: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerYWithinMargins)
+    }
+    
+    @available(iOS 8.0, *)
+    public var margins: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.margins)
+    }
+    
+    @available(iOS 8.0, *)
+    public var centerWithinMargins: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.centerWithinMargins)
+    }
+    
+}

+ 69 - 0
Example/Pods/SnapKit/Source/ConstraintDescription.swift

@@ -0,0 +1,69 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public class ConstraintDescription {
+    
+    internal let item: LayoutConstraintItem
+    internal var attributes: ConstraintAttributes
+    internal var relation: ConstraintRelation? = nil
+    internal var sourceLocation: (String, UInt)? = nil
+    internal var label: String? = nil
+    internal var related: ConstraintItem? = nil
+    internal var multiplier: ConstraintMultiplierTarget = 1.0
+    internal var constant: ConstraintConstantTarget = 0.0
+    internal var priority: ConstraintPriorityTarget = 1000.0
+    internal lazy var constraint: Constraint? = {
+        guard let relation = self.relation,
+              let related = self.related,
+              let sourceLocation = self.sourceLocation else {
+            return nil
+        }
+        let from = ConstraintItem(target: self.item, attributes: self.attributes)
+        
+        return Constraint(
+            from: from,
+            to: related,
+            relation: relation,
+            sourceLocation: sourceLocation,
+            label: self.label,
+            multiplier: self.multiplier,
+            constant: self.constant,
+            priority: self.priority
+        )
+    }()
+    
+    // MARK: Initialization
+    
+    internal init(item: LayoutConstraintItem, attributes: ConstraintAttributes) {
+        self.item = item
+        self.attributes = attributes
+    }
+    
+}

+ 72 - 0
Example/Pods/SnapKit/Source/ConstraintInsetTarget.swift

@@ -0,0 +1,72 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public protocol ConstraintInsetTarget: ConstraintConstantTarget {
+}
+
+extension Int: ConstraintInsetTarget {
+}
+
+extension UInt: ConstraintInsetTarget {
+}
+
+extension Float: ConstraintInsetTarget {
+}
+
+extension Double: ConstraintInsetTarget {
+}
+
+extension CGFloat: ConstraintInsetTarget {
+}
+
+extension ConstraintInsets: ConstraintInsetTarget {
+}
+
+extension ConstraintInsetTarget {
+
+    internal var constraintInsetTargetValue: ConstraintInsets {
+        if let amount = self as? ConstraintInsets {
+            return amount
+        } else if let amount = self as? Float {
+            return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount))
+        } else if let amount = self as? Double {
+            return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount))
+        } else if let amount = self as? CGFloat {
+            return ConstraintInsets(top: amount, left: amount, bottom: amount, right: amount)
+        } else if let amount = self as? Int {
+            return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount))
+        } else if let amount = self as? UInt {
+            return ConstraintInsets(top: CGFloat(amount), left: CGFloat(amount), bottom: CGFloat(amount), right: CGFloat(amount))
+        } else {
+            return ConstraintInsets(top: 0, left: 0, bottom: 0, right: 0)
+        }
+    }
+    
+}

+ 35 - 0
Example/Pods/SnapKit/Source/ConstraintInsets.swift

@@ -0,0 +1,35 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+#if os(iOS) || os(tvOS)
+    public typealias ConstraintInsets = UIEdgeInsets
+#else
+    public typealias ConstraintInsets = NSEdgeInsets
+#endif

+ 61 - 0
Example/Pods/SnapKit/Source/ConstraintItem.swift

@@ -0,0 +1,61 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public final class ConstraintItem {
+    
+    internal weak var target: AnyObject?
+    internal let attributes: ConstraintAttributes
+    
+    internal init(target: AnyObject?, attributes: ConstraintAttributes) {
+        self.target = target
+        self.attributes = attributes
+    }
+    
+    internal var layoutConstraintItem: LayoutConstraintItem? {
+        return self.target as? LayoutConstraintItem
+    }
+    
+}
+
+public func ==(lhs: ConstraintItem, rhs: ConstraintItem) -> Bool {
+    // pointer equality
+    guard lhs !== rhs else {
+        return true
+    }
+    
+    // must both have valid targets and identical attributes
+    guard let target1 = lhs.target,
+          let target2 = rhs.target,
+          target1 === target2 && lhs.attributes == rhs.attributes else {
+            return false
+    }
+    
+    return true
+}

+ 36 - 0
Example/Pods/SnapKit/Source/ConstraintLayoutGuide+Extensions.swift

@@ -0,0 +1,36 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#endif
+    
+    
+@available(iOS 9.0, OSX 10.11, *)
+public extension ConstraintLayoutGuide {
+    
+    public var snp: ConstraintLayoutGuideDSL {
+        return ConstraintLayoutGuideDSL(guide: self)
+    }
+    
+}

+ 37 - 0
Example/Pods/SnapKit/Source/ConstraintLayoutGuide.swift

@@ -0,0 +1,37 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+#if os(iOS) || os(tvOS)
+    @available(iOS 9.0, *)
+    public typealias ConstraintLayoutGuide = UILayoutGuide
+#else
+    @available(OSX 10.11, *)
+    public typealias ConstraintLayoutGuide = NSLayoutGuide
+#endif

+ 66 - 0
Example/Pods/SnapKit/Source/ConstraintLayoutGuideDSL.swift

@@ -0,0 +1,66 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+@available(iOS 9.0, OSX 10.11, *)
+public struct ConstraintLayoutGuideDSL: ConstraintAttributesDSL {
+    
+    @discardableResult
+    public func prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
+        return ConstraintMaker.prepareConstraints(item: self.guide, closure: closure)
+    }
+    
+    public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
+        ConstraintMaker.makeConstraints(item: self.guide, closure: closure)
+    }
+    
+    public func remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
+        ConstraintMaker.remakeConstraints(item: self.guide, closure: closure)
+    }
+    
+    public func updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
+        ConstraintMaker.updateConstraints(item: self.guide, closure: closure)
+    }
+    
+    public func removeConstraints() {
+        ConstraintMaker.removeConstraints(item: self.guide)
+    }
+    
+    public var target: AnyObject? {
+        return self.guide
+    }
+    
+    internal let guide: ConstraintLayoutGuide
+    
+    internal init(guide: ConstraintLayoutGuide) {
+        self.guide = guide
+        
+    }
+    
+}

+ 36 - 0
Example/Pods/SnapKit/Source/ConstraintLayoutSupport.swift

@@ -0,0 +1,36 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+#if os(iOS) || os(tvOS)
+    @available(iOS 8.0, *)
+    public typealias ConstraintLayoutSupport = UILayoutSupport
+#else
+    public class ConstraintLayoutSupport {}
+#endif

+ 56 - 0
Example/Pods/SnapKit/Source/ConstraintLayoutSupportDSL.swift

@@ -0,0 +1,56 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+@available(iOS 8.0, *)
+public struct ConstraintLayoutSupportDSL: ConstraintDSL {
+    
+    public var target: AnyObject? {
+        return self.support
+    }
+    
+    internal let support: ConstraintLayoutSupport
+    
+    internal init(support: ConstraintLayoutSupport) {
+        self.support = support
+        
+    }
+    
+    public var top: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.top)
+    }
+    
+    public var bottom: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.bottom)
+    }
+    
+    public var height: ConstraintItem {
+        return ConstraintItem(target: self.target, attributes: ConstraintAttributes.height)
+    }
+}

+ 204 - 0
Example/Pods/SnapKit/Source/ConstraintMaker.swift

@@ -0,0 +1,204 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+public class ConstraintMaker {
+    
+    public var left: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.left)
+    }
+    
+    public var top: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.top)
+    }
+    
+    public var bottom: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.bottom)
+    }
+    
+    public var right: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.right)
+    }
+    
+    public var leading: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.leading)
+    }
+    
+    public var trailing: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.trailing)
+    }
+    
+    public var width: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.width)
+    }
+    
+    public var height: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.height)
+    }
+    
+    public var centerX: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.centerX)
+    }
+    
+    public var centerY: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.centerY)
+    }
+    
+    @available(*, deprecated:3.0, message:"Use lastBaseline instead")
+    public var baseline: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.lastBaseline)
+    }
+    
+    public var lastBaseline: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.lastBaseline)
+    }
+    
+    @available(iOS 8.0, OSX 10.11, *)
+    public var firstBaseline: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.firstBaseline)
+    }
+    
+    @available(iOS 8.0, *)
+    public var leftMargin: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.leftMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var rightMargin: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.rightMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var topMargin: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.topMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var bottomMargin: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.bottomMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var leadingMargin: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.leadingMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var trailingMargin: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.trailingMargin)
+    }
+    
+    @available(iOS 8.0, *)
+    public var centerXWithinMargins: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.centerXWithinMargins)
+    }
+    
+    @available(iOS 8.0, *)
+    public var centerYWithinMargins: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.centerYWithinMargins)
+    }
+    
+    public var edges: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.edges)
+    }
+    public var size: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.size)
+    }
+    public var center: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.center)
+    }
+    
+    @available(iOS 8.0, *)
+    public var margins: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.margins)
+    }
+    
+    @available(iOS 8.0, *)
+    public var centerWithinMargins: ConstraintMakerExtendable {
+        return self.makeExtendableWithAttributes(.centerWithinMargins)
+    }
+    
+    private let item: LayoutConstraintItem
+    private var descriptions = [ConstraintDescription]()
+    
+    internal init(item: LayoutConstraintItem) {
+        self.item = item
+        self.item.prepare()
+    }
+    
+    internal func makeExtendableWithAttributes(_ attributes: ConstraintAttributes) -> ConstraintMakerExtendable {
+        let description = ConstraintDescription(item: self.item, attributes: attributes)
+        self.descriptions.append(description)
+        return ConstraintMakerExtendable(description)
+    }
+    
+    internal static func prepareConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
+        let maker = ConstraintMaker(item: item)
+        closure(maker)
+        var constraints: [Constraint] = []
+        for description in maker.descriptions {
+            guard let constraint = description.constraint else {
+                continue
+            }
+            constraints.append(constraint)
+        }
+        return constraints
+    }
+    
+    internal static func makeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
+        let constraints = prepareConstraints(item: item, closure: closure)
+        for constraint in constraints {
+            constraint.activateIfNeeded(updatingExisting: false)
+        }
+    }
+    
+    internal static func remakeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
+        self.removeConstraints(item: item)
+        self.makeConstraints(item: item, closure: closure)
+    }
+    
+    internal static func updateConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
+        guard item.constraints.count > 0 else {
+            self.makeConstraints(item: item, closure: closure)
+            return
+        }
+        
+        let constraints = prepareConstraints(item: item, closure: closure)
+        for constraint in constraints {
+            constraint.activateIfNeeded(updatingExisting: true)
+        }
+    }
+    
+    internal static func removeConstraints(item: LayoutConstraintItem) {
+        let constraints = item.constraints
+        for constraint in constraints {
+            constraint.deactivateIfNeeded()
+        }
+    }
+    
+}

+ 56 - 0
Example/Pods/SnapKit/Source/ConstraintMakerEditable.swift

@@ -0,0 +1,56 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public class ConstraintMakerEditable: ConstraintMakerPriortizable {
+
+    @discardableResult
+    public func multipliedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable {
+        self.description.multiplier = amount
+        return self
+    }
+    
+    @discardableResult
+    public func dividedBy(_ amount: ConstraintMultiplierTarget) -> ConstraintMakerEditable {
+        return self.multipliedBy(1.0 / amount.constraintMultiplierTargetValue)
+    }
+    
+    @discardableResult
+    public func offset(_ amount: ConstraintOffsetTarget) -> ConstraintMakerEditable {
+        self.description.constant = amount.constraintOffsetTargetValue
+        return self
+    }
+    
+    @discardableResult
+    public func inset(_ amount: ConstraintInsetTarget) -> ConstraintMakerEditable {
+        self.description.constant = amount.constraintInsetTargetValue
+        return self
+    }
+    
+}

+ 169 - 0
Example/Pods/SnapKit/Source/ConstraintMakerExtendable.swift

@@ -0,0 +1,169 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public class ConstraintMakerExtendable: ConstraintMakerRelatable {
+    
+    public var left: ConstraintMakerExtendable {
+        self.description.attributes += .left
+        return self
+    }
+    
+    public var top: ConstraintMakerExtendable {
+        self.description.attributes += .top
+        return self
+    }
+    
+    public var bottom: ConstraintMakerExtendable {
+        self.description.attributes += .bottom
+        return self
+    }
+    
+    public var right: ConstraintMakerExtendable {
+        self.description.attributes += .right
+        return self
+    }
+    
+    public var leading: ConstraintMakerExtendable {
+        self.description.attributes += .leading
+        return self
+    }
+    
+    public var trailing: ConstraintMakerExtendable {
+        self.description.attributes += .trailing
+        return self
+    }
+    
+    public var width: ConstraintMakerExtendable {
+        self.description.attributes += .width
+        return self
+    }
+    
+    public var height: ConstraintMakerExtendable {
+        self.description.attributes += .height
+        return self
+    }
+    
+    public var centerX: ConstraintMakerExtendable {
+        self.description.attributes += .centerX
+        return self
+    }
+    
+    public var centerY: ConstraintMakerExtendable {
+        self.description.attributes += .centerY
+        return self
+    }
+    
+    @available(*, deprecated:3.0, message:"Use lastBaseline instead")
+    public var baseline: ConstraintMakerExtendable {
+        self.description.attributes += .lastBaseline
+        return self
+    }
+    
+    public var lastBaseline: ConstraintMakerExtendable {
+        self.description.attributes += .lastBaseline
+        return self
+    }
+    
+    @available(iOS 8.0, OSX 10.11, *)
+    public var firstBaseline: ConstraintMakerExtendable {
+        self.description.attributes += .firstBaseline
+        return self
+    }
+    
+    @available(iOS 8.0, *)
+    public var leftMargin: ConstraintMakerExtendable {
+        self.description.attributes += .leftMargin
+        return self
+    }
+    
+    @available(iOS 8.0, *)
+    public var rightMargin: ConstraintMakerExtendable {
+        self.description.attributes += .rightMargin
+        return self
+    }
+    
+    @available(iOS 8.0, *)
+    public var topMargin: ConstraintMakerExtendable {
+        self.description.attributes += .topMargin
+        return self
+    }
+    
+    @available(iOS 8.0, *)
+    public var bottomMargin: ConstraintMakerExtendable {
+        self.description.attributes += .bottomMargin
+        return self
+    }
+    
+    @available(iOS 8.0, *)
+    public var leadingMargin: ConstraintMakerExtendable {
+        self.description.attributes += .leadingMargin
+        return self
+    }
+    
+    @available(iOS 8.0, *)
+    public var trailingMargin: ConstraintMakerExtendable {
+        self.description.attributes += .trailingMargin
+        return self
+    }
+    
+    @available(iOS 8.0, *)
+    public var centerXWithinMargins: ConstraintMakerExtendable {
+        self.description.attributes += .centerXWithinMargins
+        return self
+    }
+    
+    @available(iOS 8.0, *)
+    public var centerYWithinMargins: ConstraintMakerExtendable {
+        self.description.attributes += .centerYWithinMargins
+        return self
+    }
+    
+    public var edges: ConstraintMakerExtendable {
+        self.description.attributes += .edges
+        return self
+    }
+    public var size: ConstraintMakerExtendable {
+        self.description.attributes += .size
+        return self
+    }
+    
+    @available(iOS 8.0, *)
+    public var margins: ConstraintMakerExtendable {
+        self.description.attributes += .margins
+        return self
+    }
+    
+    @available(iOS 8.0, *)
+    public var centerWithinMargins: ConstraintMakerExtendable {
+        self.description.attributes += .centerWithinMargins
+        return self
+    }
+    
+}

+ 49 - 0
Example/Pods/SnapKit/Source/ConstraintMakerFinalizable.swift

@@ -0,0 +1,49 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public class ConstraintMakerFinalizable {
+    
+    internal let description: ConstraintDescription
+    
+    internal init(_ description: ConstraintDescription) {
+        self.description = description
+    }
+    
+    @discardableResult
+    public func labeled(_ label: String) -> ConstraintMakerFinalizable {
+        self.description.label = label
+        return self
+    }
+    
+    public var constraint: Constraint {
+        return self.description.constraint!
+    }
+    
+}

+ 68 - 0
Example/Pods/SnapKit/Source/ConstraintMakerPriortizable.swift

@@ -0,0 +1,68 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public class ConstraintMakerPriortizable: ConstraintMakerFinalizable {
+    
+    @discardableResult
+    public func priority(_ amount: ConstraintPriority) -> ConstraintMakerFinalizable {
+        self.description.priority = amount.value
+        return self
+    }
+    
+    @discardableResult
+    public func priority(_ amount: ConstraintPriorityTarget) -> ConstraintMakerFinalizable {
+        self.description.priority = amount
+        return self
+    }
+    
+    @available(*, deprecated:3.0, message:"Use priority(.required) instead.")
+    @discardableResult
+    public func priorityRequired() -> ConstraintMakerFinalizable {
+        return self.priority(.required)
+    }
+    
+    @available(*, deprecated:3.0, message:"Use priority(.high) instead.")
+    @discardableResult
+    public func priorityHigh() -> ConstraintMakerFinalizable {
+        return self.priority(.high)
+    }
+    
+    @available(*, deprecated:3.0, message:"Use priority(.medium) instead.")
+    @discardableResult
+    public func priorityMedium() -> ConstraintMakerFinalizable {
+        return self.priority(.medium)
+    }
+    
+    @available(*, deprecated:3.0, message:"Use priority(.low) instead.")
+    @discardableResult
+    public func priorityLow() -> ConstraintMakerFinalizable {
+        return self.priority(.low)
+    }
+}

+ 113 - 0
Example/Pods/SnapKit/Source/ConstraintMakerRelatable.swift

@@ -0,0 +1,113 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public class ConstraintMakerRelatable {
+    
+    internal let description: ConstraintDescription
+    
+    internal init(_ description: ConstraintDescription) {
+        self.description = description
+    }
+    
+    internal func relatedTo(_ other: ConstraintRelatableTarget, relation: ConstraintRelation, file: String, line: UInt) -> ConstraintMakerEditable {
+        let related: ConstraintItem
+        let constant: ConstraintConstantTarget
+        
+        if let other = other as? ConstraintItem {
+            guard other.attributes == ConstraintAttributes.none ||
+                  other.attributes.layoutAttributes.count <= 1 ||
+                  other.attributes.layoutAttributes == self.description.attributes.layoutAttributes ||
+                  other.attributes == .edges && self.description.attributes == .margins ||
+                  other.attributes == .margins && self.description.attributes == .edges else {
+                fatalError("Cannot constraint to multiple non identical attributes. (\(file), \(line))");
+            }
+            
+            related = other
+            constant = 0.0
+        } else if let other = other as? ConstraintView {
+            related = ConstraintItem(target: other, attributes: ConstraintAttributes.none)
+            constant = 0.0
+        } else if let other = other as? ConstraintConstantTarget {
+            related = ConstraintItem(target: nil, attributes: ConstraintAttributes.none)
+            constant = other
+        } else if #available(iOS 9.0, OSX 10.11, *), let other = other as? ConstraintLayoutGuide {
+            related = ConstraintItem(target: other, attributes: ConstraintAttributes.none)
+            constant = 0.0
+        } else {
+            fatalError("Invalid constraint. (\(file), \(line))")
+        }
+        
+        let editable = ConstraintMakerEditable(self.description)
+        editable.description.sourceLocation = (file, line)
+        editable.description.relation = relation
+        editable.description.related = related
+        editable.description.constant = constant
+        return editable
+    }
+    
+    @discardableResult
+    public func equalTo(_ other: ConstraintRelatableTarget, _ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
+        return self.relatedTo(other, relation: .equal, file: file, line: line)
+    }
+    
+    @discardableResult
+    public func equalToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
+        guard let other = self.description.item.superview else {
+            fatalError("Expected superview but found nil when attempting make constraint `equalToSuperview`.")
+        }
+        return self.relatedTo(other, relation: .equal, file: file, line: line)
+    }
+    
+    @discardableResult
+    public func lessThanOrEqualTo(_ other: ConstraintRelatableTarget, _ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
+        return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line)
+    }
+    
+    @discardableResult
+    public func lessThanOrEqualToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
+        guard let other = self.description.item.superview else {
+            fatalError("Expected superview but found nil when attempting make constraint `lessThanOrEqualToSuperview`.")
+        }
+        return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line)
+    }
+    
+    @discardableResult
+    public func greaterThanOrEqualTo(_ other: ConstraintRelatableTarget, _ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable {
+        return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line)
+    }
+    
+    @discardableResult
+    public func greaterThanOrEqualToSuperview(_ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable {
+        guard let other = self.description.item.superview else {
+            fatalError("Expected superview but found nil when attempting make constraint `greaterThanOrEqualToSuperview`.")
+        }
+        return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line)
+    }
+}

+ 75 - 0
Example/Pods/SnapKit/Source/ConstraintMultiplierTarget.swift

@@ -0,0 +1,75 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public protocol ConstraintMultiplierTarget {
+    
+    var constraintMultiplierTargetValue: CGFloat { get }
+    
+}
+
+extension Int: ConstraintMultiplierTarget {
+    
+    public var constraintMultiplierTargetValue: CGFloat {
+        return CGFloat(self)
+    }
+    
+}
+
+extension UInt: ConstraintMultiplierTarget {
+    
+    public var constraintMultiplierTargetValue: CGFloat {
+        return CGFloat(self)
+    }
+    
+}
+
+extension Float: ConstraintMultiplierTarget {
+    
+    public var constraintMultiplierTargetValue: CGFloat {
+        return CGFloat(self)
+    }
+    
+}
+
+extension Double: ConstraintMultiplierTarget {
+    
+    public var constraintMultiplierTargetValue: CGFloat {
+        return CGFloat(self)
+    }
+    
+}
+
+extension CGFloat: ConstraintMultiplierTarget {
+    
+    public var constraintMultiplierTargetValue: CGFloat {
+        return self
+    }
+    
+}

+ 69 - 0
Example/Pods/SnapKit/Source/ConstraintOffsetTarget.swift

@@ -0,0 +1,69 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public protocol ConstraintOffsetTarget: ConstraintConstantTarget {
+}
+
+extension Int: ConstraintOffsetTarget {
+}
+
+extension UInt: ConstraintOffsetTarget {
+}
+
+extension Float: ConstraintOffsetTarget {
+}
+
+extension Double: ConstraintOffsetTarget {
+}
+
+extension CGFloat: ConstraintOffsetTarget {
+}
+
+extension ConstraintOffsetTarget {
+    
+    internal var constraintOffsetTargetValue: CGFloat {
+        let offset: CGFloat
+        if let amount = self as? Float {
+            offset = CGFloat(amount)
+        } else if let amount = self as? Double {
+            offset = CGFloat(amount)
+        } else if let amount = self as? CGFloat {
+            offset = CGFloat(amount)
+        } else if let amount = self as? Int {
+            offset = CGFloat(amount)
+        } else if let amount = self as? UInt {
+            offset = CGFloat(amount)
+        } else {
+            offset = 0.0
+        }
+        return offset
+    }
+    
+}

+ 77 - 0
Example/Pods/SnapKit/Source/ConstraintPriority.swift

@@ -0,0 +1,77 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+public struct ConstraintPriority : ExpressibleByFloatLiteral, Equatable, Strideable {
+    public typealias FloatLiteralType = Float
+    
+    public let value: Float
+    
+    public init(floatLiteral value: Float) {
+        self.value = value
+    }
+    
+    public init(_ value: Float) {
+        self.value = value
+    }
+    
+    public static var required: ConstraintPriority {
+        return 1000.0
+    }
+    
+    public static var high: ConstraintPriority {
+        return 750.0
+    }
+    
+    public static var medium: ConstraintPriority {
+        #if os(OSX)
+            return 501.0
+        #else
+            return 500.0
+        #endif
+        
+    }
+    
+    public static var low: ConstraintPriority {
+        return 250.0
+    }
+    
+    public static func ==(lhs: ConstraintPriority, rhs: ConstraintPriority) -> Bool {
+        return lhs.value == rhs.value
+    }
+
+    // MARK: Strideable
+
+    public func advanced(by n: FloatLiteralType) -> ConstraintPriority {
+        return ConstraintPriority(floatLiteral: value + n)
+    }
+
+    public func distance(to other: ConstraintPriority) -> FloatLiteralType {
+        return other.value - value
+    }
+}

+ 85 - 0
Example/Pods/SnapKit/Source/ConstraintPriorityTarget.swift

@@ -0,0 +1,85 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public protocol ConstraintPriorityTarget {
+    
+    var constraintPriorityTargetValue: Float { get }
+    
+}
+
+extension Int: ConstraintPriorityTarget {
+    
+    public var constraintPriorityTargetValue: Float {
+        return Float(self)
+    }
+    
+}
+
+extension UInt: ConstraintPriorityTarget {
+    
+    public var constraintPriorityTargetValue: Float {
+        return Float(self)
+    }
+    
+}
+
+extension Float: ConstraintPriorityTarget {
+    
+    public var constraintPriorityTargetValue: Float {
+        return self
+    }
+    
+}
+
+extension Double: ConstraintPriorityTarget {
+    
+    public var constraintPriorityTargetValue: Float {
+        return Float(self)
+    }
+    
+}
+
+extension CGFloat: ConstraintPriorityTarget {
+    
+    public var constraintPriorityTargetValue: Float {
+        return Float(self)
+    }
+    
+}
+
+#if os(iOS) || os(tvOS)
+extension UILayoutPriority: ConstraintPriorityTarget {
+
+    public var constraintPriorityTargetValue: Float {
+        return self.rawValue
+    }
+
+}
+#endif

+ 66 - 0
Example/Pods/SnapKit/Source/ConstraintRelatableTarget.swift

@@ -0,0 +1,66 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public protocol ConstraintRelatableTarget {
+}
+
+extension Int: ConstraintRelatableTarget {
+}
+
+extension UInt: ConstraintRelatableTarget {
+}
+
+extension Float: ConstraintRelatableTarget {
+}
+
+extension Double: ConstraintRelatableTarget {
+}
+
+extension CGFloat: ConstraintRelatableTarget {
+}
+
+extension CGSize: ConstraintRelatableTarget {
+}
+
+extension CGPoint: ConstraintRelatableTarget {
+}
+
+extension ConstraintInsets: ConstraintRelatableTarget {
+}
+
+extension ConstraintItem: ConstraintRelatableTarget {
+}
+
+extension ConstraintView: ConstraintRelatableTarget {
+}
+
+@available(iOS 9.0, OSX 10.11, *)
+extension ConstraintLayoutGuide: ConstraintRelatableTarget {
+}

+ 48 - 0
Example/Pods/SnapKit/Source/ConstraintRelation.swift

@@ -0,0 +1,48 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+internal enum ConstraintRelation : Int {
+    case equal = 1
+    case lessThanOrEqual
+    case greaterThanOrEqual
+    
+    internal var layoutRelation: LayoutRelation {
+        get {
+            switch(self) {
+            case .equal:
+                return .equal
+            case .lessThanOrEqual:
+                return .lessThanOrEqual
+            case .greaterThanOrEqual:
+                return .greaterThanOrEqual
+            }
+        }
+    }
+}

+ 152 - 0
Example/Pods/SnapKit/Source/ConstraintView+Extensions.swift

@@ -0,0 +1,152 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public extension ConstraintView {
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_left: ConstraintItem { return self.snp.left }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_top: ConstraintItem { return self.snp.top }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_right: ConstraintItem { return self.snp.right }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_bottom: ConstraintItem { return self.snp.bottom }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_leading: ConstraintItem { return self.snp.leading }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_trailing: ConstraintItem { return self.snp.trailing }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_width: ConstraintItem { return self.snp.width }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_height: ConstraintItem { return self.snp.height }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_centerX: ConstraintItem { return self.snp.centerX }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_centerY: ConstraintItem { return self.snp.centerY }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_baseline: ConstraintItem { return self.snp.baseline }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, OSX 10.11, *)
+    public var snp_lastBaseline: ConstraintItem { return self.snp.lastBaseline }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, OSX 10.11, *)
+    public var snp_firstBaseline: ConstraintItem { return self.snp.firstBaseline }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, *)
+    public var snp_leftMargin: ConstraintItem { return self.snp.leftMargin }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, *)
+    public var snp_topMargin: ConstraintItem { return self.snp.topMargin }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, *)
+    public var snp_rightMargin: ConstraintItem { return self.snp.rightMargin }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, *)
+    public var snp_bottomMargin: ConstraintItem { return self.snp.bottomMargin }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, *)
+    public var snp_leadingMargin: ConstraintItem { return self.snp.leadingMargin }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, *)
+    public var snp_trailingMargin: ConstraintItem { return self.snp.trailingMargin }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, *)
+    public var snp_centerXWithinMargins: ConstraintItem { return self.snp.centerXWithinMargins }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, *)
+    public var snp_centerYWithinMargins: ConstraintItem { return self.snp.centerYWithinMargins }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_edges: ConstraintItem { return self.snp.edges }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_size: ConstraintItem { return self.snp.size }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public var snp_center: ConstraintItem { return self.snp.center }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, *)
+    public var snp_margins: ConstraintItem { return self.snp.margins }
+    
+    @available(iOS, deprecated:3.0, message:"Use newer snp.* syntax.")
+    @available(iOS 8.0, *)
+    public var snp_centerWithinMargins: ConstraintItem { return self.snp.centerWithinMargins }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public func snp_prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
+        return self.snp.prepareConstraints(closure)
+    }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public func snp_makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
+        self.snp.makeConstraints(closure)
+    }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public func snp_remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
+        self.snp.remakeConstraints(closure)
+    }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public func snp_updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
+        self.snp.updateConstraints(closure)
+    }
+    
+    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
+    public func snp_removeConstraints() {
+        self.snp.removeConstraints()
+    }
+    
+    public var snp: ConstraintViewDSL {
+        return ConstraintViewDSL(view: self)
+    }
+    
+}

+ 35 - 0
Example/Pods/SnapKit/Source/ConstraintView.swift

@@ -0,0 +1,35 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+#if os(iOS) || os(tvOS)
+    public typealias ConstraintView = UIView
+#else
+    public typealias ConstraintView = NSView
+#endif

+ 101 - 0
Example/Pods/SnapKit/Source/ConstraintViewDSL.swift

@@ -0,0 +1,101 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public struct ConstraintViewDSL: ConstraintAttributesDSL {
+    
+    @discardableResult
+    public func prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
+        return ConstraintMaker.prepareConstraints(item: self.view, closure: closure)
+    }
+    
+    public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
+        ConstraintMaker.makeConstraints(item: self.view, closure: closure)
+    }
+    
+    public func remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
+        ConstraintMaker.remakeConstraints(item: self.view, closure: closure)
+    }
+    
+    public func updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
+        ConstraintMaker.updateConstraints(item: self.view, closure: closure)
+    }
+    
+    public func removeConstraints() {
+        ConstraintMaker.removeConstraints(item: self.view)
+    }
+    
+    public var contentHuggingHorizontalPriority: Float {
+        get {
+            return self.view.contentHuggingPriority(for: .horizontal).rawValue
+        }
+        set {
+            self.view.setContentHuggingPriority(LayoutPriority(rawValue: newValue), for: .horizontal)
+        }
+    }
+    
+    public var contentHuggingVerticalPriority: Float {
+        get {
+            return self.view.contentHuggingPriority(for: .vertical).rawValue
+        }
+        set {
+            self.view.setContentHuggingPriority(LayoutPriority(rawValue: newValue), for: .vertical)
+        }
+    }
+    
+    public var contentCompressionResistanceHorizontalPriority: Float {
+        get {
+            return self.view.contentCompressionResistancePriority(for: .horizontal).rawValue
+        }
+        set {
+            self.view.setContentCompressionResistancePriority(LayoutPriority(rawValue: newValue), for: .horizontal)
+        }
+    }
+    
+    public var contentCompressionResistanceVerticalPriority: Float {
+        get {
+            return self.view.contentCompressionResistancePriority(for: .vertical).rawValue
+        }
+        set {
+            self.view.setContentCompressionResistancePriority(LayoutPriority(rawValue: newValue), for: .vertical)
+        }
+    }
+    
+    public var target: AnyObject? {
+        return self.view
+    }
+    
+    internal let view: ConstraintView
+    
+    internal init(view: ConstraintView) {
+        self.view = view
+        
+    }
+    
+}

+ 160 - 0
Example/Pods/SnapKit/Source/Debugging.swift

@@ -0,0 +1,160 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+public extension LayoutConstraint {
+    
+    override public var description: String {
+        var description = "<"
+        
+        description += descriptionForObject(self)
+        
+        if let firstItem = conditionalOptional(from: self.firstItem) {
+            description += " \(descriptionForObject(firstItem))"
+        }
+        
+        if self.firstAttribute != .notAnAttribute {
+            description += ".\(descriptionForAttribute(self.firstAttribute))"
+        }
+        
+        description += " \(descriptionForRelation(self.relation))"
+        
+        if let secondItem = self.secondItem {
+            description += " \(descriptionForObject(secondItem))"
+        }
+        
+        if self.secondAttribute != .notAnAttribute {
+            description += ".\(descriptionForAttribute(self.secondAttribute))"
+        }
+        
+        if self.multiplier != 1.0 {
+            description += " * \(self.multiplier)"
+        }
+        
+        if self.secondAttribute == .notAnAttribute {
+            description += " \(self.constant)"
+        } else {
+            if self.constant > 0.0 {
+                description += " + \(self.constant)"
+            } else if self.constant < 0.0 {
+                description += " - \(abs(self.constant))"
+            }
+        }
+        
+        if self.priority.rawValue != 1000.0 {
+            description += " ^\(self.priority)"
+        }
+        
+        description += ">"
+        
+        return description
+    }
+    
+}
+
+private func descriptionForRelation(_ relation: LayoutRelation) -> String {
+    switch relation {
+    case .equal:                return "=="
+    case .greaterThanOrEqual:   return ">="
+    case .lessThanOrEqual:      return "<="
+    }
+}
+
+private func descriptionForAttribute(_ attribute: LayoutAttribute) -> String {
+    #if os(iOS) || os(tvOS)
+        switch attribute {
+        case .notAnAttribute:       return "notAnAttribute"
+        case .top:                  return "top"
+        case .left:                 return "left"
+        case .bottom:               return "bottom"
+        case .right:                return "right"
+        case .leading:              return "leading"
+        case .trailing:             return "trailing"
+        case .width:                return "width"
+        case .height:               return "height"
+        case .centerX:              return "centerX"
+        case .centerY:              return "centerY"
+        case .lastBaseline:         return "lastBaseline"
+        case .firstBaseline:        return "firstBaseline"
+        case .topMargin:            return "topMargin"
+        case .leftMargin:           return "leftMargin"
+        case .bottomMargin:         return "bottomMargin"
+        case .rightMargin:          return "rightMargin"
+        case .leadingMargin:        return "leadingMargin"
+        case .trailingMargin:       return "trailingMargin"
+        case .centerXWithinMargins: return "centerXWithinMargins"
+        case .centerYWithinMargins: return "centerYWithinMargins"
+        }
+    #else
+        switch attribute {
+        case .notAnAttribute:       return "notAnAttribute"
+        case .top:                  return "top"
+        case .left:                 return "left"
+        case .bottom:               return "bottom"
+        case .right:                return "right"
+        case .leading:              return "leading"
+        case .trailing:             return "trailing"
+        case .width:                return "width"
+        case .height:               return "height"
+        case .centerX:              return "centerX"
+        case .centerY:              return "centerY"
+        case .lastBaseline:         return "lastBaseline"
+        case .firstBaseline:        return "firstBaseline"
+        }
+    #endif
+}
+
+private func conditionalOptional<T>(from object: Optional<T>) -> Optional<T> {
+    return object
+}
+
+private func conditionalOptional<T>(from object: T) -> Optional<T> {
+    return Optional.some(object)
+}
+
+private func descriptionForObject(_ object: AnyObject) -> String {
+    let pointerDescription = String(format: "%p", UInt(bitPattern: ObjectIdentifier(object)))
+    var desc = ""
+    
+    desc += type(of: object).description()
+    
+    if let object = object as? ConstraintView {
+        desc += ":\(object.snp.label() ?? pointerDescription)"
+    } else if let object = object as? LayoutConstraint {
+        desc += ":\(object.label ?? pointerDescription)"
+    } else {
+        desc += ":\(pointerDescription)"
+    }
+    
+    if let object = object as? LayoutConstraint, let file = object.constraint?.sourceLocation.0, let line = object.constraint?.sourceLocation.1 {
+        desc += "@\((file as NSString).lastPathComponent)#\(line)"
+    }
+    
+    desc += ""
+    return desc
+}

+ 57 - 0
Example/Pods/SnapKit/Source/LayoutConstraint.swift

@@ -0,0 +1,57 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public class LayoutConstraint : NSLayoutConstraint {
+    
+    public var label: String? {
+        get {
+            return self.identifier
+        }
+        set {
+            self.identifier = newValue
+        }
+    }
+    
+    internal weak var constraint: Constraint? = nil
+    
+}
+
+internal func ==(lhs: LayoutConstraint, rhs: LayoutConstraint) -> Bool {
+    guard lhs.firstItem === rhs.firstItem &&
+          lhs.secondItem === rhs.secondItem &&
+          lhs.firstAttribute == rhs.firstAttribute &&
+          lhs.secondAttribute == rhs.secondAttribute &&
+          lhs.relation == rhs.relation &&
+          lhs.priority == rhs.priority &&
+          lhs.multiplier == rhs.multiplier else {
+        return false
+    }
+    return true
+}

+ 93 - 0
Example/Pods/SnapKit/Source/LayoutConstraintItem.swift

@@ -0,0 +1,93 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#else
+    import AppKit
+#endif
+
+
+public protocol LayoutConstraintItem: class {
+}
+
+@available(iOS 9.0, OSX 10.11, *)
+extension ConstraintLayoutGuide : LayoutConstraintItem {
+}
+
+extension ConstraintView : LayoutConstraintItem {
+}
+
+
+extension LayoutConstraintItem {
+    
+    internal func prepare() {
+        if let view = self as? ConstraintView {
+            view.translatesAutoresizingMaskIntoConstraints = false
+        }
+    }
+    
+    internal var superview: ConstraintView? {
+        if let view = self as? ConstraintView {
+            return view.superview
+        }
+        
+        if #available(iOS 9.0, OSX 10.11, *), let guide = self as? ConstraintLayoutGuide {
+            return guide.owningView
+        }
+        
+        return nil
+    }
+    internal var constraints: [Constraint] {
+        return self.constraintsSet.allObjects as! [Constraint]
+    }
+    
+    internal func add(constraints: [Constraint]) {
+        let constraintsSet = self.constraintsSet
+        for constraint in constraints {
+            constraintsSet.add(constraint)
+        }
+    }
+    
+    internal func remove(constraints: [Constraint]) {
+        let constraintsSet = self.constraintsSet
+        for constraint in constraints {
+            constraintsSet.remove(constraint)
+        }
+    }
+    
+    private var constraintsSet: NSMutableSet {
+        let constraintsSet: NSMutableSet
+        
+        if let existing = objc_getAssociatedObject(self, &constraintsKey) as? NSMutableSet {
+            constraintsSet = existing
+        } else {
+            constraintsSet = NSMutableSet()
+            objc_setAssociatedObject(self, &constraintsKey, constraintsSet, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+        return constraintsSet
+        
+    }
+    
+}
+private var constraintsKey: UInt8 = 0

+ 42 - 0
Example/Pods/SnapKit/Source/Typealiases.swift

@@ -0,0 +1,42 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#if swift(>=4.2)
+    typealias LayoutRelation = NSLayoutConstraint.Relation
+    typealias LayoutAttribute = NSLayoutConstraint.Attribute
+#else
+    typealias LayoutRelation = NSLayoutRelation
+    typealias LayoutAttribute = NSLayoutAttribute
+#endif
+    typealias LayoutPriority = UILayoutPriority
+#else
+    import AppKit
+    typealias LayoutRelation = NSLayoutConstraint.Relation
+    typealias LayoutAttribute = NSLayoutConstraint.Attribute
+    typealias LayoutPriority = NSLayoutConstraint.Priority
+#endif
+

+ 36 - 0
Example/Pods/SnapKit/Source/UILayoutSupport+Extensions.swift

@@ -0,0 +1,36 @@
+//
+//  SnapKit
+//
+//  Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#endif
+
+    
+@available(iOS 8.0, *)
+public extension ConstraintLayoutSupport {
+    
+    public var snp: ConstraintLayoutSupportDSL {
+        return ConstraintLayoutSupportDSL(support: self)
+    }
+    
+}

+ 1 - 0
Example/Pods/Target Support Files/BFFramework/BFFramework.debug.xcconfig

@@ -1,5 +1,6 @@
 CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
 CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BFFramework
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
 OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
 PODS_BUILD_DIR = ${BUILD_DIR}

+ 1 - 0
Example/Pods/Target Support Files/BFFramework/BFFramework.release.xcconfig

@@ -1,5 +1,6 @@
 CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
 CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BFFramework
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
 OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
 PODS_BUILD_DIR = ${BUILD_DIR}

+ 24 - 0
Example/Pods/Target Support Files/BFFramework/ResourceBundle-BFFramework-BFFramework-Info.plist

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>BNDL</string>
+  <key>CFBundleShortVersionString</key>
+  <string>0.1.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>1</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 26 - 0
Example/Pods/Target Support Files/Kingfisher/Kingfisher-Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>4.10.1</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 5 - 0
Example/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Kingfisher : NSObject
+@end
+@implementation PodsDummy_Kingfisher
+@end

+ 12 - 0
Example/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch

@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+

+ 17 - 0
Example/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h

@@ -0,0 +1,17 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "Kingfisher.h"
+
+FOUNDATION_EXPORT double KingfisherVersionNumber;
+FOUNDATION_EXPORT const unsigned char KingfisherVersionString[];
+

+ 13 - 0
Example/Pods/Target Support Files/Kingfisher/Kingfisher.debug.xcconfig

@@ -0,0 +1,13 @@
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = $(inherited) -framework "CFNetwork"
+OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 6 - 0
Example/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap

@@ -0,0 +1,6 @@
+framework module Kingfisher {
+  umbrella header "Kingfisher-umbrella.h"
+
+  export *
+  module * { export * }
+}

+ 13 - 0
Example/Pods/Target Support Files/Kingfisher/Kingfisher.release.xcconfig

@@ -0,0 +1,13 @@
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = $(inherited) -framework "CFNetwork"
+OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 49 - 0
Example/Pods/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example-acknowledgements.markdown

@@ -23,4 +23,53 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 
+
+## Kingfisher
+
+The MIT License (MIT)
+
+Copyright (c) 2018 Wei Wang
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+
+## SnapKit
+
+Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
 Generated by CocoaPods - https://cocoapods.org

+ 61 - 0
Example/Pods/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example-acknowledgements.plist

@@ -41,6 +41,67 @@ THE SOFTWARE.
 			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>The MIT License (MIT)
+
+Copyright (c) 2018 Wei Wang
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+</string>
+			<key>License</key>
+			<string>MIT</string>
+			<key>Title</key>
+			<string>Kingfisher</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+</string>
+			<key>License</key>
+			<string>MIT</string>
+			<key>Title</key>
+			<string>SnapKit</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
 		<dict>
 			<key>FooterText</key>
 			<string>Generated by CocoaPods - https://cocoapods.org</string>

+ 4 - 0
Example/Pods/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example-frameworks.sh

@@ -176,9 +176,13 @@ code_sign_if_enabled() {
 
 if [[ "$CONFIGURATION" == "Debug" ]]; then
   install_framework "${BUILT_PRODUCTS_DIR}/BFFramework/BFFramework.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework"
 fi
 if [[ "$CONFIGURATION" == "Release" ]]; then
   install_framework "${BUILT_PRODUCTS_DIR}/BFFramework/BFFramework.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework"
 fi
 if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
   wait

+ 3 - 3
Example/Pods/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example.debug.xcconfig

@@ -1,10 +1,10 @@
 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
 CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
-FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework"
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
-HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework/BFFramework.framework/Headers"
+HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework/BFFramework.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers"
 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
-OTHER_LDFLAGS = $(inherited) -framework "BFFramework"
+OTHER_LDFLAGS = $(inherited) -framework "BFFramework" -framework "CFNetwork" -framework "Kingfisher" -framework "SnapKit"
 OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
 PODS_BUILD_DIR = ${BUILD_DIR}
 PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

+ 3 - 3
Example/Pods/Target Support Files/Pods-BFFramework_Example/Pods-BFFramework_Example.release.xcconfig

@@ -1,10 +1,10 @@
 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
 CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
-FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework"
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
-HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework/BFFramework.framework/Headers"
+HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework/BFFramework.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers"
 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
-OTHER_LDFLAGS = $(inherited) -framework "BFFramework"
+OTHER_LDFLAGS = $(inherited) -framework "BFFramework" -framework "CFNetwork" -framework "Kingfisher" -framework "SnapKit"
 OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
 PODS_BUILD_DIR = ${BUILD_DIR}
 PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

+ 3 - 3
Example/Pods/Target Support Files/Pods-BFFramework_Tests/Pods-BFFramework_Tests.debug.xcconfig

@@ -1,10 +1,10 @@
 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
 CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
-FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework" "${PODS_CONFIGURATION_BUILD_DIR}/Nimble" "${PODS_CONFIGURATION_BUILD_DIR}/Quick"
+FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/Nimble" "${PODS_CONFIGURATION_BUILD_DIR}/Quick" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
-HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework/BFFramework.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Nimble/Nimble.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Quick/Quick.framework/Headers"
+HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework/BFFramework.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Nimble/Nimble.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Quick/Quick.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers"
 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
-OTHER_LDFLAGS = $(inherited) -framework "BFFramework" -framework "Nimble" -framework "Quick" -framework "XCTest" -weak_framework "XCTest"
+OTHER_LDFLAGS = $(inherited) -framework "BFFramework" -framework "CFNetwork" -framework "Kingfisher" -framework "Nimble" -framework "Quick" -framework "SnapKit" -framework "XCTest" -weak_framework "XCTest"
 OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
 PODS_BUILD_DIR = ${BUILD_DIR}
 PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

+ 3 - 3
Example/Pods/Target Support Files/Pods-BFFramework_Tests/Pods-BFFramework_Tests.release.xcconfig

@@ -1,10 +1,10 @@
 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
 CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
-FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework" "${PODS_CONFIGURATION_BUILD_DIR}/Nimble" "${PODS_CONFIGURATION_BUILD_DIR}/Quick"
+FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/Nimble" "${PODS_CONFIGURATION_BUILD_DIR}/Quick" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit"
 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
-HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework/BFFramework.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Nimble/Nimble.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Quick/Quick.framework/Headers"
+HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BFFramework/BFFramework.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Nimble/Nimble.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Quick/Quick.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers"
 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
-OTHER_LDFLAGS = $(inherited) -framework "BFFramework" -framework "Nimble" -framework "Quick" -framework "XCTest" -weak_framework "XCTest"
+OTHER_LDFLAGS = $(inherited) -framework "BFFramework" -framework "CFNetwork" -framework "Kingfisher" -framework "Nimble" -framework "Quick" -framework "SnapKit" -framework "XCTest" -weak_framework "XCTest"
 OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
 PODS_BUILD_DIR = ${BUILD_DIR}
 PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

+ 26 - 0
Example/Pods/Target Support Files/SnapKit/SnapKit-Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>4.2.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 5 - 0
Example/Pods/Target Support Files/SnapKit/SnapKit-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_SnapKit : NSObject
+@end
+@implementation PodsDummy_SnapKit
+@end

+ 12 - 0
Example/Pods/Target Support Files/SnapKit/SnapKit-prefix.pch

@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+

+ 16 - 0
Example/Pods/Target Support Files/SnapKit/SnapKit-umbrella.h

@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double SnapKitVersionNumber;
+FOUNDATION_EXPORT const unsigned char SnapKitVersionString[];
+

+ 12 - 0
Example/Pods/Target Support Files/SnapKit/SnapKit.debug.xcconfig

@@ -0,0 +1,12 @@
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SnapKit
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/SnapKit
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 6 - 0
Example/Pods/Target Support Files/SnapKit/SnapKit.modulemap

@@ -0,0 +1,6 @@
+framework module SnapKit {
+  umbrella header "SnapKit-umbrella.h"
+
+  export *
+  module * { export * }
+}

+ 12 - 0
Example/Pods/Target Support Files/SnapKit/SnapKit.release.xcconfig

@@ -0,0 +1,12 @@
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SnapKit
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/SnapKit
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä