├── .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( 245 | onSuccess: (Success) -> Output, 246 | onFailure: (Failure) -> Output) -> Output 247 | { 248 | switch self { 249 | case let .success(value): 250 | return onSuccess(value) 251 | case let .failure(error): 252 | return onFailure(error) 253 | } 254 | } 255 | 256 | func matchSuccess(with folder: (Success?) -> Output) -> Output { 257 | return match( 258 | onSuccess: { value in return folder(value) }, 259 | onFailure: { _ in return folder(nil) } 260 | ) 261 | } 262 | 263 | func matchFailure(with folder: (Error?) -> Output) -> Output { 264 | return match( 265 | onSuccess: { _ in return folder(nil) }, 266 | onFailure: { error in return folder(error) } 267 | ) 268 | } 269 | 270 | func match(with folder: (Success?, Error?) -> Output) -> Output { 271 | return match( 272 | onSuccess: { return folder($0, nil) }, 273 | onFailure: { return folder(nil, $0) } 274 | ) 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Utility/Runtime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Runtime.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2018/10/12. 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 | func getAssociatedObject(_ object: Any, _ key: UnsafeRawPointer) -> T? { 30 | return objc_getAssociatedObject(object, key) as? T 31 | } 32 | 33 | func setRetainedAssociatedObject(_ object: Any, _ key: UnsafeRawPointer, _ value: T) { 34 | objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 35 | } 36 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Utility/SizeExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SizeExtensions.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 CoreGraphics 28 | 29 | extension CGSize: KingfisherCompatibleValue {} 30 | extension KingfisherWrapper where Base == CGSize { 31 | 32 | /// Returns a size by resizing the `base` size to a target size under a given content mode. 33 | /// 34 | /// - Parameters: 35 | /// - size: The target size to resize to. 36 | /// - contentMode: Content mode of the target size should be when resizing. 37 | /// - Returns: The resized size under the given `ContentMode`. 38 | public func resize(to size: CGSize, for contentMode: ContentMode) -> CGSize { 39 | switch contentMode { 40 | case .aspectFit: 41 | return constrained(size) 42 | case .aspectFill: 43 | return filling(size) 44 | case .none: 45 | return size 46 | } 47 | } 48 | 49 | /// Returns a size by resizing the `base` size by making it aspect fitting the given `size`. 50 | /// 51 | /// - Parameter size: The size in which the `base` should fit in. 52 | /// - Returns: The size fitted in by the input `size`, while keeps `base` aspect. 53 | public func constrained(_ size: CGSize) -> CGSize { 54 | let aspectWidth = round(aspectRatio * size.height) 55 | let aspectHeight = round(size.width / aspectRatio) 56 | 57 | return aspectWidth > size.width ? 58 | CGSize(width: size.width, height: aspectHeight) : 59 | CGSize(width: aspectWidth, height: size.height) 60 | } 61 | 62 | /// Returns a size by resizing the `base` size by making it aspect filling the given `size`. 63 | /// 64 | /// - Parameter size: The size in which the `base` should fill. 65 | /// - Returns: The size be filled by the input `size`, while keeps `base` aspect. 66 | public func filling(_ size: CGSize) -> CGSize { 67 | let aspectWidth = round(aspectRatio * size.height) 68 | let aspectHeight = round(size.width / aspectRatio) 69 | 70 | return aspectWidth < size.width ? 71 | CGSize(width: size.width, height: aspectHeight) : 72 | CGSize(width: aspectWidth, height: size.height) 73 | } 74 | 75 | /// Returns a `CGRect` for which the `base` size is constrained to an input `size` at a given `anchor` point. 76 | /// 77 | /// - Parameters: 78 | /// - size: The size in which the `base` should be constrained to. 79 | /// - anchor: An anchor point in which the size constraint should happen. 80 | /// - Returns: The result `CGRect` for the constraint operation. 81 | public func constrainedRect(for size: CGSize, anchor: CGPoint) -> CGRect { 82 | 83 | let unifiedAnchor = CGPoint(x: anchor.x.clamped(to: 0.0...1.0), 84 | y: anchor.y.clamped(to: 0.0...1.0)) 85 | 86 | let x = unifiedAnchor.x * base.width - unifiedAnchor.x * size.width 87 | let y = unifiedAnchor.y * base.height - unifiedAnchor.y * size.height 88 | let r = CGRect(x: x, y: y, width: size.width, height: size.height) 89 | 90 | let ori = CGRect(origin: .zero, size: base) 91 | return ori.intersection(r) 92 | } 93 | 94 | private var aspectRatio: CGFloat { 95 | return base.height == 0.0 ? 1.0 : base.width / base.height 96 | } 97 | } 98 | 99 | extension CGRect { 100 | func scaled(_ scale: CGFloat) -> CGRect { 101 | return CGRect(x: origin.x * scale, y: origin.y * scale, 102 | width: size.width * scale, height: size.height * scale) 103 | } 104 | } 105 | 106 | extension Comparable { 107 | func clamped(to limits: ClosedRange) -> Self { 108 | return min(max(self, limits.lowerBound), limits.upperBound) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Utility/String+MD5.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+MD5.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 18/09/25. 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 CommonCrypto 29 | 30 | extension String: KingfisherCompatibleValue { } 31 | extension KingfisherWrapper where Base == String { 32 | var md5: String { 33 | guard let data = base.data(using: .utf8) else { 34 | return base 35 | } 36 | var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH)) 37 | #if swift(>=5.0) 38 | _ = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in 39 | return CC_MD5(bytes.baseAddress, CC_LONG(data.count), &digest) 40 | } 41 | #else 42 | _ = data.withUnsafeBytes { bytes in 43 | return CC_MD5(bytes, CC_LONG(data.count), &digest) 44 | } 45 | #endif 46 | 47 | return digest.reduce(into: "") { $0 += String(format: "%02x", $1) } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Views/Indicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Indicator.swift 3 | // Kingfisher 4 | // 5 | // Created by João D. Moreira on 30/08/16. 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 canImport(AppKit) 28 | import AppKit 29 | public typealias IndicatorView = NSView 30 | #else 31 | import UIKit 32 | public typealias IndicatorView = UIView 33 | #endif 34 | 35 | /// Represents the activity indicator type which should be added to 36 | /// an image view when an image is being downloaded. 37 | /// 38 | /// - none: No indicator. 39 | /// - activity: Uses the system activity indicator. 40 | /// - image: Uses an image as indicator. GIF is supported. 41 | /// - custom: Uses a custom indicator. The type of associated value should conform to the `Indicator` protocol. 42 | public enum IndicatorType { 43 | /// No indicator. 44 | case none 45 | /// Uses the system activity indicator. 46 | case activity 47 | /// Uses an image as indicator. GIF is supported. 48 | case image(imageData: Data) 49 | /// Uses a custom indicator. The type of associated value should conform to the `Indicator` protocol. 50 | case custom(indicator: Indicator) 51 | } 52 | 53 | /// An indicator type which can be used to show the download task is in progress. 54 | public protocol Indicator { 55 | 56 | /// Called when the indicator should start animating. 57 | func startAnimatingView() 58 | 59 | /// Called when the indicator should stop animating. 60 | func stopAnimatingView() 61 | 62 | /// Center offset of the indicator. Kingfisher will use this value to determine the position of 63 | /// indicator in the super view. 64 | var centerOffset: CGPoint { get } 65 | 66 | /// The indicator view which would be added to the super view. 67 | var view: IndicatorView { get } 68 | } 69 | 70 | extension Indicator { 71 | 72 | /// Default implementation of `centerOffset` of `Indicator`. The default value is `.zero`, means that there is 73 | /// no offset for the indicator view. 74 | public var centerOffset: CGPoint { return .zero } 75 | } 76 | 77 | // Displays a NSProgressIndicator / UIActivityIndicatorView 78 | final class ActivityIndicator: Indicator { 79 | 80 | #if os(macOS) 81 | private let activityIndicatorView: NSProgressIndicator 82 | #else 83 | private let activityIndicatorView: UIActivityIndicatorView 84 | #endif 85 | private var animatingCount = 0 86 | 87 | var view: IndicatorView { 88 | return activityIndicatorView 89 | } 90 | 91 | func startAnimatingView() { 92 | if animatingCount == 0 { 93 | #if os(macOS) 94 | activityIndicatorView.startAnimation(nil) 95 | #else 96 | activityIndicatorView.startAnimating() 97 | #endif 98 | activityIndicatorView.isHidden = false 99 | } 100 | animatingCount += 1 101 | } 102 | 103 | func stopAnimatingView() { 104 | animatingCount = max(animatingCount - 1, 0) 105 | if animatingCount == 0 { 106 | #if os(macOS) 107 | activityIndicatorView.stopAnimation(nil) 108 | #else 109 | activityIndicatorView.stopAnimating() 110 | #endif 111 | activityIndicatorView.isHidden = true 112 | } 113 | } 114 | 115 | init() { 116 | #if os(macOS) 117 | activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16)) 118 | activityIndicatorView.controlSize = .small 119 | activityIndicatorView.style = .spinning 120 | #else 121 | #if os(tvOS) 122 | let indicatorStyle = UIActivityIndicatorView.Style.white 123 | #else 124 | let indicatorStyle = UIActivityIndicatorView.Style.gray 125 | #endif 126 | #if swift(>=4.2) 127 | activityIndicatorView = UIActivityIndicatorView(style: indicatorStyle) 128 | #else 129 | activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: indicatorStyle) 130 | #endif 131 | #endif 132 | } 133 | } 134 | 135 | // MARK: - ImageIndicator 136 | // Displays an ImageView. Supports gif 137 | final class ImageIndicator: Indicator { 138 | private let animatedImageIndicatorView: ImageView 139 | 140 | var view: IndicatorView { 141 | return animatedImageIndicatorView 142 | } 143 | 144 | init?( 145 | imageData data: Data, 146 | processor: ImageProcessor = DefaultImageProcessor.default, 147 | options: KingfisherParsedOptionsInfo? = nil) 148 | { 149 | var options = options ?? KingfisherParsedOptionsInfo(nil) 150 | // Use normal image view to show animations, so we need to preload all animation data. 151 | if !options.preloadAllAnimationData { 152 | options.preloadAllAnimationData = true 153 | } 154 | 155 | guard let image = processor.process(item: .data(data), options: options) else { 156 | return nil 157 | } 158 | 159 | animatedImageIndicatorView = ImageView() 160 | animatedImageIndicatorView.image = image 161 | 162 | #if os(macOS) 163 | // Need for gif to animate on macOS 164 | animatedImageIndicatorView.imageScaling = .scaleNone 165 | animatedImageIndicatorView.canDrawSubviewsIntoLayer = true 166 | #else 167 | animatedImageIndicatorView.contentMode = .center 168 | #endif 169 | } 170 | 171 | func startAnimatingView() { 172 | #if os(macOS) 173 | animatedImageIndicatorView.animates = true 174 | #else 175 | animatedImageIndicatorView.startAnimating() 176 | #endif 177 | animatedImageIndicatorView.isHidden = false 178 | } 179 | 180 | func stopAnimatingView() { 181 | #if os(macOS) 182 | animatedImageIndicatorView.animates = false 183 | #else 184 | animatedImageIndicatorView.stopAnimating() 185 | #endif 186 | animatedImageIndicatorView.isHidden = true 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/MessengerBubbles.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MessengerBubbles", 3 | "version": "0.1.0", 4 | "summary": "Create Image views for user or group like Messenger", 5 | "homepage": "https://github.com/RpX974/MessengerBubbles", 6 | "license": { 7 | "type": "MIT", 8 | "file": "LICENSE" 9 | }, 10 | "authors": { 11 | "Laurent Grondin": "laurent.grondin@epitech.eu" 12 | }, 13 | "source": { 14 | "git": "https://github.com/RpX974/MessengerBubbles.git", 15 | "tag": "0.1.0" 16 | }, 17 | "platforms": { 18 | "ios": "10.0" 19 | }, 20 | "source_files": "MessengerBubbles/Classes/**/*", 21 | "frameworks": "Foundation", 22 | "dependencies": { 23 | "Kingfisher": [ 24 | "~> 5.0" 25 | ] 26 | }, 27 | "swift_versions": "5.0", 28 | "swift_version": "5.0" 29 | } 30 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.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/Target Support Files/Kingfisher/Kingfisher-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 | FMWK 17 | CFBundleShortVersionString 18 | 5.6.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Kingfisher : NSObject 3 | @end 4 | @implementation PodsDummy_Kingfisher 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "Kingfisher.h" 14 | 15 | FOUNDATION_EXPORT double KingfisherVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap: -------------------------------------------------------------------------------- 1 | framework module Kingfisher { 2 | umbrella header "Kingfisher-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CFNetwork" 4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/MessengerBubbles/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 | FMWK 17 | CFBundleShortVersionString 18 | 0.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/MessengerBubbles/MessengerBubbles-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_MessengerBubbles : NSObject 3 | @end 4 | @implementation PodsDummy_MessengerBubbles 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/MessengerBubbles/MessengerBubbles-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/MessengerBubbles/MessengerBubbles-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double MessengerBubblesVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char MessengerBubblesVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/MessengerBubbles/MessengerBubbles.modulemap: -------------------------------------------------------------------------------- 1 | framework module MessengerBubbles { 2 | umbrella header "MessengerBubbles-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/MessengerBubbles/MessengerBubbles.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MessengerBubbles 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" 5 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Example/Pods-MessengerBubbles_Example-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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Example/Pods-MessengerBubbles_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Kingfisher 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2018 Wei Wang 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | 30 | ## MessengerBubbles 31 | 32 | Copyright (c) 2019 Laurent Grondin 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in 42 | all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 50 | THE SOFTWARE. 51 | 52 | Generated by CocoaPods - https://cocoapods.org 53 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Example/Pods-MessengerBubbles_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2018 Wei Wang 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | 40 | License 41 | MIT 42 | Title 43 | Kingfisher 44 | Type 45 | PSGroupSpecifier 46 | 47 | 48 | FooterText 49 | Copyright (c) 2019 Laurent Grondin <laurent.grondin@epitech.eu> 50 | 51 | Permission is hereby granted, free of charge, to any person obtaining a copy 52 | of this software and associated documentation files (the "Software"), to deal 53 | in the Software without restriction, including without limitation the rights 54 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 55 | copies of the Software, and to permit persons to whom the Software is 56 | furnished to do so, subject to the following conditions: 57 | 58 | The above copyright notice and this permission notice shall be included in 59 | all copies or substantial portions of the Software. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 63 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 64 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 65 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 67 | THE SOFTWARE. 68 | 69 | License 70 | MIT 71 | Title 72 | MessengerBubbles 73 | Type 74 | PSGroupSpecifier 75 | 76 | 77 | FooterText 78 | Generated by CocoaPods - https://cocoapods.org 79 | Title 80 | 81 | Type 82 | PSGroupSpecifier 83 | 84 | 85 | StringsTable 86 | Acknowledgements 87 | Title 88 | Acknowledgements 89 | 90 | 91 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Example/Pods-MessengerBubbles_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_MessengerBubbles_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_MessengerBubbles_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Example/Pods-MessengerBubbles_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework" 165 | install_framework "${BUILT_PRODUCTS_DIR}/MessengerBubbles/MessengerBubbles.framework" 166 | fi 167 | if [[ "$CONFIGURATION" == "Release" ]]; then 168 | install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework" 169 | install_framework "${BUILT_PRODUCTS_DIR}/MessengerBubbles/MessengerBubbles.framework" 170 | fi 171 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 172 | wait 173 | fi 174 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Example/Pods-MessengerBubbles_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_MessengerBubbles_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_MessengerBubbles_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Example/Pods-MessengerBubbles_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MessengerBubbles" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MessengerBubbles/MessengerBubbles.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CFNetwork" -framework "Foundation" -framework "Kingfisher" -framework "MessengerBubbles" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Example/Pods-MessengerBubbles_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_MessengerBubbles_Example { 2 | umbrella header "Pods-MessengerBubbles_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Example/Pods-MessengerBubbles_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MessengerBubbles" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MessengerBubbles/MessengerBubbles.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CFNetwork" -framework "Foundation" -framework "Kingfisher" -framework "MessengerBubbles" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Tests/Pods-MessengerBubbles_Tests-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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Tests/Pods-MessengerBubbles_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Tests/Pods-MessengerBubbles_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Tests/Pods-MessengerBubbles_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_MessengerBubbles_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_MessengerBubbles_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Tests/Pods-MessengerBubbles_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_MessengerBubbles_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_MessengerBubbles_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Tests/Pods-MessengerBubbles_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MessengerBubbles" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MessengerBubbles/MessengerBubbles.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CFNetwork" -framework "Foundation" -framework "Kingfisher" -framework "MessengerBubbles" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Tests/Pods-MessengerBubbles_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_MessengerBubbles_Tests { 2 | umbrella header "Pods-MessengerBubbles_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-MessengerBubbles_Tests/Pods-MessengerBubbles_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MessengerBubbles" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MessengerBubbles/MessengerBubbles.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CFNetwork" -framework "Foundation" -framework "Kingfisher" -framework "MessengerBubbles" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Tests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import MessengerBubbles 3 | 4 | class Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | XCTAssert(true, "Pass") 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure() { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Laurent Grondin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MessengerBubbles.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "MessengerBubbles" 3 | s.version = "0.1.0" 4 | s.summary = "Create Image views for user or group like Messenger" 5 | s.homepage = "https://github.com/RpX974/MessengerBubbles" 6 | s.license = { :type => 'MIT', :file => 'LICENSE' } 7 | s.author = { 'Laurent Grondin' => 'laurent.grondin@epitech.eu' } 8 | s.source = { :git => "https://github.com/RpX974/MessengerBubbles.git", :tag => s.version.to_s } 9 | s.ios.deployment_target = "10.0" 10 | s.source_files = 'MessengerBubbles/Classes/**/*' 11 | s.frameworks = "Foundation" 12 | s.dependency 'Kingfisher', '~> 5.0' 13 | s.swift_version = "5.0" 14 | end 15 | -------------------------------------------------------------------------------- /MessengerBubbles/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RpX974/MessengerBubbles/4c1a00b62bd078c4fdfc25aa15ea0a37ea1ac422/MessengerBubbles/Assets/.gitkeep -------------------------------------------------------------------------------- /MessengerBubbles/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RpX974/MessengerBubbles/4c1a00b62bd078c4fdfc25aa15ea0a37ea1ac422/MessengerBubbles/Classes/.gitkeep -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MessengerBubbles 2 | ![Banner](Screenshots/Banner.png?) 3 | 4 | [![Version](https://img.shields.io/cocoapods/v/MessengerBubbles.svg?style=flat)](https://cocoapods.org/pods/MessengerBubbles) 5 | [![License](https://img.shields.io/cocoapods/l/MessengerBubbles.svg?style=flat)](https://cocoapods.org/pods/MessengerBubbles) 6 | [![Platform](https://img.shields.io/cocoapods/p/MessengerBubbles.svg?style=flat)](https://cocoapods.org/pods/MessengerBubbles) 7 | 8 | ![App Image](Screenshots/gif.gif?) 9 | 10 | ## Example 11 | 12 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 13 | 14 | ## Requirements 15 | 16 | ## Installation 17 | 18 | ### CocoaPods 19 | 20 | MessengerBubbles is available through [CocoaPods](https://cocoapods.org). To install 21 | it, simply add the following line to your Podfile: 22 | 23 | ```bash 24 | pod 'MessengerBubbles' 25 | ``` 26 | 27 | ### Manually 28 | 29 | If you prefer not to use any of the aforementioned dependency managers, you can integrate CodableCloudKit into your project manually. Simply drag the `Sources` Folder into your Xcode project. 30 | 31 | ## Usage 32 | 33 | ### From Storyboard 34 | 35 | MessengerBubbles is a subview of `UIView` 36 | 37 | ![Storyboard](Screenshots/Storyboard.png?) 38 | 39 | Just create a `UIView` and replace the Custom Class with `MessengerBubbles` 40 | 41 | You can choose if you want to enable the online bubble indicator, if the user or group is online or not, change the online buble color and border color. 42 | 43 | ### Programmatically 44 | 45 | You have many ways to create a `MessengerBubbles` 46 | 47 | ```swift 48 | public init(size: CGFloat, 49 | borderColor: UIColor = .white, 50 | onlineBubbleColor: UIColor = PrivateConstants.onlineBubbleColor, 51 | enableOnlineBubble: Bool = true, 52 | isOnline: Bool = false) 53 | 54 | 55 | public init(images: [UIImage?]?, 56 | size: CGFloat, 57 | borderColor: UIColor = .white, 58 | onlineBubbleColor: UIColor = PrivateConstants.onlineBubbleColor, 59 | enableOnlineBubble: Bool = true, 60 | isOnline: Bool = false) 61 | 62 | public init(urls: [String?]?, 63 | size: CGFloat, 64 | borderColor: UIColor = .white, 65 | onlineBubbleColor: UIColor = PrivateConstants.onlineBubbleColor, 66 | enableOnlineBubble: Bool = true, 67 | isOnline: Bool = false) 68 | ``` 69 | 70 | Size is always requiered. All others properties have a default value. 71 | 72 | ```swift 73 | MessengerBubbles(size: 60) // With size only 74 | 75 | MessengerBubbles(size: 60, borderColor: .blue, 76 | onlineBubbleColor: .yellow, 77 | enabledOnlineBubble: false, 78 | isOnline: false) // With custom values 79 | 80 | MessengerBubbles(images: images, 81 | size: 60) // With an array of UIImage at initialization 82 | 83 | MessengerBubbles(urls: urls, 84 | size: 60) // With an array of String at initialization 85 | ``` 86 | 87 | You can set images after `MessengerBubbles` has been created. 88 | If your array contains 1 element, it will create a single bubble. 89 | By the way, if it contains 2 elements or more, it will create 2 bubbles even if it contains more than 2 elements. 90 | 91 | ```swift 92 | public func setImages(with images: [UIImage?]?) // Set images with an array of UIImage 93 | public func setImages(with urls: [String?]?) // Set images with an array of String 94 | 95 | let bubble = MessengerBubbles(size: 60) 96 | bubble.setImages(with: images) 97 | //or 98 | bubble.setImages(with: urls) 99 | ``` 100 | 101 | `isOnline` and `onlineBubbleColor` can be changed after initialization 102 | 103 | ```swift 104 | bubble.isOnline = true // or false 105 | bubble.onlineBubbleColor = .black // or whatever you want 106 | ``` 107 | 108 | ## Author 109 | 110 | Laurent Grondin, laurent.grondin@epitech.eu 111 | 112 | ## Contributing 113 | Contributions are very welcome 🙌 114 | 115 | ## License 116 | 117 | MessengerBubbles is available under the MIT license. See the LICENSE file for more info. 118 | -------------------------------------------------------------------------------- /Screenshots/Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RpX974/MessengerBubbles/4c1a00b62bd078c4fdfc25aa15ea0a37ea1ac422/Screenshots/Banner.png -------------------------------------------------------------------------------- /Screenshots/Storyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RpX974/MessengerBubbles/4c1a00b62bd078c4fdfc25aa15ea0a37ea1ac422/Screenshots/Storyboard.png -------------------------------------------------------------------------------- /Screenshots/gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RpX974/MessengerBubbles/4c1a00b62bd078c4fdfc25aa15ea0a37ea1ac422/Screenshots/gif.gif -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------