├── .gitignore
├── .travis.yml
├── Example
├── MessengerBubbles.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── MessengerBubbles-Example.xcscheme
├── MessengerBubbles.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── MessengerBubbles
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── LaunchScreen.xib
│ │ └── Main.storyboard
│ ├── FromCodeCell.swift
│ ├── FromCodeViewController.swift
│ ├── FromStoryboardCell.swift
│ ├── FromStoryboardViewController.swift
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── cat.imageset
│ │ │ ├── Contents.json
│ │ │ └── cat.jpg
│ │ └── dog.imageset
│ │ │ ├── Contents.json
│ │ │ └── dog.jpg
│ ├── Info.plist
│ ├── Models+DataSource.swift
│ └── UIView+Layout.swift
├── Podfile
├── Podfile.lock
├── Pods
│ ├── Kingfisher
│ │ ├── LICENSE
│ │ ├── README.md
│ │ └── Sources
│ │ │ ├── Cache
│ │ │ ├── CacheSerializer.swift
│ │ │ ├── DiskStorage.swift
│ │ │ ├── FormatIndicatedCacheSerializer.swift
│ │ │ ├── ImageCache.swift
│ │ │ ├── MemoryStorage.swift
│ │ │ └── Storage.swift
│ │ │ ├── Extensions
│ │ │ ├── ImageView+Kingfisher.swift
│ │ │ └── UIButton+Kingfisher.swift
│ │ │ ├── General
│ │ │ ├── Deprecated.swift
│ │ │ ├── ImageSource
│ │ │ │ ├── ImageDataProvider.swift
│ │ │ │ ├── Resource.swift
│ │ │ │ └── Source.swift
│ │ │ ├── Kingfisher.swift
│ │ │ ├── KingfisherError.swift
│ │ │ ├── KingfisherManager.swift
│ │ │ └── KingfisherOptionsInfo.swift
│ │ │ ├── Image
│ │ │ ├── Filter.swift
│ │ │ ├── GIFAnimatedImage.swift
│ │ │ ├── Image.swift
│ │ │ ├── ImageDrawing.swift
│ │ │ ├── ImageFormat.swift
│ │ │ ├── ImageProcessor.swift
│ │ │ ├── ImageProgressive.swift
│ │ │ ├── ImageTransition.swift
│ │ │ └── Placeholder.swift
│ │ │ ├── Kingfisher.h
│ │ │ ├── Networking
│ │ │ ├── AuthenticationChallengeResponsable.swift
│ │ │ ├── ImageDataProcessor.swift
│ │ │ ├── ImageDownloader.swift
│ │ │ ├── ImageDownloaderDelegate.swift
│ │ │ ├── ImageModifier.swift
│ │ │ ├── ImagePrefetcher.swift
│ │ │ ├── RedirectHandler.swift
│ │ │ ├── RequestModifier.swift
│ │ │ ├── SessionDataTask.swift
│ │ │ └── SessionDelegate.swift
│ │ │ ├── Utility
│ │ │ ├── Box.swift
│ │ │ ├── CallbackQueue.swift
│ │ │ ├── Delegate.swift
│ │ │ ├── ExtensionHelpers.swift
│ │ │ ├── Result.swift
│ │ │ ├── Runtime.swift
│ │ │ ├── SizeExtensions.swift
│ │ │ └── String+MD5.swift
│ │ │ └── Views
│ │ │ ├── AnimatedImageView.swift
│ │ │ └── Indicator.swift
│ ├── Local Podspecs
│ │ └── MessengerBubbles.podspec.json
│ ├── Manifest.lock
│ ├── Pods.xcodeproj
│ │ └── project.pbxproj
│ └── Target Support Files
│ │ ├── Kingfisher
│ │ ├── Kingfisher-Info.plist
│ │ ├── Kingfisher-dummy.m
│ │ ├── Kingfisher-prefix.pch
│ │ ├── Kingfisher-umbrella.h
│ │ ├── Kingfisher.modulemap
│ │ └── Kingfisher.xcconfig
│ │ ├── MessengerBubbles
│ │ ├── MessengerBubbles-Info.plist
│ │ ├── MessengerBubbles-dummy.m
│ │ ├── MessengerBubbles-prefix.pch
│ │ ├── MessengerBubbles-umbrella.h
│ │ ├── MessengerBubbles.modulemap
│ │ └── MessengerBubbles.xcconfig
│ │ ├── Pods-MessengerBubbles_Example
│ │ ├── Pods-MessengerBubbles_Example-Info.plist
│ │ ├── Pods-MessengerBubbles_Example-acknowledgements.markdown
│ │ ├── Pods-MessengerBubbles_Example-acknowledgements.plist
│ │ ├── Pods-MessengerBubbles_Example-dummy.m
│ │ ├── Pods-MessengerBubbles_Example-frameworks.sh
│ │ ├── Pods-MessengerBubbles_Example-umbrella.h
│ │ ├── Pods-MessengerBubbles_Example.debug.xcconfig
│ │ ├── Pods-MessengerBubbles_Example.modulemap
│ │ └── Pods-MessengerBubbles_Example.release.xcconfig
│ │ └── Pods-MessengerBubbles_Tests
│ │ ├── Pods-MessengerBubbles_Tests-Info.plist
│ │ ├── Pods-MessengerBubbles_Tests-acknowledgements.markdown
│ │ ├── Pods-MessengerBubbles_Tests-acknowledgements.plist
│ │ ├── Pods-MessengerBubbles_Tests-dummy.m
│ │ ├── Pods-MessengerBubbles_Tests-umbrella.h
│ │ ├── Pods-MessengerBubbles_Tests.debug.xcconfig
│ │ ├── Pods-MessengerBubbles_Tests.modulemap
│ │ └── Pods-MessengerBubbles_Tests.release.xcconfig
└── Tests
│ ├── Info.plist
│ └── Tests.swift
├── LICENSE
├── MessengerBubbles.podspec
├── MessengerBubbles
├── Assets
│ └── .gitkeep
└── Classes
│ ├── .gitkeep
│ └── MessengerBubbles.swift
├── README.md
├── Screenshots
├── Banner.png
├── Storyboard.png
└── gif.gif
└── _Pods.xcodeproj
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata/
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 |
22 | # Bundler
23 | .bundle
24 |
25 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
26 | # Carthage/Checkouts
27 |
28 | Carthage/Build
29 |
30 | # We recommend against adding the Pods directory to your .gitignore. However
31 | # you should judge for yourself, the pros and cons are mentioned at:
32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
33 | #
34 | # Note: if you ignore the Pods directory, make sure to uncomment
35 | # `pod install` in .travis.yml
36 | #
37 | # Pods/
38 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # references:
2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/
3 | # * https://github.com/supermarin/xcpretty#usage
4 |
5 | osx_image: xcode7.3
6 | language: objective-c
7 | # cache: cocoapods
8 | # podfile: Example/Podfile
9 | # before_install:
10 | # - gem install cocoapods # Since Travis is not always on latest version
11 | # - pod install --project-directory=Example
12 | script:
13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/MessengerBubbles.xcworkspace -scheme MessengerBubbles-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty
14 | - pod lib lint
15 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles.xcodeproj/xcshareddata/xcschemes/MessengerBubbles-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
78 |
80 |
86 |
87 |
88 |
89 |
90 |
91 |
97 |
99 |
105 |
106 |
107 |
108 |
110 |
111 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // MessengerBubbles
4 | //
5 | // Created by Laurent Grondin on 06/28/2019.
6 | // Copyright (c) 2019 Laurent Grondin. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles/FromCodeCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FromCodeCell.swift
3 | // MessengerBubbles_Example
4 | //
5 | // Created by Laurent Grondin on 28/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MessengerBubbles
11 |
12 | class FromCodeCell: UITableViewCell, ConfigureCell {
13 |
14 | lazy var bubbleView: MessengerBubbles = {
15 | let iv = MessengerBubbles(size: 60)
16 | return iv
17 | }()
18 |
19 | lazy var nameLabel: UILabel = {
20 | let label = UILabel()
21 | label.font = UIFont.systemFont(ofSize: 17)
22 | return label
23 | }()
24 |
25 | lazy var descriptionLabel: UILabel = {
26 | let label = UILabel()
27 | label.font = UIFont.systemFont(ofSize: 15)
28 | label.textColor = .lightGray
29 | return label
30 | }()
31 |
32 | lazy var stackView: UIStackView = {
33 | let stackView = UIStackView(arrangedSubviews: [self.nameLabel, self.descriptionLabel])
34 | stackView.axis = .vertical
35 | stackView.distribution = .equalSpacing
36 | return stackView
37 | }()
38 |
39 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
40 | super.init(style: style, reuseIdentifier: reuseIdentifier)
41 | setupUI()
42 | }
43 |
44 | required init?(coder aDecoder: NSCoder) {
45 | fatalError("init(coder:) has not been implemented")
46 | }
47 |
48 | private func setupUI() {
49 | self.selectionStyle = .none
50 | bubbleView
51 | .addTo(contentView)
52 | .vAnchor(top: nil, leading: self.contentView.leadingAnchor, bottom: nil, trailing: nil, padding: .init(top: 0, left: 10, bottom: 0, right: 0))
53 | .centerYToSuperview()
54 | stackView
55 | .addTo(contentView)
56 | .vAnchor(top: bubbleView.topAnchor,
57 | leading: bubbleView.trailingAnchor,
58 | bottom: bubbleView.bottomAnchor,
59 | trailing: contentView.trailingAnchor,
60 | padding: .init(top: 5, left: 10, bottom: 5, right: 10))
61 | }
62 |
63 | func configure(with user: User?, isOnline: Bool) {
64 | guard let data = user else { return }
65 | bubbleView.setImages(with: [data.photo])
66 | bubbleView.isOnline = isOnline
67 | nameLabel.text = data.username
68 | descriptionLabel.text = data.description
69 | }
70 |
71 | func configure(with group: Group?, isOnline: Bool) {
72 | guard let data = group else { return }
73 | bubbleView.setImages(with: data.users.map { $0.photo })
74 | bubbleView.isOnline = isOnline
75 | nameLabel.text = data.name
76 | descriptionLabel.text = data.description
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles/FromCodeViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // MessengerBubbles
4 | //
5 | // Created by Laurent Grondin on 06/28/2019.
6 | // Copyright (c) 2019 Laurent Grondin. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MessengerBubbles
11 |
12 | class FromCodeViewController: UIViewController {
13 |
14 | deinit {
15 | print("FromCodeViewController deinit")
16 | }
17 |
18 | // MARK: - Views
19 |
20 | private lazy var onlineSwitch: UISwitch = {
21 | let _switch = UISwitch()
22 | _switch.addTarget(self, action: #selector(self.onlineSwitchValueChanged(sender:)), for: .valueChanged)
23 | return _switch
24 | }()
25 | private lazy var onlineLabel: UILabel = {
26 | let label = UILabel()
27 | label.text = "Online"
28 | return label
29 | }()
30 | private lazy var onlineStackView: UIStackView = {
31 | let stackView = UIStackView(arrangedSubviews: [self.onlineLabel, self.onlineSwitch])
32 | stackView.axis = .vertical
33 | stackView.spacing = 10
34 | stackView.alignment = .center
35 | return stackView
36 | }()
37 | private lazy var tableView: UITableView = {
38 | let tableView = UITableView(frame: .zero, style: .plain)
39 | tableView.delegate = self
40 | tableView.dataSource = self
41 | tableView.separatorStyle = .none
42 | tableView.register(FromCodeCell.self, forCellReuseIdentifier: reuseIdentifier)
43 | return tableView
44 | }()
45 |
46 | // MARK: - Properties
47 |
48 | private let reuseIdentifier = "FromCodeCell"
49 |
50 | // MARK: - Initializers
51 |
52 | var _title: String = ""
53 |
54 | override func viewDidLoad() {
55 | super.viewDidLoad()
56 | setupUI()
57 | }
58 |
59 | private func setupUI() {
60 | tableView
61 | .addTo(self.view)
62 | .vFillSuperview()
63 | onlineStackView
64 | .addTo(self.view)
65 | .vAnchor(top: nil, leading: nil, bottom: self.view.bottomAnchor, trailing: nil,
66 | padding: .init(top: 0, left: 0, bottom: 30, right: 0))
67 | .centerXToSuperview()
68 | }
69 |
70 | // MARK: - IBAction
71 |
72 | @objc private func onlineSwitchValueChanged(sender: UISwitch) {
73 | tableView.reloadData()
74 | }
75 | }
76 |
77 | // MARK: - Extensions
78 | // MARK: - TableView
79 |
80 | extension FromCodeViewController: UITableViewDataSource, UITableViewDelegate {
81 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
82 | return 2
83 | }
84 |
85 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
86 | let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! FromCodeCell
87 | switch indexPath.row {
88 | case 0:
89 | cell.configure(with: User.dataSource.first, isOnline: onlineSwitch.isOn)
90 | default:
91 | cell.configure(with: Group.dataSource, isOnline: onlineSwitch.isOn)
92 | }
93 | return cell
94 | }
95 |
96 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
97 | return 80
98 | }
99 | }
100 |
101 | // MARK: - String
102 |
103 | extension String {
104 | var image: UIImage? {
105 | return UIImage(named: self)
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles/FromStoryboardCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FromStoryboardCell.swift
3 | // MessengerBubbles_Example
4 | //
5 | // Created by Laurent Grondin on 28/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MessengerBubbles
11 |
12 | protocol ConfigureCell {
13 | func configure(with user: User?, isOnline: Bool)
14 | func configure(with group: Group?, isOnline: Bool)
15 | }
16 |
17 | class FromStoryboardCell: UITableViewCell, ConfigureCell {
18 |
19 | @IBOutlet weak var bubble: MessengerBubbles!
20 | @IBOutlet weak var nameLabel: UILabel!
21 | @IBOutlet weak var descriptionLabel: UILabel!
22 |
23 | func configure(with user: User?, isOnline: Bool) {
24 | guard let data = user else { return }
25 | bubble.setImages(with: [data.photo])
26 | bubble.isOnline = isOnline
27 | nameLabel.text = data.username
28 | descriptionLabel.text = data.description
29 | }
30 |
31 | func configure(with group: Group?, isOnline: Bool) {
32 | guard let data = group else { return }
33 | bubble.setImages(with: data.users.map { $0.photo })
34 | bubble.isOnline = isOnline
35 | nameLabel.text = data.name
36 | descriptionLabel.text = data.description
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles/FromStoryboardViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FromStoryboardViewController.swift
3 | // MessengerBubbles_Example
4 | //
5 | // Created by Laurent Grondin on 28/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MessengerBubbles
11 |
12 | class FromStoryboardViewController: UIViewController {
13 |
14 | deinit {
15 | print("FromStoryboardViewController deinit")
16 | }
17 |
18 | @IBOutlet weak var tableView: UITableView!
19 |
20 | @IBOutlet weak var onlineSwitch: UISwitch!
21 | private let reuseIdentifier = "FromStoryboardCell"
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 | }
26 |
27 | @IBAction func onlineSwitchValueChanged(_ sender: Any) {
28 | tableView.reloadData()
29 | }
30 | }
31 |
32 | extension FromStoryboardViewController: UITableViewDelegate, UITableViewDataSource {
33 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
34 | return 2
35 | }
36 |
37 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
38 | let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! FromStoryboardCell
39 | switch indexPath.row {
40 | case 0:
41 | cell.configure(with: User.dataSource.first, isOnline: onlineSwitch.isOn)
42 | default:
43 | cell.configure(with: Group.dataSource, isOnline: onlineSwitch.isOn)
44 | }
45 | return cell
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Example/MessengerBubbles/Images.xcassets/cat.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "cat.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/MessengerBubbles/Images.xcassets/cat.imageset/cat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RpX974/MessengerBubbles/4c1a00b62bd078c4fdfc25aa15ea0a37ea1ac422/Example/MessengerBubbles/Images.xcassets/cat.imageset/cat.jpg
--------------------------------------------------------------------------------
/Example/MessengerBubbles/Images.xcassets/dog.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "dog.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/MessengerBubbles/Images.xcassets/dog.imageset/dog.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RpX974/MessengerBubbles/4c1a00b62bd078c4fdfc25aa15ea0a37ea1ac422/Example/MessengerBubbles/Images.xcassets/dog.imageset/dog.jpg
--------------------------------------------------------------------------------
/Example/MessengerBubbles/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 |
38 | NSAppTransportSecurity
39 |
40 | NSAllowsArbitraryLoads
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles/Models+DataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Models.swift
3 | // MessengerBubbles_Example
4 | //
5 | // Created by Laurent Grondin on 28/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct User {
12 | let username: String
13 | let photo: String
14 | let description: String
15 | }
16 |
17 | struct Group {
18 | let users: [User]
19 | let name: String
20 | let description: String
21 | }
22 |
23 | extension User {
24 | static var dataSource: [User] {
25 | return [User(username: "John Doe",
26 | photo: "https://media.cdnandroid.com/b1/b5/9e/8f/imagen-3d-wild-animals-live-wallpaper-0big.jpg",
27 | description: "This is from a User"),
28 | User(username: "Janette Doe",
29 | photo: "https://ae01.alicdn.com/kf/HTB10hgEkol7MKJjSZFDq6yOEpXa6/1103-Fresco-Macro-Nyan-Cat-Occhiali-Wall-Sticker-Art-Poster-Per-La-Decorazione-Domestica-Pittura-Della.jpg_640x640.jpg",
30 | description: "This is from one User")
31 | ]
32 | }
33 | }
34 |
35 | extension Group {
36 | static var dataSource: Group {
37 | return Group(users: User.dataSource,
38 | name: "Party",
39 | description: "This is from a Group")
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Example/MessengerBubbles/UIView+Layout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Layout.swift
3 | // MessengerBubbles_Example
4 | //
5 | // Created by Laurent Grondin on 28/06/2019.
6 | // Copyright © 2019 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | typealias Constraint = NSLayoutConstraint
12 |
13 | struct AnchoredConstraints {
14 | var top, leading, bottom, trailing, width, height: NSLayoutConstraint?
15 | }
16 |
17 | extension UIView {
18 |
19 | @discardableResult
20 | func anchor(top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets = .zero, size: CGSize = .zero) -> AnchoredConstraints {
21 |
22 | translatesAutoresizingMaskIntoConstraints = false
23 | var anchoredConstraints = AnchoredConstraints()
24 |
25 | if let top = top {
26 | anchoredConstraints.top = topAnchor.constraint(equalTo: top, constant: padding.top)
27 | }
28 |
29 | if let leading = leading {
30 | anchoredConstraints.leading = leadingAnchor.constraint(equalTo: leading, constant: padding.left)
31 | }
32 |
33 | if let bottom = bottom {
34 | anchoredConstraints.bottom = bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom)
35 | }
36 |
37 | if let trailing = trailing {
38 | anchoredConstraints.trailing = trailingAnchor.constraint(equalTo: trailing, constant: -padding.right)
39 | }
40 |
41 | if size.width != 0 {
42 | anchoredConstraints.width = widthAnchor.constraint(equalToConstant: size.width)
43 | }
44 |
45 | if size.height != 0 {
46 | anchoredConstraints.height = heightAnchor.constraint(equalToConstant: size.height)
47 | }
48 |
49 | [anchoredConstraints.top, anchoredConstraints.leading, anchoredConstraints.bottom, anchoredConstraints.trailing, anchoredConstraints.width, anchoredConstraints.height].forEach{ $0?.isActive = true }
50 |
51 | return anchoredConstraints
52 | }
53 |
54 | @discardableResult
55 | func fillSuperview(padding: UIEdgeInsets = .zero) -> AnchoredConstraints {
56 | translatesAutoresizingMaskIntoConstraints = false
57 | let anchoredConstraints = AnchoredConstraints()
58 | guard let superviewTopAnchor = superview?.topAnchor,
59 | let superviewBottomAnchor = superview?.bottomAnchor,
60 | let superviewLeadingAnchor = superview?.leadingAnchor,
61 | let superviewTrailingAnchor = superview?.trailingAnchor else {
62 | return anchoredConstraints
63 | }
64 |
65 | return anchor(top: superviewTopAnchor, leading: superviewLeadingAnchor, bottom: superviewBottomAnchor, trailing: superviewTrailingAnchor, padding: padding)
66 | }
67 |
68 | @discardableResult
69 | func vAnchor(top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets = .zero, size: CGSize = .zero) -> Self {
70 |
71 | translatesAutoresizingMaskIntoConstraints = false
72 | var anchoredConstraints = AnchoredConstraints()
73 |
74 | if let top = top {
75 | anchoredConstraints.top = topAnchor.constraint(equalTo: top, constant: padding.top)
76 | }
77 |
78 | if let leading = leading {
79 | anchoredConstraints.leading = leadingAnchor.constraint(equalTo: leading, constant: padding.left)
80 | }
81 |
82 | if let bottom = bottom {
83 | anchoredConstraints.bottom = bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom)
84 | }
85 |
86 | if let trailing = trailing {
87 | anchoredConstraints.trailing = trailingAnchor.constraint(equalTo: trailing, constant: -padding.right)
88 | }
89 |
90 | if size.width != 0 {
91 | anchoredConstraints.width = widthAnchor.constraint(equalToConstant: size.width)
92 | }
93 |
94 | if size.height != 0 {
95 | anchoredConstraints.height = heightAnchor.constraint(equalToConstant: size.height)
96 | }
97 |
98 | [anchoredConstraints.top, anchoredConstraints.leading, anchoredConstraints.bottom, anchoredConstraints.trailing, anchoredConstraints.width, anchoredConstraints.height].forEach{ $0?.isActive = true }
99 |
100 | return self
101 | }
102 |
103 | @discardableResult
104 | func vFillSuperview(padding: UIEdgeInsets = .zero) -> Self {
105 | translatesAutoresizingMaskIntoConstraints = false
106 | guard let superviewTopAnchor = superview?.topAnchor,
107 | let superviewBottomAnchor = superview?.bottomAnchor,
108 | let superviewLeadingAnchor = superview?.leadingAnchor,
109 | let superviewTrailingAnchor = superview?.trailingAnchor else {
110 | return self
111 | }
112 |
113 | return vAnchor(top: superviewTopAnchor, leading: superviewLeadingAnchor, bottom: superviewBottomAnchor, trailing: superviewTrailingAnchor, padding: padding)
114 | }
115 |
116 | @discardableResult
117 | func centerInSuperview(size: CGSize = .zero) -> Self {
118 | translatesAutoresizingMaskIntoConstraints = false
119 | if let superviewCenterXAnchor = superview?.centerXAnchor {
120 | centerXAnchor.constraint(equalTo: superviewCenterXAnchor).isActive = true
121 | }
122 |
123 | if let superviewCenterYAnchor = superview?.centerYAnchor {
124 | centerYAnchor.constraint(equalTo: superviewCenterYAnchor).isActive = true
125 | }
126 |
127 | if size.width != 0 {
128 | widthAnchor.constraint(equalToConstant: size.width).isActive = true
129 | }
130 |
131 | if size.height != 0 {
132 | heightAnchor.constraint(equalToConstant: size.height).isActive = true
133 | }
134 | return self
135 | }
136 |
137 | @discardableResult
138 | func centerXToSuperview() -> Self {
139 | translatesAutoresizingMaskIntoConstraints = false
140 | if let superviewCenterXAnchor = superview?.centerXAnchor {
141 | centerXAnchor.constraint(equalTo: superviewCenterXAnchor).isActive = true
142 | }
143 | return self
144 | }
145 |
146 | @discardableResult
147 | func centerYToSuperview() -> Self {
148 | translatesAutoresizingMaskIntoConstraints = false
149 | if let superviewCenterYAnchor = superview?.centerYAnchor {
150 | centerYAnchor.constraint(equalTo: superviewCenterYAnchor).isActive = true
151 | }
152 | return self
153 | }
154 |
155 | @discardableResult
156 | func withSize(_ size: CGSize) -> Self {
157 | translatesAutoresizingMaskIntoConstraints = false
158 | widthAnchor.constraint(equalToConstant: size.width).isActive = true
159 | heightAnchor.constraint(equalToConstant: size.height).isActive = true
160 | return self
161 | }
162 |
163 | @discardableResult
164 | func withBorder(width: CGFloat, color: UIColor) -> Self {
165 | layer.borderWidth = width
166 | layer.borderColor = color.cgColor
167 | return self
168 | }
169 |
170 | @discardableResult
171 | func withCornerRadius(_ radius: CGFloat) -> Self {
172 | layer.cornerRadius = radius
173 | clipsToBounds = true
174 | return self
175 | }
176 |
177 | @discardableResult
178 | func addTo(_ view: UIView) -> Self {
179 | view.addSubview(self)
180 | return self
181 | }
182 |
183 | convenience init(backgroundColor: UIColor = .clear) {
184 | self.init(frame: .zero)
185 | self.backgroundColor = backgroundColor
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | use_frameworks!
2 |
3 | target 'MessengerBubbles_Example' do
4 | pod 'MessengerBubbles', :path => '../'
5 | pod 'Kingfisher', '~> 5.0'
6 |
7 | target 'MessengerBubbles_Tests' do
8 | inherit! :search_paths
9 |
10 |
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Kingfisher (5.6.0)
3 | - MessengerBubbles (0.1.0):
4 | - Kingfisher (~> 5.0)
5 |
6 | DEPENDENCIES:
7 | - Kingfisher (~> 5.0)
8 | - MessengerBubbles (from `../`)
9 |
10 | SPEC REPOS:
11 | https://github.com/cocoapods/specs.git:
12 | - Kingfisher
13 |
14 | EXTERNAL SOURCES:
15 | MessengerBubbles:
16 | :path: "../"
17 |
18 | SPEC CHECKSUMS:
19 | Kingfisher: 1a50df6880a60bae26f3e21a0df3d17b892f5e0b
20 | MessengerBubbles: 84246aaca2e82a0431bc2b7e3277c6c328f9c50a
21 |
22 | PODFILE CHECKSUM: 8dba77afc93785dacabc04a0e934e9cf4a7578bc
23 |
24 | COCOAPODS: 1.7.2
25 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Wei Wang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Cache/CacheSerializer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CacheSerializer.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2016/09/02.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// An `CacheSerializer` is used to convert some data to an image object after
30 | /// retrieving it from disk storage, and vice versa, to convert an image to data object
31 | /// for storing to the disk storage.
32 | public protocol CacheSerializer {
33 |
34 | /// Gets the serialized data from a provided image
35 | /// and optional original data for caching to disk.
36 | ///
37 | /// - Parameters:
38 | /// - image: The image needed to be serialized.
39 | /// - original: The original data which is just downloaded.
40 | /// If the image is retrieved from cache instead of
41 | /// downloaded, it will be `nil`.
42 | /// - Returns: The data object for storing to disk, or `nil` when no valid
43 | /// data could be serialized.
44 | func data(with image: Image, original: Data?) -> Data?
45 |
46 | /// Gets an image from provided serialized data.
47 | ///
48 | /// - Parameters:
49 | /// - data: The data from which an image should be deserialized.
50 | /// - options: The parsed options for deserialization.
51 | /// - Returns: An image deserialized or `nil` when no valid image
52 | /// could be deserialized.
53 | func image(with data: Data, options: KingfisherParsedOptionsInfo) -> Image?
54 |
55 | /// Gets an image deserialized from provided data.
56 | ///
57 | /// - Parameters:
58 | /// - data: The data from which an image should be deserialized.
59 | /// - options: Options for deserialization.
60 | /// - Returns: An image deserialized or `nil` when no valid image
61 | /// could be deserialized.
62 | /// - Note:
63 | /// This method is deprecated. Please implement the version with
64 | /// `KingfisherParsedOptionsInfo` as parameter instead.
65 | @available(*, deprecated,
66 | message: "Deprecated. Implement the method with same name but with `KingfisherParsedOptionsInfo` instead.")
67 | func image(with data: Data, options: KingfisherOptionsInfo?) -> Image?
68 | }
69 |
70 | extension CacheSerializer {
71 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? {
72 | return image(with: data, options: KingfisherParsedOptionsInfo(options))
73 | }
74 | }
75 |
76 | /// Represents a basic and default `CacheSerializer` used in Kingfisher disk cache system.
77 | /// It could serialize and deserialize images in PNG, JPEG and GIF format. For
78 | /// image other than these formats, a normalized `pngRepresentation` will be used.
79 | public struct DefaultCacheSerializer: CacheSerializer {
80 |
81 | /// The default general cache serializer used across Kingfisher's cache.
82 | public static let `default` = DefaultCacheSerializer()
83 | private init() {}
84 |
85 | /// - Parameters:
86 | /// - image: The image needed to be serialized.
87 | /// - original: The original data which is just downloaded.
88 | /// If the image is retrieved from cache instead of
89 | /// downloaded, it will be `nil`.
90 | /// - Returns: The data object for storing to disk, or `nil` when no valid
91 | /// data could be serialized.
92 | ///
93 | /// - Note:
94 | /// Only when `original` contains valid PNG, JPEG and GIF format data, the `image` will be
95 | /// converted to the corresponding data type. Otherwise, if the `original` is provided but it is not
96 | /// a valid format, the `original` data will be used for cache.
97 | ///
98 | /// If `original` is `nil`, the input `image` will be encoded as PNG data.
99 | public func data(with image: Image, original: Data?) -> Data? {
100 | return image.kf.data(format: original?.kf.imageFormat ?? .unknown)
101 | }
102 |
103 | /// Gets an image deserialized from provided data.
104 | ///
105 | /// - Parameters:
106 | /// - data: The data from which an image should be deserialized.
107 | /// - options: Options for deserialization.
108 | /// - Returns: An image deserialized or `nil` when no valid image
109 | /// could be deserialized.
110 | public func image(with data: Data, options: KingfisherParsedOptionsInfo) -> Image? {
111 | return KingfisherWrapper.image(data: data, options: options.imageCreatingOptions)
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Cache/FormatIndicatedCacheSerializer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RequestModifier.swift
3 | // Kingfisher
4 | //
5 | // Created by Junyu Kuang on 5/28/17.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// `FormatIndicatedCacheSerializer` lets you indicate an image format for serialized caches.
30 | ///
31 | /// It could serialize and deserialize PNG, JPEG and GIF images. For
32 | /// image other than these formats, a normalized `pngRepresentation` will be used.
33 | ///
34 | /// Example:
35 | /// ````
36 | /// let profileImageSize = CGSize(width: 44, height: 44)
37 | ///
38 | /// // A round corner image.
39 | /// let imageProcessor = RoundCornerImageProcessor(
40 | /// cornerRadius: profileImageSize.width / 2, targetSize: profileImageSize)
41 | ///
42 | /// let optionsInfo: KingfisherOptionsInfo = [
43 | /// .cacheSerializer(FormatIndicatedCacheSerializer.png),
44 | /// .processor(imageProcessor)]
45 | ///
46 | /// A URL pointing to a JPEG image.
47 | /// let url = URL(string: "https://example.com/image.jpg")!
48 | ///
49 | /// // Image will be always cached as PNG format to preserve alpha channel for round rectangle.
50 | /// // So when you load it from cache again later, it will be still round cornered.
51 | /// // Otherwise, the corner part would be filled by white color (since JPEG does not contain an alpha channel).
52 | /// imageView.kf.setImage(with: url, options: optionsInfo)
53 | /// ````
54 | public struct FormatIndicatedCacheSerializer: CacheSerializer {
55 |
56 | /// A `FormatIndicatedCacheSerializer` which converts image from and to PNG format. If the image cannot be
57 | /// represented by PNG format, it will fallback to its real format which is determined by `original` data.
58 | public static let png = FormatIndicatedCacheSerializer(imageFormat: .PNG)
59 |
60 | /// A `FormatIndicatedCacheSerializer` which converts image from and to JPEG format. If the image cannot be
61 | /// represented by JPEG format, it will fallback to its real format which is determined by `original` data.
62 | public static let jpeg = FormatIndicatedCacheSerializer(imageFormat: .JPEG)
63 |
64 | /// A `FormatIndicatedCacheSerializer` which converts image from and to GIF format. If the image cannot be
65 | /// represented by GIF format, it will fallback to its real format which is determined by `original` data.
66 | public static let gif = FormatIndicatedCacheSerializer(imageFormat: .GIF)
67 |
68 | /// The indicated image format.
69 | private let imageFormat: ImageFormat
70 |
71 | /// Creates data which represents the given `image` under a format.
72 | public func data(with image: Image, original: Data?) -> Data? {
73 |
74 | func imageData(withFormat imageFormat: ImageFormat) -> Data? {
75 | switch imageFormat {
76 | case .PNG: return image.kf.pngRepresentation()
77 | case .JPEG: return image.kf.jpegRepresentation(compressionQuality: 1.0)
78 | case .GIF: return image.kf.gifRepresentation()
79 | case .unknown: return nil
80 | }
81 | }
82 |
83 | // generate data with indicated image format
84 | if let data = imageData(withFormat: imageFormat) {
85 | return data
86 | }
87 |
88 | let originalFormat = original?.kf.imageFormat ?? .unknown
89 |
90 | // generate data with original image's format
91 | if originalFormat != imageFormat, let data = imageData(withFormat: originalFormat) {
92 | return data
93 | }
94 |
95 | return original ?? image.kf.normalized.kf.pngRepresentation()
96 | }
97 |
98 | /// Same implementation as `DefaultCacheSerializer`.
99 | public func image(with data: Data, options: KingfisherParsedOptionsInfo) -> Image? {
100 | return KingfisherWrapper.image(data: data, options: options.imageCreatingOptions)
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Cache/Storage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Storage.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2018/10/15.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Constants for some time intervals
30 | struct TimeConstants {
31 | static let secondsInOneMinute = 60
32 | static let minutesInOneHour = 60
33 | static let hoursInOneDay = 24
34 | static let secondsInOneDay = secondsInOneMinute * minutesInOneHour * hoursInOneDay
35 | }
36 |
37 | /// Represents the expiration strategy used in storage.
38 | ///
39 | /// - never: The item never expires.
40 | /// - seconds: The item expires after a time duration of given seconds from now.
41 | /// - days: The item expires after a time duration of given days from now.
42 | /// - date: The item expires after a given date.
43 | public enum StorageExpiration {
44 | /// The item never expires.
45 | case never
46 | /// The item expires after a time duration of given seconds from now.
47 | case seconds(TimeInterval)
48 | /// The item expires after a time duration of given days from now.
49 | case days(Int)
50 | /// The item expires after a given date.
51 | case date(Date)
52 | /// Indicates the item is already expired. Use this to skip cache.
53 | case expired
54 |
55 | func estimatedExpirationSince(_ date: Date) -> Date {
56 | switch self {
57 | case .never: return .distantFuture
58 | case .seconds(let seconds): return date.addingTimeInterval(seconds)
59 | case .days(let days): return date.addingTimeInterval(TimeInterval(TimeConstants.secondsInOneDay * days))
60 | case .date(let ref): return ref
61 | case .expired: return .distantPast
62 | }
63 | }
64 |
65 | var estimatedExpirationSinceNow: Date {
66 | return estimatedExpirationSince(Date())
67 | }
68 |
69 | var isExpired: Bool {
70 | return timeInterval <= 0
71 | }
72 |
73 | var timeInterval: TimeInterval {
74 | switch self {
75 | case .never: return .infinity
76 | case .seconds(let seconds): return seconds
77 | case .days(let days): return TimeInterval(TimeConstants.secondsInOneDay * days)
78 | case .date(let ref): return ref.timeIntervalSinceNow
79 | case .expired: return -(.infinity)
80 | }
81 | }
82 | }
83 |
84 | /// Represents the expiration extending strategy used in storage to after access.
85 | ///
86 | /// - none: The item expires after the original time, without extending after access.
87 | /// - cacheTime: The item expiration extends by the original cache time after each access.
88 | /// - expirationTime: The item expiration extends by the provided time after each access.
89 | public enum ExpirationExtending {
90 | /// The item expires after the original time, without extending after access.
91 | case none
92 | /// The item expiration extends by the original cache time after each access.
93 | case cacheTime
94 | /// The item expiration extends by the provided time after each access.
95 | case expirationTime(_ expiration: StorageExpiration)
96 | }
97 |
98 | /// Represents types which cost in memory can be calculated.
99 | public protocol CacheCostCalculable {
100 | var cacheCost: Int { get }
101 | }
102 |
103 | /// Represents types which can be converted to and from data.
104 | public protocol DataTransformable {
105 | func toData() throws -> Data
106 | static func fromData(_ data: Data) throws -> Self
107 | static var empty: Self { get }
108 | }
109 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/General/ImageSource/ImageDataProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageDataProvider.swift
3 | // Kingfisher
4 | //
5 | // Created by onevcat on 2018/11/13.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Represents a data provider to provide image data to Kingfisher when setting with
30 | /// `Source.provider` source. Compared to `Source.network` member, it gives a chance
31 | /// to load some image data in your own way, as long as you can provide the data
32 | /// representation for the image.
33 | public protocol ImageDataProvider {
34 |
35 | /// The key used in cache.
36 | var cacheKey: String { get }
37 |
38 | /// Provides the data which represents image. Kingfisher uses the data you pass in the
39 | /// handler to process images and caches it for later use.
40 | ///
41 | /// - Parameter handler: The handler you should call when you prepared your data.
42 | /// If the data is loaded successfully, call the handler with
43 | /// a `.success` with the data associated. Otherwise, call it
44 | /// with a `.failure` and pass the error.
45 | ///
46 | /// - Note:
47 | /// If the `handler` is called with a `.failure` with error, a `dataProviderError` of
48 | /// `ImageSettingErrorReason` will be finally thrown out to you as the `KingfisherError`
49 | /// from the framework.
50 | func data(handler: @escaping (Result) -> Void)
51 | }
52 |
53 | /// Represents an image data provider for loading from a local file URL on disk.
54 | /// Uses this type for adding a disk image to Kingfisher. Compared to loading it
55 | /// directly, you can get benefit of using Kingfisher's extension methods, as well
56 | /// as applying `ImageProcessor`s and storing the image to `ImageCache` of Kingfisher.
57 | public struct LocalFileImageDataProvider: ImageDataProvider {
58 |
59 | // MARK: Public Properties
60 |
61 | /// The file URL from which the image be loaded.
62 | public let fileURL: URL
63 |
64 | // MARK: Initializers
65 |
66 | /// Creates an image data provider by supplying the target local file URL.
67 | ///
68 | /// - Parameters:
69 | /// - fileURL: The file URL from which the image be loaded.
70 | /// - cacheKey: The key is used for caching the image data. By default,
71 | /// the `absoluteString` of `fileURL` is used.
72 | public init(fileURL: URL, cacheKey: String? = nil) {
73 | self.fileURL = fileURL
74 | self.cacheKey = cacheKey ?? fileURL.absoluteString
75 | }
76 |
77 | // MARK: Protocol Conforming
78 |
79 | /// The key used in cache.
80 | public var cacheKey: String
81 |
82 | public func data(handler: (Result) -> Void) {
83 | handler(Result(catching: { try Data(contentsOf: fileURL) }))
84 | }
85 | }
86 |
87 | /// Represents an image data provider for loading image from a given Base64 encoded string.
88 | public struct Base64ImageDataProvider: ImageDataProvider {
89 |
90 | // MARK: Public Properties
91 | /// The encoded Base64 string for the image.
92 | public let base64String: String
93 |
94 | // MARK: Initializers
95 |
96 | /// Creates an image data provider by supplying the Base64 encoded string.
97 | ///
98 | /// - Parameters:
99 | /// - base64String: The Base64 encoded string for an image.
100 | /// - cacheKey: The key is used for caching the image data. You need a different key for any different image.
101 | public init(base64String: String, cacheKey: String) {
102 | self.base64String = base64String
103 | self.cacheKey = cacheKey
104 | }
105 |
106 | // MARK: Protocol Conforming
107 |
108 | /// The key used in cache.
109 | public var cacheKey: String
110 |
111 | public func data(handler: (Result) -> Void) {
112 | let data = Data(base64Encoded: base64String)!
113 | handler(.success(data))
114 | }
115 | }
116 |
117 | /// Represents an image data provider for a raw data object.
118 | public struct RawImageDataProvider: ImageDataProvider {
119 |
120 | // MARK: Public Properties
121 |
122 | /// The raw data object to provide to Kingfisher image loader.
123 | public let data: Data
124 |
125 | // MARK: Initializers
126 |
127 | /// Creates an image data provider by the given raw `data` value and a `cacheKey` be used in Kingfisher cache.
128 | ///
129 | /// - Parameters:
130 | /// - data: The raw data reprensents an image.
131 | /// - cacheKey: The key is used for caching the image data. You need a different key for any different image.
132 | public init(data: Data, cacheKey: String) {
133 | self.data = data
134 | self.cacheKey = cacheKey
135 | }
136 |
137 | // MARK: Protocol Conforming
138 |
139 | /// The key used in cache.
140 | public var cacheKey: String
141 |
142 | public func data(handler: @escaping (Result) -> Void) {
143 | handler(.success(data))
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/General/ImageSource/Resource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Resource.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/4/6.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Represents an image resource at a certain url and a given cache key.
30 | /// Kingfisher will use a `Resource` to download a resource from network and cache it with the cache key when
31 | /// using `Source.network` as its image setting source.
32 | public protocol Resource {
33 |
34 | /// The key used in cache.
35 | var cacheKey: String { get }
36 |
37 | /// The target image URL.
38 | var downloadURL: URL { get }
39 | }
40 |
41 | /// ImageResource is a simple combination of `downloadURL` and `cacheKey`.
42 | /// When passed to image view set methods, Kingfisher will try to download the target
43 | /// image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache.
44 | public struct ImageResource: Resource {
45 |
46 | // MARK: - Initializers
47 |
48 | /// Creates an image resource.
49 | ///
50 | /// - Parameters:
51 | /// - downloadURL: The target image URL from where the image can be downloaded.
52 | /// - cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key.
53 | /// Default is `nil`.
54 | public init(downloadURL: URL, cacheKey: String? = nil) {
55 | self.downloadURL = downloadURL
56 | self.cacheKey = cacheKey ?? downloadURL.absoluteString
57 | }
58 |
59 | // MARK: Protocol Conforming
60 |
61 | /// The key used in cache.
62 | public let cacheKey: String
63 |
64 | /// The target image URL.
65 | public let downloadURL: URL
66 | }
67 |
68 | /// URL conforms to `Resource` in Kingfisher.
69 | /// The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`.
70 | /// If you need customize the url and/or cache key, use `ImageResource` instead.
71 | extension URL: Resource {
72 | public var cacheKey: String { return absoluteString }
73 | public var downloadURL: URL { return self }
74 | }
75 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/General/ImageSource/Source.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Source.swift
3 | // Kingfisher
4 | //
5 | // Created by onevcat on 2018/11/17.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Represents an image setting source for Kingfisher methods.
30 | ///
31 | /// A `Source` value indicates the way how the target image can be retrieved and cached.
32 | ///
33 | /// - network: The target image should be got from network remotely. The associated `Resource`
34 | /// value defines detail information like image URL and cache key.
35 | /// - provider: The target image should be provided in a data format. Normally, it can be an image
36 | /// from local storage or in any other encoding format (like Base64).
37 | public enum Source {
38 |
39 | /// Represents the source task identifier when setting an image to a view with extension methods.
40 | public enum Identifier {
41 |
42 | /// The underlying value type of source identifier.
43 | public typealias Value = UInt
44 | static var current: Value = 0
45 | static func next() -> Value {
46 | current += 1
47 | return current
48 | }
49 | }
50 |
51 | // MARK: Member Cases
52 |
53 | /// The target image should be got from network remotely. The associated `Resource`
54 | /// value defines detail information like image URL and cache key.
55 | case network(Resource)
56 |
57 | /// The target image should be provided in a data format. Normally, it can be an image
58 | /// from local storage or in any other encoding format (like Base64).
59 | case provider(ImageDataProvider)
60 |
61 | // MARK: Getting Properties
62 |
63 | /// The cache key defined for this source value.
64 | public var cacheKey: String {
65 | switch self {
66 | case .network(let resource): return resource.cacheKey
67 | case .provider(let provider): return provider.cacheKey
68 | }
69 | }
70 |
71 | /// The URL defined for this source value.
72 | ///
73 | /// For a `.network` source, it is the `downloadURL` of associated `Resource` instance.
74 | /// For a `.provider` value, it is always `nil`.
75 | public var url: URL? {
76 | switch self {
77 | case .network(let resource): return resource.downloadURL
78 | // `ImageDataProvider` does not provide a URL. All it cares is how to get the data back.
79 | case .provider(_): return nil
80 | }
81 | }
82 | }
83 |
84 | extension Source {
85 | var asResource: Resource? {
86 | guard case .network(let resource) = self else {
87 | return nil
88 | }
89 | return resource
90 | }
91 |
92 | var asProvider: ImageDataProvider? {
93 | guard case .provider(let provider) = self else {
94 | return nil
95 | }
96 | return provider
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/General/Kingfisher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Kingfisher.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 16/9/14.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 | import ImageIO
29 |
30 | #if os(macOS)
31 | import AppKit
32 | public typealias Image = NSImage
33 | public typealias View = NSView
34 | public typealias Color = NSColor
35 | public typealias ImageView = NSImageView
36 | public typealias Button = NSButton
37 | #else
38 | import UIKit
39 | public typealias Image = UIImage
40 | public typealias Color = UIColor
41 | #if !os(watchOS)
42 | public typealias ImageView = UIImageView
43 | public typealias View = UIView
44 | public typealias Button = UIButton
45 | #else
46 | import WatchKit
47 | #endif
48 | #endif
49 |
50 | /// Wrapper for Kingfisher compatible types. This type provides an extension point for
51 | /// connivence methods in Kingfisher.
52 | public struct KingfisherWrapper {
53 | public let base: Base
54 | public init(_ base: Base) {
55 | self.base = base
56 | }
57 | }
58 |
59 | /// Represents an object type that is compatible with Kingfisher. You can use `kf` property to get a
60 | /// value in the namespace of Kingfisher.
61 | public protocol KingfisherCompatible: AnyObject { }
62 |
63 | /// Represents a value type that is compatible with Kingfisher. You can use `kf` property to get a
64 | /// value in the namespace of Kingfisher.
65 | public protocol KingfisherCompatibleValue {}
66 |
67 | extension KingfisherCompatible {
68 | /// Gets a namespace holder for Kingfisher compatible types.
69 | public var kf: KingfisherWrapper {
70 | get { return KingfisherWrapper(self) }
71 | set { }
72 | }
73 | }
74 |
75 | extension KingfisherCompatibleValue {
76 | /// Gets a namespace holder for Kingfisher compatible types.
77 | public var kf: KingfisherWrapper {
78 | get { return KingfisherWrapper(self) }
79 | set { }
80 | }
81 | }
82 |
83 | extension Image: KingfisherCompatible { }
84 | #if !os(watchOS)
85 | extension ImageView: KingfisherCompatible { }
86 | extension Button: KingfisherCompatible { }
87 | #else
88 | extension WKInterfaceImage: KingfisherCompatible { }
89 | #endif
90 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Image/Filter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Filter.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2016/08/31.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import CoreImage
28 |
29 | // Reuse the same CI Context for all CI drawing.
30 | private let ciContext = CIContext(options: nil)
31 |
32 | /// Represents the type of transformer method, which will be used in to provide a `Filter`.
33 | public typealias Transformer = (CIImage) -> CIImage?
34 |
35 | /// Represents a processor based on a `CIImage` `Filter`.
36 | /// It requires a filter to create an `ImageProcessor`.
37 | public protocol CIImageProcessor: ImageProcessor {
38 | var filter: Filter { get }
39 | }
40 |
41 | extension CIImageProcessor {
42 |
43 | /// Processes the input `ImageProcessItem` with this processor.
44 | ///
45 | /// - Parameters:
46 | /// - item: Input item which will be processed by `self`.
47 | /// - options: Options when processing the item.
48 | /// - Returns: The processed image.
49 | ///
50 | /// - Note: See documentation of `ImageProcessor` protocol for more.
51 | public func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> Image? {
52 | switch item {
53 | case .image(let image):
54 | return image.kf.apply(filter)
55 | case .data:
56 | return (DefaultImageProcessor.default >> self).process(item: item, options: options)
57 | }
58 | }
59 | }
60 |
61 | /// A wrapper struct for a `Transformer` of CIImage filters. A `Filter`
62 | /// value could be used to create a `CIImage` processor.
63 | public struct Filter {
64 |
65 | let transform: Transformer
66 |
67 | public init(transform: @escaping Transformer) {
68 | self.transform = transform
69 | }
70 |
71 | /// Tint filter which will apply a tint color to images.
72 | public static var tint: (Color) -> Filter = {
73 | color in
74 | Filter {
75 | input in
76 |
77 | let colorFilter = CIFilter(name: "CIConstantColorGenerator")!
78 | colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey)
79 |
80 | let filter = CIFilter(name: "CISourceOverCompositing")!
81 |
82 | let colorImage = colorFilter.outputImage
83 | filter.setValue(colorImage, forKey: kCIInputImageKey)
84 | filter.setValue(input, forKey: kCIInputBackgroundImageKey)
85 |
86 | return filter.outputImage?.cropped(to: input.extent)
87 | }
88 | }
89 |
90 | /// Represents color control elements. It is a tuple of
91 | /// `(brightness, contrast, saturation, inputEV)`
92 | public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat)
93 |
94 | /// Color control filter which will apply color control change to images.
95 | public static var colorControl: (ColorElement) -> Filter = { arg -> Filter in
96 | let (brightness, contrast, saturation, inputEV) = arg
97 | return Filter { input in
98 | let paramsColor = [kCIInputBrightnessKey: brightness,
99 | kCIInputContrastKey: contrast,
100 | kCIInputSaturationKey: saturation]
101 | let blackAndWhite = input.applyingFilter("CIColorControls", parameters: paramsColor)
102 | let paramsExposure = [kCIInputEVKey: inputEV]
103 | return blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure)
104 | }
105 | }
106 | }
107 |
108 | extension KingfisherWrapper where Base: Image {
109 |
110 | /// Applies a `Filter` containing `CIImage` transformer to `self`.
111 | ///
112 | /// - Parameter filter: The filter used to transform `self`.
113 | /// - Returns: A transformed image by input `Filter`.
114 | ///
115 | /// - Note:
116 | /// Only CG-based images are supported. If any error happens
117 | /// during transforming, `self` will be returned.
118 | public func apply(_ filter: Filter) -> Image {
119 |
120 | guard let cgImage = cgImage else {
121 | assertionFailure("[Kingfisher] Tint image only works for CG-based image.")
122 | return base
123 | }
124 |
125 | let inputImage = CIImage(cgImage: cgImage)
126 | guard let outputImage = filter.transform(inputImage) else {
127 | return base
128 | }
129 |
130 | guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else {
131 | assertionFailure("[Kingfisher] Can not make an tint image within context.")
132 | return base
133 | }
134 |
135 | #if os(macOS)
136 | return fixedForRetinaPixel(cgImage: result, to: size)
137 | #else
138 | return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation)
139 | #endif
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Image/GIFAnimatedImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AnimatedImage.swift
3 | // Kingfisher
4 | //
5 | // Created by onevcat on 2018/09/26.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 | import ImageIO
29 |
30 | /// Represents a set of image creating options used in Kingfisher.
31 | public struct ImageCreatingOptions {
32 |
33 | /// The target scale of image needs to be created.
34 | public let scale: CGFloat
35 |
36 | /// The expected animation duration if an animated image being created.
37 | public let duration: TimeInterval
38 |
39 | /// For an animated image, whether or not all frames should be loaded before displaying.
40 | public let preloadAll: Bool
41 |
42 | /// For an animated image, whether or not only the first image should be
43 | /// loaded as a static image. It is useful for preview purpose of an animated image.
44 | public let onlyFirstFrame: Bool
45 |
46 | /// Creates an `ImageCreatingOptions` object.
47 | ///
48 | /// - Parameters:
49 | /// - scale: The target scale of image needs to be created. Default is `1.0`.
50 | /// - duration: The expected animation duration if an animated image being created.
51 | /// A value less or equal to `0.0` means the animated image duration will
52 | /// be determined by the frame data. Default is `0.0`.
53 | /// - preloadAll: For an animated image, whether or not all frames should be loaded before displaying.
54 | /// Default is `false`.
55 | /// - onlyFirstFrame: For an animated image, whether or not only the first image should be
56 | /// loaded as a static image. It is useful for preview purpose of an animated image.
57 | /// Default is `false`.
58 | public init(
59 | scale: CGFloat = 1.0,
60 | duration: TimeInterval = 0.0,
61 | preloadAll: Bool = false,
62 | onlyFirstFrame: Bool = false)
63 | {
64 | self.scale = scale
65 | self.duration = duration
66 | self.preloadAll = preloadAll
67 | self.onlyFirstFrame = onlyFirstFrame
68 | }
69 | }
70 |
71 | // Represents the decoding for a GIF image. This class extracts frames from an `imageSource`, then
72 | // hold the images for later use.
73 | class GIFAnimatedImage {
74 | let images: [Image]
75 | let duration: TimeInterval
76 |
77 | init?(from imageSource: CGImageSource, for info: [String: Any], options: ImageCreatingOptions) {
78 | let frameCount = CGImageSourceGetCount(imageSource)
79 | var images = [Image]()
80 | var gifDuration = 0.0
81 |
82 | for i in 0 ..< frameCount {
83 | guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, info as CFDictionary) else {
84 | return nil
85 | }
86 |
87 | if frameCount == 1 {
88 | gifDuration = .infinity
89 | } else {
90 | // Get current animated GIF frame duration
91 | gifDuration += GIFAnimatedImage.getFrameDuration(from: imageSource, at: i)
92 | }
93 | images.append(KingfisherWrapper.image(cgImage: imageRef, scale: options.scale, refImage: nil))
94 | if options.onlyFirstFrame { break }
95 | }
96 | self.images = images
97 | self.duration = gifDuration
98 | }
99 |
100 | // Calculates frame duration for a gif frame out of the kCGImagePropertyGIFDictionary dictionary.
101 | static func getFrameDuration(from gifInfo: [String: Any]?) -> TimeInterval {
102 | let defaultFrameDuration = 0.1
103 | guard let gifInfo = gifInfo else { return defaultFrameDuration }
104 |
105 | let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as? NSNumber
106 | let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber
107 | let duration = unclampedDelayTime ?? delayTime
108 |
109 | guard let frameDuration = duration else { return defaultFrameDuration }
110 | return frameDuration.doubleValue > 0.011 ? frameDuration.doubleValue : defaultFrameDuration
111 | }
112 |
113 | // Calculates frame duration at a specific index for a gif from an `imageSource`.
114 | static func getFrameDuration(from imageSource: CGImageSource, at index: Int) -> TimeInterval {
115 | guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, index, nil)
116 | as? [String: Any] else { return 0.0 }
117 |
118 | let gifInfo = properties[kCGImagePropertyGIFDictionary as String] as? [String: Any]
119 | return getFrameDuration(from: gifInfo)
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Image/ImageFormat.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageFormat.swift
3 | // Kingfisher
4 | //
5 | // Created by onevcat on 2018/09/28.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Represents image format.
30 | ///
31 | /// - unknown: The format cannot be recognized or not supported yet.
32 | /// - PNG: PNG image format.
33 | /// - JPEG: JPEG image format.
34 | /// - GIF: GIF image format.
35 | public enum ImageFormat {
36 | /// The format cannot be recognized or not supported yet.
37 | case unknown
38 | /// PNG image format.
39 | case PNG
40 | /// JPEG image format.
41 | case JPEG
42 | /// GIF image format.
43 | case GIF
44 |
45 | struct HeaderData {
46 | static var PNG: [UInt8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
47 | static var JPEG_SOI: [UInt8] = [0xFF, 0xD8]
48 | static var JPEG_IF: [UInt8] = [0xFF]
49 | static var GIF: [UInt8] = [0x47, 0x49, 0x46]
50 | }
51 |
52 | /// https://en.wikipedia.org/wiki/JPEG
53 | public enum JPEGMarker {
54 | case SOF0 //baseline
55 | case SOF2 //progressive
56 | case DHT //Huffman Table
57 | case DQT //Quantization Table
58 | case DRI //Restart Interval
59 | case SOS //Start Of Scan
60 | case RSTn(UInt8) //Restart
61 | case APPn //Application-specific
62 | case COM //Comment
63 | case EOI //End Of Image
64 |
65 | var bytes: [UInt8] {
66 | switch self {
67 | case .SOF0: return [0xFF, 0xC0]
68 | case .SOF2: return [0xFF, 0xC2]
69 | case .DHT: return [0xFF, 0xC4]
70 | case .DQT: return [0xFF, 0xDB]
71 | case .DRI: return [0xFF, 0xDD]
72 | case .SOS: return [0xFF, 0xDA]
73 | case .RSTn(let n): return [0xFF, 0xD0 + n]
74 | case .APPn: return [0xFF, 0xE0]
75 | case .COM: return [0xFF, 0xFE]
76 | case .EOI: return [0xFF, 0xD9]
77 | }
78 | }
79 | }
80 | }
81 |
82 |
83 | extension Data: KingfisherCompatibleValue {}
84 |
85 | // MARK: - Misc Helpers
86 | extension KingfisherWrapper where Base == Data {
87 | /// Gets the image format corresponding to the data.
88 | public var imageFormat: ImageFormat {
89 | guard base.count > 8 else { return .unknown }
90 |
91 | var buffer = [UInt8](repeating: 0, count: 8)
92 | base.copyBytes(to: &buffer, count: 8)
93 |
94 | if buffer == ImageFormat.HeaderData.PNG {
95 | return .PNG
96 |
97 | } else if buffer[0] == ImageFormat.HeaderData.JPEG_SOI[0],
98 | buffer[1] == ImageFormat.HeaderData.JPEG_SOI[1],
99 | buffer[2] == ImageFormat.HeaderData.JPEG_IF[0]
100 | {
101 | return .JPEG
102 |
103 | } else if buffer[0] == ImageFormat.HeaderData.GIF[0],
104 | buffer[1] == ImageFormat.HeaderData.GIF[1],
105 | buffer[2] == ImageFormat.HeaderData.GIF[2]
106 | {
107 | return .GIF
108 | }
109 |
110 | return .unknown
111 | }
112 |
113 | public func contains(jpeg marker: ImageFormat.JPEGMarker) -> Bool {
114 | guard imageFormat == .JPEG else {
115 | return false
116 | }
117 |
118 | var buffer = [UInt8](repeating: 0, count: base.count)
119 | base.copyBytes(to: &buffer, count: base.count)
120 | for (index, item) in buffer.enumerated() {
121 | guard
122 | item == marker.bytes.first,
123 | buffer.count > index + 1,
124 | buffer[index + 1] == marker.bytes[1] else {
125 | continue
126 | }
127 | return true
128 | }
129 | return false
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Image/ImageTransition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageTransition.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/9/18.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | #if os(iOS) || os(tvOS)
28 | import UIKit
29 |
30 | /// Transition effect which will be used when an image downloaded and set by `UIImageView`
31 | /// extension API in Kingfisher. You can assign an enum value with transition duration as
32 | /// an item in `KingfisherOptionsInfo` to enable the animation transition.
33 | ///
34 | /// Apple's UIViewAnimationOptions is used under the hood.
35 | /// For custom transition, you should specified your own transition options, animations and
36 | /// completion handler as well.
37 | ///
38 | /// - none: No animation transition.
39 | /// - fade: Fade in the loaded image in a given duration.
40 | /// - flipFromLeft: Flip from left transition.
41 | /// - flipFromRight: Flip from right transition.
42 | /// - flipFromTop: Flip from top transition.
43 | /// - flipFromBottom: Flip from bottom transition.
44 | /// - custom: Custom transition.
45 | public enum ImageTransition {
46 | /// No animation transition.
47 | case none
48 | /// Fade in the loaded image in a given duration.
49 | case fade(TimeInterval)
50 | /// Flip from left transition.
51 | case flipFromLeft(TimeInterval)
52 | /// Flip from right transition.
53 | case flipFromRight(TimeInterval)
54 | /// Flip from top transition.
55 | case flipFromTop(TimeInterval)
56 | /// Flip from bottom transition.
57 | case flipFromBottom(TimeInterval)
58 | /// Custom transition defined by a general animation block.
59 | /// - duration: The time duration of this custom transition.
60 | /// - options: `UIView.AnimationOptions` should be used in the transition.
61 | /// - animations: The animation block will be applied when setting image.
62 | /// - completion: A block called when the transition animation finishes.
63 | case custom(duration: TimeInterval,
64 | options: UIView.AnimationOptions,
65 | animations: ((UIImageView, UIImage) -> Void)?,
66 | completion: ((Bool) -> Void)?)
67 |
68 | var duration: TimeInterval {
69 | switch self {
70 | case .none: return 0
71 | case .fade(let duration): return duration
72 |
73 | case .flipFromLeft(let duration): return duration
74 | case .flipFromRight(let duration): return duration
75 | case .flipFromTop(let duration): return duration
76 | case .flipFromBottom(let duration): return duration
77 |
78 | case .custom(let duration, _, _, _): return duration
79 | }
80 | }
81 |
82 | var animationOptions: UIView.AnimationOptions {
83 | switch self {
84 | case .none: return []
85 | case .fade: return .transitionCrossDissolve
86 |
87 | case .flipFromLeft: return .transitionFlipFromLeft
88 | case .flipFromRight: return .transitionFlipFromRight
89 | case .flipFromTop: return .transitionFlipFromTop
90 | case .flipFromBottom: return .transitionFlipFromBottom
91 |
92 | case .custom(_, let options, _, _): return options
93 | }
94 | }
95 |
96 | var animations: ((UIImageView, UIImage) -> Void)? {
97 | switch self {
98 | case .custom(_, _, let animations, _): return animations
99 | default: return { $0.image = $1 }
100 | }
101 | }
102 |
103 | var completion: ((Bool) -> Void)? {
104 | switch self {
105 | case .custom(_, _, _, let completion): return completion
106 | default: return nil
107 | }
108 | }
109 | }
110 | #else
111 | // Just a placeholder for compiling on macOS.
112 | public enum ImageTransition {
113 | case none
114 | }
115 | #endif
116 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Image/Placeholder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Placeholder.swift
3 | // Kingfisher
4 | //
5 | // Created by Tieme van Veen on 28/08/2017.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | #if os(macOS)
28 | import AppKit
29 | #else
30 | import UIKit
31 | #endif
32 |
33 | /// Represents a placeholder type which could be set while loading as well as
34 | /// loading finished without getting an image.
35 | public protocol Placeholder {
36 |
37 | /// How the placeholder should be added to a given image view.
38 | func add(to imageView: ImageView)
39 |
40 | /// How the placeholder should be removed from a given image view.
41 | func remove(from imageView: ImageView)
42 | }
43 |
44 | /// Default implementation of an image placeholder. The image will be set or
45 | /// reset directly for `image` property of the image view.
46 | extension Image: Placeholder {
47 | /// How the placeholder should be added to a given image view.
48 | public func add(to imageView: ImageView) { imageView.image = self }
49 |
50 | /// How the placeholder should be removed from a given image view.
51 | public func remove(from imageView: ImageView) { imageView.image = nil }
52 | }
53 |
54 | /// Default implementation of an arbitrary view as placeholder. The view will be
55 | /// added as a subview when adding and be removed from its super view when removing.
56 | ///
57 | /// To use your customize View type as placeholder, simply let it conforming to
58 | /// `Placeholder` by `extension MyView: Placeholder {}`.
59 | extension Placeholder where Self: View {
60 |
61 | /// How the placeholder should be added to a given image view.
62 | public func add(to imageView: ImageView) {
63 | imageView.addSubview(self)
64 | translatesAutoresizingMaskIntoConstraints = false
65 |
66 | centerXAnchor.constraint(equalTo: imageView.centerXAnchor).isActive = true
67 | centerYAnchor.constraint(equalTo: imageView.centerYAnchor).isActive = true
68 | heightAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true
69 | widthAnchor.constraint(equalTo: imageView.widthAnchor).isActive = true
70 | }
71 |
72 | /// How the placeholder should be removed from a given image view.
73 | public func remove(from imageView: ImageView) {
74 | removeFromSuperview()
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Kingfisher.h:
--------------------------------------------------------------------------------
1 | //
2 | // Kingfisher.h
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/4/6.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | #import
28 |
29 | //! Project version number for Kingfisher.
30 | FOUNDATION_EXPORT double KingfisherVersionNumber;
31 |
32 | //! Project version string for Kingfisher.
33 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[];
34 |
35 | // In this header, you should import all the public headers of your framework using statements like #import
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Networking/AuthenticationChallengeResponsable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthenticationChallengeResponsable.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2018/10/11.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Protocol indicates that an authentication challenge could be handled.
30 | public protocol AuthenticationChallengeResponsable: AnyObject {
31 |
32 | /// Called when a session level authentication challenge is received.
33 | /// This method provide a chance to handle and response to the authentication
34 | /// challenge before downloading could start.
35 | ///
36 | /// - Parameters:
37 | /// - downloader: The downloader which receives this challenge.
38 | /// - challenge: An object that contains the request for authentication.
39 | /// - completionHandler: A handler that your delegate method must call.
40 | ///
41 | /// - Note: This method is a forward from `URLSessionDelegate.urlSession(:didReceiveChallenge:completionHandler:)`.
42 | /// Please refer to the document of it in `URLSessionDelegate`.
43 | func downloader(
44 | _ downloader: ImageDownloader,
45 | didReceive challenge: URLAuthenticationChallenge,
46 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
47 |
48 | /// Called when a task level authentication challenge is received.
49 | /// This method provide a chance to handle and response to the authentication
50 | /// challenge before downloading could start.
51 | ///
52 | /// - Parameters:
53 | /// - downloader: The downloader which receives this challenge.
54 | /// - task: The task whose request requires authentication.
55 | /// - challenge: An object that contains the request for authentication.
56 | /// - completionHandler: A handler that your delegate method must call.
57 | func downloader(
58 | _ downloader: ImageDownloader,
59 | task: URLSessionTask,
60 | didReceive challenge: URLAuthenticationChallenge,
61 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
62 | }
63 |
64 | extension AuthenticationChallengeResponsable {
65 |
66 | public func downloader(
67 | _ downloader: ImageDownloader,
68 | didReceive challenge: URLAuthenticationChallenge,
69 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
70 | {
71 | if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
72 | if let trustedHosts = downloader.trustedHosts, trustedHosts.contains(challenge.protectionSpace.host) {
73 | let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
74 | completionHandler(.useCredential, credential)
75 | return
76 | }
77 | }
78 |
79 | completionHandler(.performDefaultHandling, nil)
80 | }
81 |
82 | public func downloader(
83 | _ downloader: ImageDownloader,
84 | task: URLSessionTask,
85 | didReceive challenge: URLAuthenticationChallenge,
86 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
87 | {
88 | completionHandler(.performDefaultHandling, nil)
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Networking/ImageDataProcessor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageDataProcessor.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2018/10/11.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | private let sharedProcessingQueue: CallbackQueue =
30 | .dispatch(DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Process"))
31 |
32 | // Handles image processing work on an own process queue.
33 | class ImageDataProcessor {
34 | let data: Data
35 | let callbacks: [SessionDataTask.TaskCallback]
36 | let queue: CallbackQueue
37 |
38 | // Note: We have an optimization choice there, to reduce queue dispatch by checking callback
39 | // queue settings in each option...
40 | let onImageProcessed = Delegate<(Result, SessionDataTask.TaskCallback), Void>()
41 |
42 | init(data: Data, callbacks: [SessionDataTask.TaskCallback], processingQueue: CallbackQueue?) {
43 | self.data = data
44 | self.callbacks = callbacks
45 | self.queue = processingQueue ?? sharedProcessingQueue
46 | }
47 |
48 | func process() {
49 | queue.execute(doProcess)
50 | }
51 |
52 | private func doProcess() {
53 | var processedImages = [String: Image]()
54 | for callback in callbacks {
55 | let processor = callback.options.processor
56 | var image = processedImages[processor.identifier]
57 | if image == nil {
58 | image = processor.process(item: .data(data), options: callback.options)
59 | processedImages[processor.identifier] = image
60 | }
61 |
62 | let result: Result
63 | if let image = image {
64 | var finalImage = image
65 | if let imageModifier = callback.options.imageModifier {
66 | finalImage = imageModifier.modify(image)
67 | }
68 | if callback.options.backgroundDecode {
69 | finalImage = finalImage.kf.decoded
70 | }
71 | result = .success(finalImage)
72 | } else {
73 | let error = KingfisherError.processorError(
74 | reason: .processingFailed(processor: processor, item: .data(data)))
75 | result = .failure(error)
76 | }
77 | onImageProcessed.call((result, callback))
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Networking/ImageDownloaderDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageDownloaderDelegate.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2018/10/11.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Protocol of `ImageDownloader`. This protocol provides a set of methods which are related to image downloader
30 | /// working stages and rules.
31 | public protocol ImageDownloaderDelegate: AnyObject {
32 |
33 | /// Called when the `ImageDownloader` object will start downloading an image from a specified URL.
34 | ///
35 | /// - Parameters:
36 | /// - downloader: The `ImageDownloader` object which is used for the downloading operation.
37 | /// - url: URL of the starting request.
38 | /// - request: The request object for the download process.
39 | ///
40 | func imageDownloader(_ downloader: ImageDownloader, willDownloadImageForURL url: URL, with request: URLRequest?)
41 |
42 | /// Called when the `ImageDownloader` completes a downloading request with success or failure.
43 | ///
44 | /// - Parameters:
45 | /// - downloader: The `ImageDownloader` object which is used for the downloading operation.
46 | /// - url: URL of the original request URL.
47 | /// - response: The response object of the downloading process.
48 | /// - error: The error in case of failure.
49 | ///
50 | func imageDownloader(
51 | _ downloader: ImageDownloader,
52 | didFinishDownloadingImageForURL url: URL,
53 | with response: URLResponse?,
54 | error: Error?)
55 |
56 | /// Called when the `ImageDownloader` object successfully downloaded image data from specified URL. This is
57 | /// your last chance to verify or modify the downloaded data before Kingfisher tries to perform addition
58 | /// processing on the image data.
59 | ///
60 | /// - Parameters:
61 | /// - downloader: The `ImageDownloader` object which is used for the downloading operation.
62 | /// - data: The original downloaded data.
63 | /// - url: The URL of the original request URL.
64 | /// - Returns: The data from which Kingfisher should use to create an image. You need to provide valid data
65 | /// which content is one of the supported image file format. Kingfisher will perform process on this
66 | /// data and try to convert it to an image object.
67 | /// - Note:
68 | /// This can be used to pre-process raw image data before creation of `Image` instance (i.e.
69 | /// decrypting or verification). If `nil` returned, the processing is interrupted and a `KingfisherError` with
70 | /// `ResponseErrorReason.dataModifyingFailed` will be raised. You could use this fact to stop the image
71 | /// processing flow if you find the data is corrupted or malformed.
72 | func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data?
73 |
74 | /// Called when the `ImageDownloader` object successfully downloads and processes an image from specified URL.
75 | ///
76 | /// - Parameters:
77 | /// - downloader: The `ImageDownloader` object which is used for the downloading operation.
78 | /// - image: The downloaded and processed image.
79 | /// - url: URL of the original request URL.
80 | /// - response: The original response object of the downloading process.
81 | ///
82 | func imageDownloader(
83 | _ downloader: ImageDownloader,
84 | didDownload image: Image,
85 | for url: URL,
86 | with response: URLResponse?)
87 |
88 | /// Checks if a received HTTP status code is valid or not.
89 | /// By default, a status code in range 200..<400 is considered as valid.
90 | /// If an invalid code is received, the downloader will raise an `KingfisherError` with
91 | /// `ResponseErrorReason.invalidHTTPStatusCode` as its reason.
92 | ///
93 | /// - Parameters:
94 | /// - code: The received HTTP status code.
95 | /// - downloader: The `ImageDownloader` object asks for validate status code.
96 | /// - Returns: Returns a value to indicate whether this HTTP status code is valid or not.
97 | /// - Note: If the default 200 to 400 valid code does not suit your need,
98 | /// you can implement this method to change that behavior.
99 | func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool
100 | }
101 |
102 | // Default implementation for `ImageDownloaderDelegate`.
103 | extension ImageDownloaderDelegate {
104 | public func imageDownloader(
105 | _ downloader: ImageDownloader,
106 | willDownloadImageForURL url: URL,
107 | with request: URLRequest?) {}
108 |
109 | public func imageDownloader(
110 | _ downloader: ImageDownloader,
111 | didFinishDownloadingImageForURL url: URL,
112 | with response: URLResponse?,
113 | error: Error?) {}
114 |
115 | public func imageDownloader(
116 | _ downloader: ImageDownloader,
117 | didDownload image: Image,
118 | for url: URL,
119 | with response: URLResponse?) {}
120 |
121 | public func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool {
122 | return (200..<400).contains(code)
123 | }
124 | public func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? {
125 | return data
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Networking/ImageModifier.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageModifier.swift
3 | // Kingfisher
4 | //
5 | // Created by Ethan Gill on 2017/11/28.
6 | //
7 | // Copyright (c) 2019 Ethan Gill
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// An `ImageModifier` can be used to change properties on an image in between
30 | /// cache serialization and use of the image. The modified returned image will be
31 | /// only used for current rendering purpose, the serialization data will not contain
32 | /// the changes applied by the `ImageModifier`.
33 | public protocol ImageModifier {
34 | /// Modify an input `Image`.
35 | ///
36 | /// - parameter image: Image which will be modified by `self`
37 | ///
38 | /// - returns: The modified image.
39 | ///
40 | /// - Note: The return value will be unmodified if modifying is not possible on
41 | /// the current platform.
42 | /// - Note: Most modifiers support UIImage or NSImage, but not CGImage.
43 | func modify(_ image: Image) -> Image
44 | }
45 |
46 | /// A wrapper for creating an `ImageModifier` easier.
47 | /// This type conforms to `ImageModifier` and wraps an image modify block.
48 | /// If the `block` throws an error, the original image will be used.
49 | public struct AnyImageModifier: ImageModifier {
50 |
51 | /// A block which modifies images, or returns the original image
52 | /// if modification cannot be performed with an error.
53 | let block: (Image) throws -> Image
54 |
55 | /// Creates an `AnyImageModifier` with a given `modify` block.
56 | public init(modify: @escaping (Image) throws -> Image) {
57 | block = modify
58 | }
59 |
60 | /// Modify an input `Image`. See `ImageModifier` protocol for more.
61 | public func modify(_ image: Image) -> Image {
62 | return (try? block(image)) ?? image
63 | }
64 | }
65 |
66 | #if os(iOS) || os(tvOS) || os(watchOS)
67 | import UIKit
68 |
69 | /// Modifier for setting the rendering mode of images.
70 | public struct RenderingModeImageModifier: ImageModifier {
71 |
72 | /// The rendering mode to apply to the image.
73 | public let renderingMode: UIImage.RenderingMode
74 |
75 | /// Creates a `RenderingModeImageModifier`.
76 | ///
77 | /// - Parameter renderingMode: The rendering mode to apply to the image. Default is `.automatic`.
78 | public init(renderingMode: UIImage.RenderingMode = .automatic) {
79 | self.renderingMode = renderingMode
80 | }
81 |
82 | /// Modify an input `Image`. See `ImageModifier` protocol for more.
83 | public func modify(_ image: Image) -> Image {
84 | return image.withRenderingMode(renderingMode)
85 | }
86 | }
87 |
88 | /// Modifier for setting the `flipsForRightToLeftLayoutDirection` property of images.
89 | public struct FlipsForRightToLeftLayoutDirectionImageModifier: ImageModifier {
90 |
91 | /// Creates a `FlipsForRightToLeftLayoutDirectionImageModifier`.
92 | public init() {}
93 |
94 | /// Modify an input `Image`. See `ImageModifier` protocol for more.
95 | public func modify(_ image: Image) -> Image {
96 | return image.imageFlippedForRightToLeftLayoutDirection()
97 | }
98 | }
99 |
100 | /// Modifier for setting the `alignmentRectInsets` property of images.
101 | public struct AlignmentRectInsetsImageModifier: ImageModifier {
102 |
103 | /// The alignment insets to apply to the image
104 | public let alignmentInsets: UIEdgeInsets
105 |
106 | /// Creates an `AlignmentRectInsetsImageModifier`.
107 | public init(alignmentInsets: UIEdgeInsets) {
108 | self.alignmentInsets = alignmentInsets
109 | }
110 |
111 | /// Modify an input `Image`. See `ImageModifier` protocol for more.
112 | public func modify(_ image: Image) -> Image {
113 | return image.withAlignmentRectInsets(alignmentInsets)
114 | }
115 | }
116 | #endif
117 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Networking/RedirectHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RedirectHandler.swift
3 | // Kingfisher
4 | //
5 | // Created by Roman Maidanovych on 2018/12/10.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Represents and wraps a method for modifying request during an image download request redirection.
30 | public protocol ImageDownloadRedirectHandler {
31 |
32 | /// The `ImageDownloadRedirectHandler` contained will be used to change the request before redirection.
33 | /// This is the posibility you can modify the image download request during redirection. You can modify the
34 | /// request for some customizing purpose, such as adding auth token to the header, do basic HTTP auth or
35 | /// something like url mapping.
36 | ///
37 | /// Usually, you pass an `ImageDownloadRedirectHandler` as the associated value of
38 | /// `KingfisherOptionsInfoItem.redirectHandler` and use it as the `options` parameter in related methods.
39 | ///
40 | /// If you do nothing with the input `request` and return it as is, a downloading process will redirect with it.
41 | ///
42 | /// - Parameters:
43 | /// - task: The current `SessionDataTask` which triggers this redirect.
44 | /// - response: The response received during redirection.
45 | /// - newRequest: The request for redirection which can be modified.
46 | /// - completionHandler: A closure for being called with modified request.
47 | func handleHTTPRedirection(
48 | for task: SessionDataTask,
49 | response: HTTPURLResponse,
50 | newRequest: URLRequest,
51 | completionHandler: @escaping (URLRequest?) -> Void)
52 | }
53 |
54 | /// A wrapper for creating an `ImageDownloadRedirectHandler` easier.
55 | /// This type conforms to `ImageDownloadRedirectHandler` and wraps an redirect request modify block.
56 | public struct AnyRedirectHandler: ImageDownloadRedirectHandler {
57 |
58 | let block: (SessionDataTask, HTTPURLResponse, URLRequest, (URLRequest?) -> Void) -> Void
59 |
60 | public func handleHTTPRedirection(
61 | for task: SessionDataTask,
62 | response: HTTPURLResponse,
63 | newRequest: URLRequest,
64 | completionHandler: @escaping (URLRequest?) -> Void)
65 | {
66 | block(task, response, newRequest, completionHandler)
67 | }
68 |
69 | /// Creates a value of `ImageDownloadRedirectHandler` which runs `modify` block.
70 | ///
71 | /// - Parameter modify: The request modifying block runs when a request modifying task comes.
72 | ///
73 | public init(handle: @escaping (SessionDataTask, HTTPURLResponse, URLRequest, (URLRequest?) -> Void) -> Void) {
74 | block = handle
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Networking/RequestModifier.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RequestModifier.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2016/09/05.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Represents and wraps a method for modifying request before an image download request starts.
30 | public protocol ImageDownloadRequestModifier {
31 |
32 | /// A method will be called just before the `request` being sent.
33 | /// This is the last chance you can modify the image download request. You can modify the request for some
34 | /// customizing purpose, such as adding auth token to the header, do basic HTTP auth or something like url mapping.
35 | ///
36 | /// Usually, you pass an `ImageDownloadRequestModifier` as the associated value of
37 | /// `KingfisherOptionsInfoItem.requestModifier` and use it as the `options` parameter in related methods.
38 | ///
39 | /// If you do nothing with the input `request` and return it as is, a downloading process will start with it.
40 | ///
41 | /// - Parameter request: The input request contains necessary information like `url`. This request is generated
42 | /// according to your resource url as a GET request.
43 | /// - Returns: A modified version of request, which you wish to use for downloading an image. If `nil` returned,
44 | /// a `KingfisherError.requestError` with `.emptyRequest` as its reason will occur.
45 | ///
46 | func modified(for request: URLRequest) -> URLRequest?
47 | }
48 |
49 | /// A wrapper for creating an `ImageDownloadRequestModifier` easier.
50 | /// This type conforms to `ImageDownloadRequestModifier` and wraps an image modify block.
51 | public struct AnyModifier: ImageDownloadRequestModifier {
52 |
53 | let block: (URLRequest) -> URLRequest?
54 |
55 | /// For `ImageDownloadRequestModifier` conformation.
56 | public func modified(for request: URLRequest) -> URLRequest? {
57 | return block(request)
58 | }
59 |
60 | /// Creates a value of `ImageDownloadRequestModifier` which runs `modify` block.
61 | ///
62 | /// - Parameter modify: The request modifying block runs when a request modifying task comes.
63 | /// The return `URLRequest?` value of this block will be used as the image download request.
64 | /// If `nil` returned, a `KingfisherError.requestError` with `.emptyRequest` as its
65 | /// reason will occur.
66 | public init(modify: @escaping (URLRequest) -> URLRequest?) {
67 | block = modify
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Networking/SessionDataTask.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionDataTask.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2018/11/1.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Represents a session data task in `ImageDownloader`. It consists of an underlying `URLSessionDataTask` and
30 | /// an array of `TaskCallback`. Multiple `TaskCallback`s could be added for a single downloading data task.
31 | public class SessionDataTask {
32 |
33 | /// Represents the type of token which used for cancelling a task.
34 | public typealias CancelToken = Int
35 |
36 | struct TaskCallback {
37 | let onCompleted: Delegate, Void>?
38 | let options: KingfisherParsedOptionsInfo
39 | }
40 |
41 | /// Downloaded raw data of current task.
42 | public private(set) var mutableData: Data
43 |
44 | /// The underlying download task. It is only for debugging purpose when you encountered an error. You should not
45 | /// modify the content of this task or start it yourself.
46 | public let task: URLSessionDataTask
47 | private var callbacksStore = [CancelToken: TaskCallback]()
48 |
49 | var callbacks: [SessionDataTask.TaskCallback] {
50 | lock.lock()
51 | defer { lock.unlock() }
52 | return Array(callbacksStore.values)
53 | }
54 |
55 | private var currentToken = 0
56 | private let lock = NSLock()
57 |
58 | let onTaskDone = Delegate<(Result<(Data, URLResponse?), KingfisherError>, [TaskCallback]), Void>()
59 | let onCallbackCancelled = Delegate<(CancelToken, TaskCallback), Void>()
60 |
61 | var started = false
62 | var containsCallbacks: Bool {
63 | // We should be able to use `task.state != .running` to check it.
64 | // However, in some rare cases, cancelling the task does not change
65 | // task state to `.cancelling` immediately, but still in `.running`.
66 | // So we need to check callbacks count to for sure that it is safe to remove the
67 | // task in delegate.
68 | return !callbacks.isEmpty
69 | }
70 |
71 | init(task: URLSessionDataTask) {
72 | self.task = task
73 | mutableData = Data()
74 | }
75 |
76 | func addCallback(_ callback: TaskCallback) -> CancelToken {
77 | lock.lock()
78 | defer { lock.unlock() }
79 | callbacksStore[currentToken] = callback
80 | defer { currentToken += 1 }
81 | return currentToken
82 | }
83 |
84 | func removeCallback(_ token: CancelToken) -> TaskCallback? {
85 | lock.lock()
86 | defer { lock.unlock() }
87 | if let callback = callbacksStore[token] {
88 | callbacksStore[token] = nil
89 | return callback
90 | }
91 | return nil
92 | }
93 |
94 | func resume() {
95 | guard !started else { return }
96 | started = true
97 | task.resume()
98 | }
99 |
100 | func cancel(token: CancelToken) {
101 | guard let callback = removeCallback(token) else {
102 | return
103 | }
104 | if callbacksStore.count == 0 {
105 | task.cancel()
106 | }
107 | onCallbackCancelled.call((token, callback))
108 | }
109 |
110 | func forceCancel() {
111 | for token in callbacksStore.keys {
112 | cancel(token: token)
113 | }
114 | }
115 |
116 | func didReceiveData(_ data: Data) {
117 | mutableData.append(data)
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Networking/SessionDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionDelegate.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2018/11/1.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | // Represents the delegate object of downloader session. It also behave like a task manager for downloading.
30 | class SessionDelegate: NSObject {
31 |
32 | typealias SessionChallengeFunc = (
33 | URLSession,
34 | URLAuthenticationChallenge,
35 | (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
36 | )
37 |
38 | typealias SessionTaskChallengeFunc = (
39 | URLSession,
40 | URLSessionTask,
41 | URLAuthenticationChallenge,
42 | (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
43 | )
44 |
45 | private var tasks: [URL: SessionDataTask] = [:]
46 | private let lock = NSLock()
47 |
48 | let onValidStatusCode = Delegate()
49 | let onDownloadingFinished = Delegate<(URL, Result), Void>()
50 | let onDidDownloadData = Delegate()
51 |
52 | let onReceiveSessionChallenge = Delegate()
53 | let onReceiveSessionTaskChallenge = Delegate()
54 |
55 | func add(
56 | _ dataTask: URLSessionDataTask,
57 | url: URL,
58 | callback: SessionDataTask.TaskCallback) -> DownloadTask
59 | {
60 | lock.lock()
61 | defer { lock.unlock() }
62 |
63 | // Create a new task if necessary.
64 | let task = SessionDataTask(task: dataTask)
65 | task.onCallbackCancelled.delegate(on: self) { [unowned task] (self, value) in
66 | let (token, callback) = value
67 |
68 | let error = KingfisherError.requestError(reason: .taskCancelled(task: task, token: token))
69 | task.onTaskDone.call((.failure(error), [callback]))
70 | // No other callbacks waiting, we can clear the task now.
71 | if !task.containsCallbacks {
72 | let dataTask = task.task
73 | self.remove(dataTask)
74 | }
75 | }
76 | let token = task.addCallback(callback)
77 | tasks[url] = task
78 | return DownloadTask(sessionTask: task, cancelToken: token)
79 | }
80 |
81 | func append(
82 | _ task: SessionDataTask,
83 | url: URL,
84 | callback: SessionDataTask.TaskCallback) -> DownloadTask
85 | {
86 | let token = task.addCallback(callback)
87 | return DownloadTask(sessionTask: task, cancelToken: token)
88 | }
89 |
90 | private func remove(_ task: URLSessionTask) {
91 | guard let url = task.originalRequest?.url else {
92 | return
93 | }
94 | lock.lock()
95 | defer {lock.unlock()}
96 | tasks[url] = nil
97 | }
98 |
99 | private func task(for task: URLSessionTask) -> SessionDataTask? {
100 |
101 | guard let url = task.originalRequest?.url else {
102 | return nil
103 | }
104 |
105 | lock.lock()
106 | defer { lock.unlock() }
107 | guard let sessionTask = tasks[url] else {
108 | return nil
109 | }
110 | guard sessionTask.task.taskIdentifier == task.taskIdentifier else {
111 | return nil
112 | }
113 | return sessionTask
114 | }
115 |
116 | func task(for url: URL) -> SessionDataTask? {
117 | lock.lock()
118 | defer { lock.unlock() }
119 | return tasks[url]
120 | }
121 |
122 | func cancelAll() {
123 | lock.lock()
124 | let taskValues = tasks.values
125 | lock.unlock()
126 | for task in taskValues {
127 | task.forceCancel()
128 | }
129 | }
130 |
131 | func cancel(url: URL) {
132 | lock.lock()
133 | let task = tasks[url]
134 | lock.unlock()
135 | task?.forceCancel()
136 | }
137 | }
138 |
139 | extension SessionDelegate: URLSessionDataDelegate {
140 |
141 | func urlSession(
142 | _ session: URLSession,
143 | dataTask: URLSessionDataTask,
144 | didReceive response: URLResponse,
145 | completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
146 | {
147 | guard let httpResponse = response as? HTTPURLResponse else {
148 | let error = KingfisherError.responseError(reason: .invalidURLResponse(response: response))
149 | onCompleted(task: dataTask, result: .failure(error))
150 | completionHandler(.cancel)
151 | return
152 | }
153 |
154 | let httpStatusCode = httpResponse.statusCode
155 | guard onValidStatusCode.call(httpStatusCode) == true else {
156 | let error = KingfisherError.responseError(reason: .invalidHTTPStatusCode(response: httpResponse))
157 | onCompleted(task: dataTask, result: .failure(error))
158 | completionHandler(.cancel)
159 | return
160 | }
161 | completionHandler(.allow)
162 | }
163 |
164 | func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
165 | guard let task = self.task(for: dataTask) else {
166 | return
167 | }
168 |
169 | task.didReceiveData(data)
170 |
171 | task.callbacks.forEach { callback in
172 | callback.options.onDataReceived?.forEach { sideEffect in
173 | sideEffect.onDataReceived(session, task: task, data: data)
174 | }
175 | }
176 | }
177 |
178 | func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
179 | guard let sessionTask = self.task(for: task) else { return }
180 |
181 | if let url = task.originalRequest?.url {
182 | let result: Result
183 | if let error = error {
184 | result = .failure(KingfisherError.responseError(reason: .URLSessionError(error: error)))
185 | } else if let response = task.response {
186 | result = .success(response)
187 | } else {
188 | result = .failure(KingfisherError.responseError(reason: .noURLResponse(task: sessionTask)))
189 | }
190 | onDownloadingFinished.call((url, result))
191 | }
192 |
193 | let result: Result<(Data, URLResponse?), KingfisherError>
194 | if let error = error {
195 | result = .failure(KingfisherError.responseError(reason: .URLSessionError(error: error)))
196 | } else {
197 | if let data = onDidDownloadData.call(sessionTask), let finalData = data {
198 | result = .success((finalData, task.response))
199 | } else {
200 | result = .failure(KingfisherError.responseError(reason: .dataModifyingFailed(task: sessionTask)))
201 | }
202 | }
203 | onCompleted(task: task, result: result)
204 | }
205 |
206 | func urlSession(
207 | _ session: URLSession,
208 | didReceive challenge: URLAuthenticationChallenge,
209 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
210 | {
211 | onReceiveSessionChallenge.call((session, challenge, completionHandler))
212 | }
213 |
214 | func urlSession(
215 | _ session: URLSession,
216 | task: URLSessionTask,
217 | didReceive challenge: URLAuthenticationChallenge,
218 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
219 | {
220 | onReceiveSessionTaskChallenge.call((session, task, challenge, completionHandler))
221 | }
222 |
223 | func urlSession(
224 | _ session: URLSession,
225 | task: URLSessionTask,
226 | willPerformHTTPRedirection response: HTTPURLResponse,
227 | newRequest request: URLRequest,
228 | completionHandler: @escaping (URLRequest?) -> Void)
229 | {
230 | guard let sessionDataTask = self.task(for: task),
231 | let redirectHandler = Array(sessionDataTask.callbacks).last?.options.redirectHandler else
232 | {
233 | completionHandler(request)
234 | return
235 | }
236 |
237 | redirectHandler.handleHTTPRedirection(
238 | for: sessionDataTask,
239 | response: response,
240 | newRequest: request,
241 | completionHandler: completionHandler)
242 | }
243 |
244 | private func onCompleted(task: URLSessionTask, result: Result<(Data, URLResponse?), KingfisherError>) {
245 | guard let sessionTask = self.task(for: task) else {
246 | return
247 | }
248 | remove(task)
249 | sessionTask.onTaskDone.call((result, sessionTask.callbacks))
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Utility/Box.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Box.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2018/3/17.
6 | // Copyright (c) 2019 Wei Wang
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | import Foundation
27 |
28 | class Box {
29 | var value: T
30 |
31 | init(_ value: T) {
32 | self.value = value
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Utility/CallbackQueue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CallbackQueue.swift
3 | // Kingfisher
4 | //
5 | // Created by onevcat on 2018/10/15.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// Represents callback queue behaviors when an calling of closure be dispatched.
30 | ///
31 | /// - asyncMain: Dispatch the calling to `DispatchQueue.main` with an `async` behavior.
32 | /// - currentMainOrAsync: Dispatch the calling to `DispatchQueue.main` with an `async` behavior if current queue is not
33 | /// `.main`. Otherwise, call the closure immediately in current main queue.
34 | /// - untouch: Do not change the calling queue for closure.
35 | /// - dispatch: Dispatches to a specified `DispatchQueue`.
36 | public enum CallbackQueue {
37 | /// Dispatch the calling to `DispatchQueue.main` with an `async` behavior.
38 | case mainAsync
39 | /// Dispatch the calling to `DispatchQueue.main` with an `async` behavior if current queue is not
40 | /// `.main`. Otherwise, call the closure immediately in current main queue.
41 | case mainCurrentOrAsync
42 | /// Do not change the calling queue for closure.
43 | case untouch
44 | /// Dispatches to a specified `DispatchQueue`.
45 | case dispatch(DispatchQueue)
46 |
47 | public func execute(_ block: @escaping () -> Void) {
48 | switch self {
49 | case .mainAsync:
50 | DispatchQueue.main.async { block() }
51 | case .mainCurrentOrAsync:
52 | DispatchQueue.main.safeAsync { block() }
53 | case .untouch:
54 | block()
55 | case .dispatch(let queue):
56 | queue.async { block() }
57 | }
58 | }
59 |
60 | var queue: DispatchQueue {
61 | switch self {
62 | case .mainAsync: return .main
63 | case .mainCurrentOrAsync: return .main
64 | case .untouch: return OperationQueue.current?.underlyingQueue ?? .main
65 | case .dispatch(let queue): return queue
66 | }
67 | }
68 | }
69 |
70 | extension DispatchQueue {
71 | // This method will dispatch the `block` to self.
72 | // If `self` is the main queue, and current thread is main thread, the block
73 | // will be invoked immediately instead of being dispatched.
74 | func safeAsync(_ block: @escaping ()->()) {
75 | if self === DispatchQueue.main && Thread.isMainThread {
76 | block()
77 | } else {
78 | async { block() }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Utility/Delegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Delegate.swift
3 | // Kingfisher
4 | //
5 | // Created by onevcat on 2018/10/10.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | /// A delegate helper type to "shadow" weak `self`, to prevent creating an unexpected retain cycle.
30 | class Delegate {
31 | init() {}
32 |
33 | private var block: ((Input) -> Output?)?
34 |
35 | func delegate(on target: T, block: ((T, Input) -> Output)?) {
36 | // The `target` is weak inside block, so you do not need to worry about it in the caller side.
37 | self.block = { [weak target] input in
38 | guard let target = target else { return nil }
39 | return block?(target, input)
40 | }
41 | }
42 |
43 | func call(_ input: Input) -> Output? {
44 | return block?(input)
45 | }
46 | }
47 |
48 | extension Delegate where Input == Void {
49 | // To make syntax better for `Void` input.
50 | func call() -> Output? {
51 | return call(())
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Utility/ExtensionHelpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExtensionHelpers.swift
3 | // Kingfisher
4 | //
5 | // Created by onevcat on 2018/09/28.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | extension Float {
30 | var isEven: Bool {
31 | return truncatingRemainder(dividingBy: 2.0) == 0
32 | }
33 | }
34 |
35 | #if canImport(AppKit)
36 | import AppKit
37 | extension NSBezierPath {
38 | convenience init(roundedRect rect: NSRect, topLeftRadius: CGFloat, topRightRadius: CGFloat,
39 | bottomLeftRadius: CGFloat, bottomRightRadius: CGFloat)
40 | {
41 | self.init()
42 |
43 | let maxCorner = min(rect.width, rect.height) / 2
44 |
45 | let radiusTopLeft = min(maxCorner, max(0, topLeftRadius))
46 | let radiusTopRight = min(maxCorner, max(0, topRightRadius))
47 | let radiusBottomLeft = min(maxCorner, max(0, bottomLeftRadius))
48 | let radiusBottomRight = min(maxCorner, max(0, bottomRightRadius))
49 |
50 | guard !rect.isEmpty else {
51 | return
52 | }
53 |
54 | let topLeft = NSPoint(x: rect.minX, y: rect.maxY)
55 | let topRight = NSPoint(x: rect.maxX, y: rect.maxY)
56 | let bottomRight = NSPoint(x: rect.maxX, y: rect.minY)
57 |
58 | move(to: NSPoint(x: rect.midX, y: rect.maxY))
59 | appendArc(from: topLeft, to: rect.origin, radius: radiusTopLeft)
60 | appendArc(from: rect.origin, to: bottomRight, radius: radiusBottomLeft)
61 | appendArc(from: bottomRight, to: topRight, radius: radiusBottomRight)
62 | appendArc(from: topRight, to: topLeft, radius: radiusTopRight)
63 | close()
64 | }
65 |
66 | convenience init(roundedRect rect: NSRect, byRoundingCorners corners: RectCorner, radius: CGFloat) {
67 | let radiusTopLeft = corners.contains(.topLeft) ? radius : 0
68 | let radiusTopRight = corners.contains(.topRight) ? radius : 0
69 | let radiusBottomLeft = corners.contains(.bottomLeft) ? radius : 0
70 | let radiusBottomRight = corners.contains(.bottomRight) ? radius : 0
71 |
72 | self.init(roundedRect: rect, topLeftRadius: radiusTopLeft, topRightRadius: radiusTopRight,
73 | bottomLeftRadius: radiusBottomLeft, bottomRightRadius: radiusBottomRight)
74 | }
75 | }
76 |
77 | extension Image {
78 | // macOS does not support scale. This is just for code compatibility across platforms.
79 | convenience init?(data: Data, scale: CGFloat) {
80 | self.init(data: data)
81 | }
82 | }
83 | #endif
84 |
85 | #if canImport(UIKit)
86 | import UIKit
87 | extension RectCorner {
88 | var uiRectCorner: UIRectCorner {
89 |
90 | var result: UIRectCorner = []
91 |
92 | if contains(.topLeft) { result.insert(.topLeft) }
93 | if contains(.topRight) { result.insert(.topRight) }
94 | if contains(.bottomLeft) { result.insert(.bottomLeft) }
95 | if contains(.bottomRight) { result.insert(.bottomRight) }
96 |
97 | return result
98 | }
99 | }
100 | #endif
101 |
102 | extension Date {
103 | var isPast: Bool {
104 | return isPast(referenceDate: Date())
105 | }
106 |
107 | var isFuture: Bool {
108 | return !isPast
109 | }
110 |
111 | func isPast(referenceDate: Date) -> Bool {
112 | return timeIntervalSince(referenceDate) <= 0
113 | }
114 |
115 | func isFuture(referenceDate: Date) -> Bool {
116 | return !isPast(referenceDate: referenceDate)
117 | }
118 |
119 | // `Date` in memory is a wrap for `TimeInterval`. But in file attribute it can only accept `Int` number.
120 | // By default the system will `round` it. But it is not friendly for testing purpose.
121 | // So we always `ceil` the value when used for file attributes.
122 | var fileAttributeDate: Date {
123 | return Date(timeIntervalSince1970: ceil(timeIntervalSince1970))
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Utility/Result.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Result.swift
3 | // Kingfisher
4 | //
5 | // Created by onevcat on 2018/09/22.
6 | //
7 | // Copyright (c) 2019 Wei Wang
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | #if swift(>=4.3)
30 | /// Result type already built-in
31 | #else
32 | /// A value that represents either a success or failure, capturing associated
33 | /// values in both cases.
34 | public enum Result {
35 | /// A success, storing a `Value`.
36 | case success(Success)
37 |
38 | /// A failure, storing an `Error`.
39 | case failure(Failure)
40 |
41 | /// Evaluates the given transform closure when this `Result` instance is
42 | /// `.success`, passing the value as a parameter.
43 | ///
44 | /// Use the `map` method with a closure that returns a non-`Result` value.
45 | ///
46 | /// - Parameter transform: A closure that takes the successful value of the
47 | /// instance.
48 | /// - Returns: A new `Result` instance with the result of the transform, if
49 | /// it was applied.
50 | public func map(
51 | _ transform: (Success) -> NewSuccess
52 | ) -> Result {
53 | switch self {
54 | case let .success(success):
55 | return .success(transform(success))
56 | case let .failure(failure):
57 | return .failure(failure)
58 | }
59 | }
60 |
61 | /// Evaluates the given transform closure when this `Result` instance is
62 | /// `.failure`, passing the error as a parameter.
63 | ///
64 | /// Use the `mapError` method with a closure that returns a non-`Result`
65 | /// value.
66 | ///
67 | /// - Parameter transform: A closure that takes the failure value of the
68 | /// instance.
69 | /// - Returns: A new `Result` instance with the result of the transform, if
70 | /// it was applied.
71 | public func mapError(
72 | _ transform: (Failure) -> NewFailure
73 | ) -> Result {
74 | switch self {
75 | case let .success(success):
76 | return .success(success)
77 | case let .failure(failure):
78 | return .failure(transform(failure))
79 | }
80 | }
81 |
82 | /// Evaluates the given transform closure when this `Result` instance is
83 | /// `.success`, passing the value as a parameter and flattening the result.
84 | ///
85 | /// - Parameter transform: A closure that takes the successful value of the
86 | /// instance.
87 | /// - Returns: A new `Result` instance, either from the transform or from
88 | /// the previous error value.
89 | public func flatMap(
90 | _ transform: (Success) -> Result
91 | ) -> Result {
92 | switch self {
93 | case let .success(success):
94 | return transform(success)
95 | case let .failure(failure):
96 | return .failure(failure)
97 | }
98 | }
99 |
100 | /// Evaluates the given transform closure when this `Result` instance is
101 | /// `.failure`, passing the error as a parameter and flattening the result.
102 | ///
103 | /// - Parameter transform: A closure that takes the error value of the
104 | /// instance.
105 | /// - Returns: A new `Result` instance, either from the transform or from
106 | /// the previous success value.
107 | public func flatMapError(
108 | _ transform: (Failure) -> Result
109 | ) -> Result {
110 | switch self {
111 | case let .success(success):
112 | return .success(success)
113 | case let .failure(failure):
114 | return transform(failure)
115 | }
116 | }
117 | }
118 |
119 | extension Result where Failure: Error {
120 | /// Returns the success value as a throwing expression.
121 | ///
122 | /// Use this method to retrieve the value of this result if it represents a
123 | /// success, or to catch the value if it represents a failure.
124 | ///
125 | /// let integerResult: Result = .success(5)
126 | /// do {
127 | /// let value = try integerResult.get()
128 | /// print("The value is \(value).")
129 | /// } catch error {
130 | /// print("Error retrieving the value: \(error)")
131 | /// }
132 | /// // Prints "The value is 5."
133 | ///
134 | /// - Returns: The success value, if the instance represents a success.
135 | /// - Throws: The failure value, if the instance represents a failure.
136 | public func get() throws -> Success {
137 | switch self {
138 | case let .success(success):
139 | return success
140 | case let .failure(failure):
141 | throw failure
142 | }
143 | }
144 |
145 | /// Unwraps the `Result` into a throwing expression.
146 | ///
147 | /// - Returns: The success value, if the instance is a success.
148 | /// - Throws: The error value, if the instance is a failure.
149 | @available(*, deprecated, message: "This method will be removed soon. Use `get() throws -> Success` instead.")
150 | public func unwrapped() throws -> Success {
151 | switch self {
152 | case let .success(value):
153 | return value
154 | case let .failure(error):
155 | throw error
156 | }
157 | }
158 | }
159 |
160 | extension Result where Failure == Swift.Error {
161 | /// Creates a new result by evaluating a throwing closure, capturing the
162 | /// returned value as a success, or any thrown error as a failure.
163 | ///
164 | /// - Parameter body: A throwing closure to evaluate.
165 | @_transparent
166 | public init(catching body: () throws -> Success) {
167 | do {
168 | self = .success(try body())
169 | } catch {
170 | self = .failure(error)
171 | }
172 | }
173 | }
174 |
175 | extension Result : Equatable where Success : Equatable, Failure: Equatable { }
176 |
177 | extension Result : Hashable where Success : Hashable, Failure : Hashable { }
178 |
179 | extension Result : CustomDebugStringConvertible {
180 | public var debugDescription: String {
181 | var output = "Result."
182 | switch self {
183 | case let .success(value):
184 | output += "success("
185 | debugPrint(value, terminator: "", to: &output)
186 | case let .failure(error):
187 | output += "failure("
188 | debugPrint(error, terminator: "", to: &output)
189 | }
190 | output += ")"
191 |
192 | return output
193 | }
194 | }
195 | #endif
196 |
197 | // Deprecated
198 | extension Result {
199 |
200 | /// The stored value of a successful `Result`. `nil` if the `Result` was a failure.
201 | @available(*, deprecated, message: "This method will be removed soon. Use `get() throws -> Success` instead.")
202 | public var value: Success? {
203 | switch self {
204 | case let .success(value):
205 | return value
206 | case .failure:
207 | return nil
208 | }
209 | }
210 |
211 | /// The stored value of a failure `Result`. `nil` if the `Result` was a success.
212 | @available(*, deprecated, message: "This method will be removed soon. Use `get() throws -> Success` instead.")
213 | public var error: Failure? {
214 | switch self {
215 | case let .failure(error):
216 | return error
217 | case .success:
218 | return nil
219 | }
220 | }
221 |
222 | /// A Boolean value indicating whether the `Result` as a success.
223 | @available(*, deprecated, message: "This method will be removed soon. Use methods defined in `Swift.Result`.")
224 | public var isSuccess: Bool {
225 | switch self {
226 | case .success:
227 | return true
228 | case .failure:
229 | return false
230 | }
231 | }
232 | }
233 |
234 | // These helper methods are not public since we do not want them to be exposed or cause any conflicting.
235 | // However, they are just wrapper of `ResultUtil` static methods.
236 | extension Result where Failure: Error {
237 |
238 | /// Evaluates the given transform closures to create a single output value.
239 | ///
240 | /// - Parameters:
241 | /// - onSuccess: A closure that transforms the success value.
242 | /// - onFailure: A closure that transforms the error value.
243 | /// - Returns: A single `Output` value.
244 | func match