├── .DS_Store ├── .gitignore ├── MoviesAPP.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── MoviesAPP.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── MoviesAPP ├── Common │ ├── AutoSizeTableView.swift │ ├── Configurator │ │ └── TableViewConfigurator.swift │ ├── Constants │ │ ├── Colors.swift │ │ ├── Errors.swift │ │ ├── Fonts.swift │ │ └── Grid.swift │ ├── Extensions │ │ ├── NSAttributedStringExtensions.swift │ │ ├── NumberExtensions.swift │ │ ├── UINavigationController+Extensions.swift │ │ └── UITableView+Extensions.swift │ └── ViewCode.swift ├── Info.plist ├── Resources │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── Images │ │ │ ├── Contents.json │ │ │ ├── avatar.imageset │ │ │ ├── Contents.json │ │ │ └── avatar.pdf │ │ │ └── background.imageset │ │ │ ├── Contents.json │ │ │ └── background.pdf │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ └── SceneDelegate.swift ├── Screens │ ├── MovieCell.swift │ └── MoviesViewController.swift └── Service │ ├── APIConstants.swift │ ├── Models │ ├── Episode.swift │ ├── Image.swift │ ├── Movie.swift │ └── Responses.swift │ ├── MoviesService.swift │ └── Service.swift ├── Podfile ├── Podfile.lock ├── Pods ├── Kingfisher │ ├── LICENSE │ ├── README.md │ └── Sources │ │ ├── Cache │ │ ├── CacheSerializer.swift │ │ ├── DiskStorage.swift │ │ ├── FormatIndicatedCacheSerializer.swift │ │ ├── ImageCache.swift │ │ ├── MemoryStorage.swift │ │ └── Storage.swift │ │ ├── Extensions │ │ ├── CPListItem+Kingfisher.swift │ │ ├── ImageView+Kingfisher.swift │ │ ├── NSButton+Kingfisher.swift │ │ ├── NSTextAttachment+Kingfisher.swift │ │ ├── TVMonogramView+Kingfisher.swift │ │ ├── UIButton+Kingfisher.swift │ │ └── WKInterfaceImage+Kingfisher.swift │ │ ├── General │ │ ├── ImageSource │ │ │ ├── AVAssetImageDataProvider.swift │ │ │ ├── ImageDataProvider.swift │ │ │ ├── Resource.swift │ │ │ └── Source.swift │ │ ├── KF.swift │ │ ├── KFOptionsSetter.swift │ │ ├── Kingfisher.swift │ │ ├── KingfisherError.swift │ │ ├── KingfisherManager.swift │ │ └── KingfisherOptionsInfo.swift │ │ ├── Image │ │ ├── Filter.swift │ │ ├── GIFAnimatedImage.swift │ │ ├── GraphicsContext.swift │ │ ├── Image.swift │ │ ├── ImageDrawing.swift │ │ ├── ImageFormat.swift │ │ ├── ImageProcessor.swift │ │ ├── ImageProgressive.swift │ │ ├── ImageTransition.swift │ │ └── Placeholder.swift │ │ ├── Networking │ │ ├── AuthenticationChallengeResponsable.swift │ │ ├── ImageDataProcessor.swift │ │ ├── ImageDownloader.swift │ │ ├── ImageDownloaderDelegate.swift │ │ ├── ImageModifier.swift │ │ ├── ImagePrefetcher.swift │ │ ├── RedirectHandler.swift │ │ ├── RequestModifier.swift │ │ ├── RetryStrategy.swift │ │ ├── SessionDataTask.swift │ │ └── SessionDelegate.swift │ │ ├── SwiftUI │ │ ├── ImageBinder.swift │ │ ├── ImageContext.swift │ │ ├── KFAnimatedImage.swift │ │ ├── KFImage.swift │ │ ├── KFImageOptions.swift │ │ ├── KFImageProtocol.swift │ │ └── KFImageRenderer.swift │ │ ├── Utility │ │ ├── Box.swift │ │ ├── CallbackQueue.swift │ │ ├── Delegate.swift │ │ ├── ExtensionHelpers.swift │ │ ├── Result.swift │ │ ├── Runtime.swift │ │ ├── SizeExtensions.swift │ │ └── String+MD5.swift │ │ └── Views │ │ ├── AnimatedImageView.swift │ │ └── Indicator.swift ├── Manifest.lock ├── Pods.xcodeproj │ └── project.pbxproj └── Target Support Files │ ├── Kingfisher │ ├── Kingfisher-Info.plist │ ├── Kingfisher-dummy.m │ ├── Kingfisher-prefix.pch │ ├── Kingfisher-umbrella.h │ ├── Kingfisher.debug.xcconfig │ ├── Kingfisher.modulemap │ └── Kingfisher.release.xcconfig │ └── Pods-MoviesAPP │ ├── Pods-MoviesAPP-Info.plist │ ├── Pods-MoviesAPP-acknowledgements.markdown │ ├── Pods-MoviesAPP-acknowledgements.plist │ ├── Pods-MoviesAPP-dummy.m │ ├── Pods-MoviesAPP-frameworks-Debug-input-files.xcfilelist │ ├── Pods-MoviesAPP-frameworks-Debug-output-files.xcfilelist │ ├── Pods-MoviesAPP-frameworks-Release-input-files.xcfilelist │ ├── Pods-MoviesAPP-frameworks-Release-output-files.xcfilelist │ ├── Pods-MoviesAPP-frameworks.sh │ ├── Pods-MoviesAPP-umbrella.h │ ├── Pods-MoviesAPP.debug.xcconfig │ ├── Pods-MoviesAPP.modulemap │ └── Pods-MoviesAPP.release.xcconfig └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bullas/viewcode-live-coding-dio/357739f5886242b1d6e44095331da684e610b5ef/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | -------------------------------------------------------------------------------- /MoviesAPP.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MoviesAPP.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MoviesAPP.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /MoviesAPP.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MoviesAPP/Common/AutoSizeTableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutoSizeTableView.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 12/02/22. 6 | // 7 | 8 | import UIKit 9 | 10 | final class AutoSizeTableView: UITableView { 11 | 12 | // MARK: - Functions 13 | 14 | override func reloadData() { 15 | super.reloadData() 16 | self.invalidateIntrinsicContentSize() 17 | self.layoutIfNeeded() 18 | } 19 | 20 | override var intrinsicContentSize: CGSize { 21 | return CGSize(width: contentSize.width, height: contentSize.height) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /MoviesAPP/Common/Configurator/TableViewConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewConfigurator.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 11/02/22. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | import SwiftUI 11 | 12 | // MARK: Section 13 | struct TableViewSectionViewModel { 14 | let title: String? 15 | var rows: [TableViewCellViewModelProtocol] 16 | } 17 | 18 | // MARK: Cell View Model 19 | protocol TableViewCellViewModelProtocol {} 20 | 21 | // MARK: Cell View Model 22 | protocol TableViewCellProtocol where Self: UITableViewCell { 23 | func load(viewModel: TableViewCellViewModelProtocol) 24 | } 25 | 26 | // MARK: TableView Configurator 27 | protocol TableViewConfiguratorProtocol: NSObject { 28 | associatedtype Cell 29 | var source: [TableViewSectionViewModel]? { get } 30 | var target: UITableView? { get } 31 | var onSelected: ((IndexPath) -> Void)? { get set } 32 | func load(source: [TableViewSectionViewModel]) 33 | } 34 | 35 | typealias TableViewConfiguratorType = NSObject & TableViewConfiguratorProtocol & UITableViewDataSource & UITableViewDelegate 36 | 37 | final class TableViewConfigurator: TableViewConfiguratorType { 38 | 39 | // MARK: Properties 40 | 41 | private(set) var source: [TableViewSectionViewModel]? 42 | private(set) weak var target: UITableView? 43 | var onSelected: ((IndexPath) -> Void)? 44 | 45 | // MARK: Init 46 | 47 | init(target: UITableView) { 48 | self.target = target 49 | super.init() 50 | self.target?.dataSource = self 51 | self.target?.delegate = self 52 | self.target?.registerCell(type: Cell.self) 53 | } 54 | 55 | func load(source: [TableViewSectionViewModel]) { 56 | self.source = source 57 | target?.reloadData() 58 | } 59 | 60 | // MARK: DataSource 61 | 62 | func numberOfSections(in tableView: UITableView) -> Int { 63 | source?.count ?? 0 64 | } 65 | 66 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 67 | source?[section].rows.count ?? 0 68 | } 69 | 70 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 71 | guard let cell = tableView.dequeueCell(withType: Cell.self) as? TableViewCellProtocol, 72 | let viewModel = source?[indexPath.section].rows[indexPath.row] else { return UITableViewCell() } 73 | 74 | cell.load(viewModel: viewModel) 75 | return cell 76 | } 77 | 78 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 79 | source?[section].title 80 | } 81 | 82 | // MARK: Delegate 83 | 84 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 85 | onSelected?(indexPath) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /MoviesAPP/Common/Constants/Colors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Colors.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 10/02/22. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | enum Colors: String { 12 | 13 | case secondary 14 | case primary 15 | case background 16 | case overlay 17 | 18 | var color: UIColor? { 19 | return UIColor(named: self.rawValue) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /MoviesAPP/Common/Constants/Errors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Errors.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 10/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum ServiceError: Error { 11 | case parseError 12 | case statusCode(Int) 13 | case badRequest 14 | } 15 | -------------------------------------------------------------------------------- /MoviesAPP/Common/Constants/Fonts.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Fonts.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 10/02/22. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | enum Fonts { 12 | 13 | case small(UIFont.Weight) 14 | case medium(UIFont.Weight) 15 | case large(UIFont.Weight) 16 | 17 | var font: UIFont { 18 | switch self { 19 | case .small(let weight): 20 | return UIFont.systemFont(ofSize: 12, weight: weight) 21 | case .medium(let weight): 22 | return UIFont.systemFont(ofSize: 18, weight: weight) 23 | case .large(let weight): 24 | return UIFont.systemFont(ofSize: 28, weight: weight) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /MoviesAPP/Common/Constants/Grid.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Grid.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 10/02/22. 6 | // 7 | 8 | import UIKit 9 | 10 | enum Grid { 11 | case small 12 | case medium 13 | case large 14 | 15 | var value: CGFloat { 16 | switch self { 17 | case .small: 18 | return 10 19 | case .medium: 20 | return 16 21 | case .large: 22 | return 32 23 | } 24 | } 25 | 26 | var insets: UIEdgeInsets { 27 | switch self { 28 | case .small: 29 | return UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) 30 | case .medium: 31 | return UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 15) 32 | case .large: 33 | return UIEdgeInsets(top: 32, left: 32, bottom: 32, right: 32) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /MoviesAPP/Common/Extensions/NSAttributedStringExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedStringExtensions.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 12/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension NSAttributedString { 11 | 12 | static func fromHTML(html: String) -> NSAttributedString? { 13 | let style = "" 14 | let data = Data(html.appending(String(format: style)).utf8) 15 | let attributedString = try? NSAttributedString(data: data, 16 | options: [.documentType: NSAttributedString.DocumentType.html], 17 | documentAttributes: nil) 18 | return attributedString 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /MoviesAPP/Common/Extensions/NumberExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NumberExtensions.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 12/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Int { 11 | func toString() -> String? { 12 | return "\(self)" 13 | } 14 | } 15 | 16 | extension Double { 17 | func toString() -> String? { 18 | return "\(self)" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /MoviesAPP/Common/Extensions/UINavigationController+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationController+Extensions.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 12/02/22. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | extension UINavigationController { 12 | func setAppearance() { 13 | self.navigationBar.prefersLargeTitles = true 14 | 15 | let appearance = UINavigationBarAppearance() 16 | appearance.backgroundColor = Colors.primary.color 17 | appearance.titleTextAttributes = [.foregroundColor: Colors.background.color ?? .white] 18 | appearance.largeTitleTextAttributes = [.foregroundColor: Colors.background.color ?? .white] 19 | 20 | self.navigationBar.tintColor = .white 21 | self.navigationBar.standardAppearance = appearance 22 | self.navigationBar.compactAppearance = appearance 23 | self.navigationBar.scrollEdgeAppearance = appearance 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /MoviesAPP/Common/Extensions/UITableView+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+Extensions.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 11/02/22. 6 | // 7 | 8 | import UIKit 9 | 10 | public extension UITableView { 11 | 12 | func registerCell(type: UITableViewCell.Type) { 13 | register(type, forCellReuseIdentifier: type.identifier) 14 | } 15 | 16 | func dequeueCell(withType type: UITableViewCell.Type) -> T? { 17 | return dequeueReusableCell(withIdentifier: type.identifier) as? T 18 | } 19 | 20 | func dequeueCell(withType type: UITableViewCell.Type, for indexPath: IndexPath) -> T? { 21 | return dequeueReusableCell(withIdentifier: type.identifier, for: indexPath) as? T 22 | } 23 | 24 | } 25 | 26 | public extension UITableViewCell { 27 | static var identifier: String { 28 | return String(describing: self) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MoviesAPP/Common/ViewCode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewCode.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 10/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol ViewCode { 11 | 12 | func buildHierarchy() 13 | func setupConstraints() 14 | func applyAdditionalChanges() 15 | } 16 | 17 | extension ViewCode { 18 | 19 | func setupView() { 20 | buildHierarchy() 21 | setupConstraints() 22 | applyAdditionalChanges() 23 | } 24 | 25 | func buildHierarchy() {} 26 | 27 | func setupConstraints() {} 28 | 29 | func applyAdditionalChanges() {} 30 | } 31 | -------------------------------------------------------------------------------- /MoviesAPP/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIApplicationSceneManifest 6 | 7 | UIApplicationSupportsMultipleScenes 8 | 9 | UISceneConfigurations 10 | 11 | UIWindowSceneSessionRoleApplication 12 | 13 | 14 | UISceneConfigurationName 15 | Default Configuration 16 | UISceneDelegateClassName 17 | $(PRODUCT_MODULE_NAME).SceneDelegate 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MoviesAPP/Resources/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MoviesAPP 4 | // 5 | // Created by Karolina Attekita on 28/03/22. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /MoviesAPP/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /MoviesAPP/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /MoviesAPP/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /MoviesAPP/Resources/Assets.xcassets/Images/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /MoviesAPP/Resources/Assets.xcassets/Images/avatar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "avatar.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /MoviesAPP/Resources/Assets.xcassets/Images/avatar.imageset/avatar.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bullas/viewcode-live-coding-dio/357739f5886242b1d6e44095331da684e610b5ef/MoviesAPP/Resources/Assets.xcassets/Images/avatar.imageset/avatar.pdf -------------------------------------------------------------------------------- /MoviesAPP/Resources/Assets.xcassets/Images/background.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "background.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /MoviesAPP/Resources/Assets.xcassets/Images/background.imageset/background.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bullas/viewcode-live-coding-dio/357739f5886242b1d6e44095331da684e610b5ef/MoviesAPP/Resources/Assets.xcassets/Images/background.imageset/background.pdf -------------------------------------------------------------------------------- /MoviesAPP/Resources/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /MoviesAPP/Resources/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // MoviesAPP 4 | // 5 | // Created by Karolina Attekita on 28/03/22. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let windowScene = (scene as? UIWindowScene) else { return } 20 | 21 | self.window = UIWindow(windowScene: windowScene) 22 | self.window?.rootViewController = UINavigationController(rootViewController: MoviesViewController()) 23 | self.window?.makeKeyAndVisible() 24 | } 25 | 26 | func sceneDidDisconnect(_ scene: UIScene) { 27 | // Called as the scene is being released by the system. 28 | // This occurs shortly after the scene enters the background, or when its session is discarded. 29 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 30 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 31 | } 32 | 33 | func sceneDidBecomeActive(_ scene: UIScene) { 34 | // Called when the scene has moved from an inactive state to an active state. 35 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 36 | } 37 | 38 | func sceneWillResignActive(_ scene: UIScene) { 39 | // Called when the scene will move from an active state to an inactive state. 40 | // This may occur due to temporary interruptions (ex. an incoming phone call). 41 | } 42 | 43 | func sceneWillEnterForeground(_ scene: UIScene) { 44 | // Called as the scene transitions from the background to the foreground. 45 | // Use this method to undo the changes made on entering the background. 46 | } 47 | 48 | func sceneDidEnterBackground(_ scene: UIScene) { 49 | // Called as the scene transitions from the foreground to the background. 50 | // Use this method to save data, release shared resources, and store enough scene-specific state information 51 | // to restore the scene back to its current state. 52 | } 53 | 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /MoviesAPP/Screens/MovieCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovieCell.swift 3 | // MoviesAPP 4 | // 5 | // Created by Karolina Attekita on 28/03/22. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | import Kingfisher 11 | 12 | final class MovieCell: UITableViewCell { 13 | 14 | private lazy var containerView: UIView = { 15 | let view = UIView() 16 | view.backgroundColor = .clear 17 | view.layer.masksToBounds = true 18 | view.layer.cornerRadius = 20 19 | view.translatesAutoresizingMaskIntoConstraints = false 20 | return view 21 | }() 22 | 23 | private lazy var blurView: UIVisualEffectView = { 24 | let blurEffect = UIBlurEffect(style: .dark) 25 | let blurView = UIVisualEffectView(effect: blurEffect) 26 | blurView.translatesAutoresizingMaskIntoConstraints = false 27 | return blurView 28 | }() 29 | 30 | private lazy var stackView: UIStackView = { 31 | let stackView = UIStackView(arrangedSubviews: [title, genres, rateStackView]) 32 | stackView.axis = .vertical 33 | stackView.spacing = 8 34 | stackView.translatesAutoresizingMaskIntoConstraints = false 35 | return stackView 36 | }() 37 | 38 | private lazy var title: UILabel = { 39 | let label = UILabel() 40 | label.font = Fonts.large(.regular).font 41 | label.textColor = .white 42 | label.numberOfLines = 2 43 | label.translatesAutoresizingMaskIntoConstraints = false 44 | return label 45 | }() 46 | 47 | private lazy var genres: UILabel = { 48 | let label = UILabel() 49 | label.font = Fonts.small(.regular).font 50 | label.textColor = .lightGray 51 | label.numberOfLines = 2 52 | label.translatesAutoresizingMaskIntoConstraints = false 53 | return label 54 | }() 55 | 56 | private lazy var poster: UIImageView = { 57 | let image = UIImageView() 58 | image.translatesAutoresizingMaskIntoConstraints = false 59 | return image 60 | }() 61 | 62 | private lazy var rateStackView: UIStackView = { 63 | let stackView = UIStackView(arrangedSubviews: [star, rateLabel]) 64 | stackView.axis = .horizontal 65 | stackView.spacing = 8 66 | stackView.translatesAutoresizingMaskIntoConstraints = false 67 | return stackView 68 | }() 69 | 70 | private lazy var star: UIImageView = { 71 | let image = UIImageView() 72 | image.translatesAutoresizingMaskIntoConstraints = false 73 | image.image = UIImage.init(systemName: "star") 74 | image.tintColor = .yellow 75 | return image 76 | }() 77 | 78 | private lazy var rateLabel: UILabel = { 79 | let label = UILabel() 80 | label.font = Fonts.medium(.regular).font 81 | label.textColor = .white 82 | label.numberOfLines = 2 83 | label.translatesAutoresizingMaskIntoConstraints = false 84 | return label 85 | }() 86 | 87 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 88 | super.init(style: style, reuseIdentifier: reuseIdentifier) 89 | setupView() 90 | } 91 | 92 | @available(*, unavailable) 93 | required init?(coder: NSCoder) { 94 | fatalError("init(coder:) has not been implemented") 95 | } 96 | 97 | func configure(with model: Movie) { 98 | title.text = model.name 99 | genres.text = model.genres?.joined(separator: ", ") 100 | rateLabel.text = model.rating?.average?.toString() 101 | 102 | guard let image = model.image?.medium, 103 | let imageUrl = URL(string: image) else { 104 | return 105 | } 106 | 107 | poster.kf.indicatorType = .activity 108 | poster.kf.setImage(with: imageUrl) 109 | } 110 | } 111 | 112 | extension MovieCell: ViewCode { 113 | func buildHierarchy() { 114 | contentView.addSubview(containerView) 115 | containerView.addSubview(blurView) 116 | containerView.addSubview(poster) 117 | containerView.addSubview(stackView) 118 | } 119 | 120 | func setupConstraints() { 121 | NSLayoutConstraint.activate([ 122 | containerView.topAnchor.constraint(equalTo: contentView.topAnchor), 123 | containerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 24), 124 | containerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -24), 125 | containerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -24), 126 | 127 | blurView.topAnchor.constraint(equalTo: containerView.topAnchor), 128 | blurView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), 129 | blurView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), 130 | blurView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), 131 | 132 | poster.topAnchor.constraint(equalTo: containerView.topAnchor), 133 | poster.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), 134 | poster.widthAnchor.constraint(equalToConstant: 100), 135 | poster.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), 136 | 137 | stackView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), 138 | stackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16), 139 | stackView.leadingAnchor.constraint(equalTo: poster.trailingAnchor, constant: 16), 140 | 141 | star.widthAnchor.constraint(equalToConstant: 24), 142 | star.heightAnchor.constraint(equalToConstant: 24) 143 | ]) 144 | 145 | let heightConstraint = containerView.heightAnchor.constraint(equalToConstant: 150) 146 | heightConstraint.isActive = true 147 | heightConstraint.priority = UILayoutPriority.init(999) 148 | } 149 | 150 | func applyAdditionalChanges() { 151 | contentView.backgroundColor = .clear 152 | backgroundColor = .clear 153 | selectionStyle = .none 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /MoviesAPP/Screens/MoviesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MoviesViewController.swift 3 | // MoviesAPP 4 | // 5 | // Created by Karolina Attekita on 28/03/22. 6 | // 7 | 8 | import UIKit 9 | 10 | final class MoviesViewController: UIViewController { 11 | 12 | private lazy var backgroundImage: UIImageView = { 13 | let image = UIImageView() 14 | image.image = UIImage(named: "background") 15 | image.contentMode = .scaleToFill 16 | image.translatesAutoresizingMaskIntoConstraints = false 17 | image.alpha = 0.4 18 | return image 19 | }() 20 | 21 | private lazy var searchController: UISearchController = { 22 | let searchController = UISearchController(searchResultsController: nil) 23 | searchController.searchBar.tintColor = .white 24 | searchController.searchBar.barStyle = .black 25 | searchController.searchBar.delegate = self 26 | return searchController 27 | }() 28 | 29 | private lazy var tableView: UITableView = { 30 | let tableView = UITableView() 31 | tableView.dataSource = self 32 | tableView.delegate = self 33 | tableView.backgroundColor = .clear 34 | tableView.translatesAutoresizingMaskIntoConstraints = false 35 | tableView.estimatedRowHeight = 44 36 | tableView.rowHeight = UITableView.automaticDimension 37 | tableView.registerCell(type: MovieCell.self) 38 | return tableView 39 | }() 40 | 41 | private var movies: [Movie]? { 42 | didSet { 43 | self.tableView.reloadData() 44 | } 45 | } 46 | 47 | private let service: MoviesService = MoviesService() 48 | 49 | override func viewDidLoad() { 50 | super.viewDidLoad() 51 | setupView() 52 | } 53 | 54 | override func viewDidAppear(_ animated: Bool) { 55 | super.viewDidAppear(animated) 56 | fetchMovies() 57 | } 58 | 59 | private func fetchMovies() { 60 | service.fetchList { [weak self] result in 61 | switch result { 62 | case .success(let response): 63 | self?.movies = response 64 | case .failure: 65 | self?.movies = nil 66 | } 67 | } 68 | } 69 | 70 | private func searchhMovies(term: String) { 71 | service.fetchResults(term) { [weak self] result in 72 | switch result { 73 | case .success(let response): 74 | self?.movies = response.compactMap({ $0.show }) 75 | case .failure: 76 | self?.movies = nil 77 | } 78 | } 79 | } 80 | 81 | 82 | private func setupNavigation() { 83 | title = "My movies" 84 | navigationController?.navigationBar.prefersLargeTitles = true 85 | let textAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white] 86 | navigationController?.navigationBar.largeTitleTextAttributes = textAttributes 87 | navigationItem.searchController = searchController 88 | } 89 | } 90 | 91 | extension MoviesViewController: ViewCode { 92 | func buildHierarchy() { 93 | view.addSubview(backgroundImage) 94 | view.addSubview(tableView) 95 | } 96 | 97 | func setupConstraints() { 98 | NSLayoutConstraint.activate([ 99 | backgroundImage.topAnchor.constraint(equalTo: view.topAnchor), 100 | backgroundImage.leadingAnchor.constraint(equalTo: view.leadingAnchor), 101 | backgroundImage.trailingAnchor.constraint(equalTo: view.trailingAnchor), 102 | backgroundImage.bottomAnchor.constraint(equalTo: view.bottomAnchor), 103 | 104 | tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), 105 | tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), 106 | tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), 107 | tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor) 108 | ]) 109 | } 110 | 111 | func applyAdditionalChanges() { 112 | setupNavigation() 113 | } 114 | } 115 | 116 | 117 | extension MoviesViewController: UITableViewDataSource, UITableViewDelegate { 118 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 119 | return movies?.count ?? 0 120 | } 121 | 122 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 123 | guard let movieCell = tableView.dequeueCell(withType: MovieCell.self, for: indexPath) as? MovieCell else { 124 | return UITableViewCell() 125 | } 126 | if let model = movies?[indexPath.row] { 127 | movieCell.configure(with: model) 128 | } 129 | return movieCell 130 | } 131 | } 132 | 133 | 134 | // MARK: - Search Bar 135 | extension MoviesViewController: UISearchBarDelegate { 136 | func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { 137 | guard !searchText.isEmpty else { 138 | fetchMovies() 139 | return 140 | } 141 | searchhMovies(term: searchText) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /MoviesAPP/Service/APIConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIConstants.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 10/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum APIConstants { 11 | static let baseURL = "https://api.tvmaze.com/" 12 | 13 | enum Endpoints { 14 | case search 15 | case shows 16 | case episodes(Int) 17 | 18 | var path: String { 19 | switch self { 20 | case .search: 21 | return "search/shows" 22 | case .shows: 23 | return "shows" 24 | case .episodes(let showID): 25 | return "shows/\(showID)/episodes" 26 | } 27 | } 28 | 29 | var urlString: String { 30 | return APIConstants.baseURL + self.path 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /MoviesAPP/Service/Models/Episode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Episode.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 12/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - EpisodeElement 11 | struct Episode: Codable { 12 | let id: Int 13 | let name: String? 14 | let season, number: Int? 15 | let image: Image? 16 | let summary: String? 17 | } 18 | 19 | struct Season: Codable { 20 | let number: Int? 21 | let episodes: [Episode]? 22 | } 23 | -------------------------------------------------------------------------------- /MoviesAPP/Service/Models/Image.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Image.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 12/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Image 11 | struct Image: Codable { 12 | let medium, original: String 13 | } 14 | -------------------------------------------------------------------------------- /MoviesAPP/Service/Models/Movie.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Show.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 11/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Show 11 | struct Movie: Codable { 12 | let id: Int 13 | let name: String? 14 | let genres: [String]? 15 | let schedule: Schedule? 16 | let rating: Rating? 17 | let image: Image? 18 | let summary: String? 19 | } 20 | 21 | // MARK: - Rating 22 | struct Rating: Codable { 23 | let average: Double? 24 | } 25 | 26 | // MARK: - Schedule 27 | struct Schedule: Codable { 28 | let time: String 29 | let days: [String] 30 | 31 | func toString() -> String { 32 | return "\(time) (\(days.joined(separator: ", ")))" 33 | } 34 | } 35 | struct SearchShowElement: Codable { 36 | let show: Movie 37 | } 38 | -------------------------------------------------------------------------------- /MoviesAPP/Service/Models/Responses.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Responses.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 12/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | typealias EpisodeResponse = [Episode] 11 | typealias ShowResponse = [Movie] 12 | typealias SearchShowResponse = [SearchShowElement] 13 | -------------------------------------------------------------------------------- /MoviesAPP/Service/MoviesService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MoviesService.swift 3 | // MoviesAPP 4 | // 5 | // Created by Karolina Attekita on 28/03/22. 6 | // 7 | 8 | import Foundation 9 | 10 | final class MoviesService { 11 | 12 | // MARK: Properties 13 | 14 | private let service: ServiceProvider 15 | 16 | // MARK: Init's 17 | 18 | init(service: ServiceProvider = Service()) { 19 | self.service = service 20 | } 21 | 22 | func fetchResults(_ search: String, result: @escaping (Result) -> Void) { 23 | let parameters = ["q": search] 24 | service.makeRequest(endpoint: .search, parameters: parameters) { response in 25 | DispatchQueue.main.async { 26 | result(response) 27 | } 28 | } 29 | } 30 | 31 | func fetchList(result: @escaping(Result) -> Void) { 32 | service.makeRequest(endpoint: .shows, parameters: nil) { response in 33 | DispatchQueue.main.async { 34 | result(response) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /MoviesAPP/Service/Service.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Service.swift 3 | // MazeTv 4 | // 5 | // Created by Karolina Attekita on 10/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol ServiceProvider { 11 | func makeRequest(endpoint: APIConstants.Endpoints, parameters: [String: String?]?, result: @escaping(Result) -> Void) 12 | } 13 | 14 | final class Service: ServiceProvider { 15 | 16 | // MARK: Properties 17 | 18 | private let session: URLSession = { 19 | let config = URLSessionConfiguration.default 20 | config.allowsCellularAccess = false 21 | config.httpAdditionalHeaders = ["Content-Type": "aplication/json"] 22 | config.timeoutIntervalForRequest = 30.0 23 | config.httpMaximumConnectionsPerHost = 5 24 | return URLSession(configuration: config) 25 | }() 26 | 27 | func makeRequest(endpoint: APIConstants.Endpoints, parameters: [String: String?]?, result: @escaping(Result) -> Void) { 28 | 29 | guard var components = URLComponents(string: endpoint.urlString) else { return } 30 | 31 | components.queryItems = parameters?.map { (key, value) in 32 | URLQueryItem(name: key, value: value) 33 | } 34 | 35 | guard let url = components.url else { return } 36 | var request = URLRequest(url: url) 37 | request.setValue("application/json", forHTTPHeaderField: "Content-Type") 38 | request.setValue("application/json", forHTTPHeaderField: "Accept") 39 | 40 | let dataTask = session.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in 41 | 42 | guard error == nil, 43 | let response = response as? HTTPURLResponse, 44 | let data = data 45 | else { 46 | result(.failure(ServiceError.badRequest)) 47 | return 48 | } 49 | 50 | do { 51 | guard response.statusCode == 200 else { 52 | result(.failure(ServiceError.statusCode(response.statusCode))) 53 | return 54 | } 55 | 56 | let codable = try JSONDecoder().decode(T.self, from: data) 57 | result(.success(codable)) 58 | } catch { 59 | print("Unexpected error: \(error).") 60 | result(.failure(ServiceError.parseError)) 61 | } 62 | } 63 | dataTask.resume() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'MoviesAPP' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for MoviesAPP 9 | pod 'Kingfisher', '~> 7.0' 10 | 11 | end 12 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kingfisher (7.1.2) 3 | 4 | DEPENDENCIES: 5 | - Kingfisher (~> 7.0) 6 | 7 | SPEC REPOS: 8 | trunk: 9 | - Kingfisher 10 | 11 | SPEC CHECKSUMS: 12 | Kingfisher: 44ed6a8504763f27bab46163adfac83f5deb240c 13 | 14 | PODFILE CHECKSUM: 492d6753b0c9aa2c1e4e2a3ea542771bb3abfd47 15 | 16 | COCOAPODS: 1.11.2 17 | -------------------------------------------------------------------------------- /Pods/Kingfisher/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 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 | -------------------------------------------------------------------------------- /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 | import CoreGraphics 29 | 30 | /// An `CacheSerializer` is used to convert some data to an image object after 31 | /// retrieving it from disk storage, and vice versa, to convert an image to data object 32 | /// for storing to the disk storage. 33 | public protocol CacheSerializer { 34 | 35 | /// Gets the serialized data from a provided image 36 | /// and optional original data for caching to disk. 37 | /// 38 | /// - Parameters: 39 | /// - image: The image needed to be serialized. 40 | /// - original: The original data which is just downloaded. 41 | /// If the image is retrieved from cache instead of 42 | /// downloaded, it will be `nil`. 43 | /// - Returns: The data object for storing to disk, or `nil` when no valid 44 | /// data could be serialized. 45 | func data(with image: KFCrossPlatformImage, original: Data?) -> Data? 46 | 47 | /// Gets an image from provided serialized data. 48 | /// 49 | /// - Parameters: 50 | /// - data: The data from which an image should be deserialized. 51 | /// - options: The parsed options for deserialization. 52 | /// - Returns: An image deserialized or `nil` when no valid image 53 | /// could be deserialized. 54 | func image(with data: Data, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? 55 | } 56 | 57 | /// Represents a basic and default `CacheSerializer` used in Kingfisher disk cache system. 58 | /// It could serialize and deserialize images in PNG, JPEG and GIF format. For 59 | /// image other than these formats, a normalized `pngRepresentation` will be used. 60 | public struct DefaultCacheSerializer: CacheSerializer { 61 | 62 | /// The default general cache serializer used across Kingfisher's cache. 63 | public static let `default` = DefaultCacheSerializer() 64 | 65 | /// The compression quality when converting image to a lossy format data. Default is 1.0. 66 | public var compressionQuality: CGFloat = 1.0 67 | 68 | /// Whether the original data should be preferred when serializing the image. 69 | /// If `true`, the input original data will be checked first and used unless the data is `nil`. 70 | /// In that case, the serialization will fall back to creating data from image. 71 | public var preferCacheOriginalData: Bool = false 72 | 73 | /// Creates a cache serializer that serialize and deserialize images in PNG, JPEG and GIF format. 74 | /// 75 | /// - Note: 76 | /// Use `DefaultCacheSerializer.default` unless you need to specify your own properties. 77 | /// 78 | public init() { } 79 | 80 | /// - Parameters: 81 | /// - image: The image needed to be serialized. 82 | /// - original: The original data which is just downloaded. 83 | /// If the image is retrieved from cache instead of 84 | /// downloaded, it will be `nil`. 85 | /// - Returns: The data object for storing to disk, or `nil` when no valid 86 | /// data could be serialized. 87 | /// 88 | /// - Note: 89 | /// Only when `original` contains valid PNG, JPEG and GIF format data, the `image` will be 90 | /// converted to the corresponding data type. Otherwise, if the `original` is provided but it is not 91 | /// If `original` is `nil`, the input `image` will be encoded as PNG data. 92 | public func data(with image: KFCrossPlatformImage, original: Data?) -> Data? { 93 | if preferCacheOriginalData { 94 | return original ?? 95 | image.kf.data( 96 | format: original?.kf.imageFormat ?? .unknown, 97 | compressionQuality: compressionQuality 98 | ) 99 | } else { 100 | return image.kf.data( 101 | format: original?.kf.imageFormat ?? .unknown, 102 | compressionQuality: compressionQuality 103 | ) 104 | } 105 | } 106 | 107 | /// Gets an image deserialized from provided data. 108 | /// 109 | /// - Parameters: 110 | /// - data: The data from which an image should be deserialized. 111 | /// - options: Options for deserialization. 112 | /// - Returns: An image deserialized or `nil` when no valid image 113 | /// could be deserialized. 114 | public func image(with data: Data, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? { 115 | return KingfisherWrapper.image(data: data, options: options.imageCreatingOptions) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /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 | import CoreGraphics 29 | 30 | /// `FormatIndicatedCacheSerializer` lets you indicate an image format for serialized caches. 31 | /// 32 | /// It could serialize and deserialize PNG, JPEG and GIF images. For 33 | /// image other than these formats, a normalized `pngRepresentation` will be used. 34 | /// 35 | /// Example: 36 | /// ```` 37 | /// let profileImageSize = CGSize(width: 44, height: 44) 38 | /// 39 | /// // A round corner image. 40 | /// let imageProcessor = RoundCornerImageProcessor( 41 | /// cornerRadius: profileImageSize.width / 2, targetSize: profileImageSize) 42 | /// 43 | /// let optionsInfo: KingfisherOptionsInfo = [ 44 | /// .cacheSerializer(FormatIndicatedCacheSerializer.png), 45 | /// .processor(imageProcessor)] 46 | /// 47 | /// A URL pointing to a JPEG image. 48 | /// let url = URL(string: "https://example.com/image.jpg")! 49 | /// 50 | /// // Image will be always cached as PNG format to preserve alpha channel for round rectangle. 51 | /// // So when you load it from cache again later, it will be still round cornered. 52 | /// // Otherwise, the corner part would be filled by white color (since JPEG does not contain an alpha channel). 53 | /// imageView.kf.setImage(with: url, options: optionsInfo) 54 | /// ```` 55 | public struct FormatIndicatedCacheSerializer: CacheSerializer { 56 | 57 | /// A `FormatIndicatedCacheSerializer` which converts image from and to PNG format. If the image cannot be 58 | /// represented by PNG format, it will fallback to its real format which is determined by `original` data. 59 | public static let png = FormatIndicatedCacheSerializer(imageFormat: .PNG, jpegCompressionQuality: nil) 60 | 61 | /// A `FormatIndicatedCacheSerializer` which converts image from and to JPEG format. If the image cannot be 62 | /// represented by JPEG format, it will fallback to its real format which is determined by `original` data. 63 | /// The compression quality is 1.0 when using this serializer. If you need to set a customized compression quality, 64 | /// use `jpeg(compressionQuality:)`. 65 | public static let jpeg = FormatIndicatedCacheSerializer(imageFormat: .JPEG, jpegCompressionQuality: 1.0) 66 | 67 | /// A `FormatIndicatedCacheSerializer` which converts image from and to JPEG format with a settable compression 68 | /// quality. If the image cannot be represented by JPEG format, it will fallback to its real format which is 69 | /// determined by `original` data. 70 | /// - Parameter compressionQuality: The compression quality when converting image to JPEG data. 71 | public static func jpeg(compressionQuality: CGFloat) -> FormatIndicatedCacheSerializer { 72 | return FormatIndicatedCacheSerializer(imageFormat: .JPEG, jpegCompressionQuality: compressionQuality) 73 | } 74 | 75 | /// A `FormatIndicatedCacheSerializer` which converts image from and to GIF format. If the image cannot be 76 | /// represented by GIF format, it will fallback to its real format which is determined by `original` data. 77 | public static let gif = FormatIndicatedCacheSerializer(imageFormat: .GIF, jpegCompressionQuality: nil) 78 | 79 | /// The indicated image format. 80 | private let imageFormat: ImageFormat 81 | 82 | /// The compression quality used for loss image format (like JPEG). 83 | private let jpegCompressionQuality: CGFloat? 84 | 85 | /// Creates data which represents the given `image` under a format. 86 | public func data(with image: KFCrossPlatformImage, original: Data?) -> Data? { 87 | 88 | func imageData(withFormat imageFormat: ImageFormat) -> Data? { 89 | return autoreleasepool { () -> Data? in 90 | switch imageFormat { 91 | case .PNG: return image.kf.pngRepresentation() 92 | case .JPEG: return image.kf.jpegRepresentation(compressionQuality: jpegCompressionQuality ?? 1.0) 93 | case .GIF: return image.kf.gifRepresentation() 94 | case .unknown: return nil 95 | } 96 | } 97 | } 98 | 99 | // generate data with indicated image format 100 | if let data = imageData(withFormat: imageFormat) { 101 | return data 102 | } 103 | 104 | let originalFormat = original?.kf.imageFormat ?? .unknown 105 | 106 | // generate data with original image's format 107 | if originalFormat != imageFormat, let data = imageData(withFormat: originalFormat) { 108 | return data 109 | } 110 | 111 | return original ?? image.kf.normalized.kf.pngRepresentation() 112 | } 113 | 114 | /// Same implementation as `DefaultCacheSerializer`. 115 | public func image(with data: Data, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? { 116 | return KingfisherWrapper.image(data: data, options: options.imageCreatingOptions) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /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 = 86_400 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): 59 | return date.addingTimeInterval(seconds) 60 | case .days(let days): 61 | let duration: TimeInterval = TimeInterval(TimeConstants.secondsInOneDay) * TimeInterval(days) 62 | return date.addingTimeInterval(duration) 63 | case .date(let ref): 64 | return ref 65 | case .expired: 66 | return .distantPast 67 | } 68 | } 69 | 70 | var estimatedExpirationSinceNow: Date { 71 | return estimatedExpirationSince(Date()) 72 | } 73 | 74 | var isExpired: Bool { 75 | return timeInterval <= 0 76 | } 77 | 78 | var timeInterval: TimeInterval { 79 | switch self { 80 | case .never: return .infinity 81 | case .seconds(let seconds): return seconds 82 | case .days(let days): return TimeInterval(TimeConstants.secondsInOneDay) * TimeInterval(days) 83 | case .date(let ref): return ref.timeIntervalSinceNow 84 | case .expired: return -(.infinity) 85 | } 86 | } 87 | } 88 | 89 | /// Represents the expiration extending strategy used in storage to after access. 90 | /// 91 | /// - none: The item expires after the original time, without extending after access. 92 | /// - cacheTime: The item expiration extends by the original cache time after each access. 93 | /// - expirationTime: The item expiration extends by the provided time after each access. 94 | public enum ExpirationExtending { 95 | /// The item expires after the original time, without extending after access. 96 | case none 97 | /// The item expiration extends by the original cache time after each access. 98 | case cacheTime 99 | /// The item expiration extends by the provided time after each access. 100 | case expirationTime(_ expiration: StorageExpiration) 101 | } 102 | 103 | /// Represents types which cost in memory can be calculated. 104 | public protocol CacheCostCalculable { 105 | var cacheCost: Int { get } 106 | } 107 | 108 | /// Represents types which can be converted to and from data. 109 | public protocol DataTransformable { 110 | func toData() throws -> Data 111 | static func fromData(_ data: Data) throws -> Self 112 | static var empty: Self { get } 113 | } 114 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/General/ImageSource/AVAssetImageDataProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AVAssetImageDataProvider.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2020/08/09. 6 | // 7 | // Copyright (c) 2020 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(watchOS) 28 | 29 | import Foundation 30 | import AVKit 31 | 32 | #if canImport(MobileCoreServices) 33 | import MobileCoreServices 34 | #else 35 | import CoreServices 36 | #endif 37 | 38 | /// A data provider to provide thumbnail data from a given AVKit asset. 39 | public struct AVAssetImageDataProvider: ImageDataProvider { 40 | 41 | /// The possible error might be caused by the `AVAssetImageDataProvider`. 42 | /// - userCancelled: The data provider process is cancelled. 43 | /// - invalidImage: The retrieved image is invalid. 44 | public enum AVAssetImageDataProviderError: Error { 45 | case userCancelled 46 | case invalidImage(_ image: CGImage?) 47 | } 48 | 49 | /// The asset image generator bound to `self`. 50 | public let assetImageGenerator: AVAssetImageGenerator 51 | 52 | /// The time at which the image should be generate in the asset. 53 | public let time: CMTime 54 | 55 | private var internalKey: String { 56 | return (assetImageGenerator.asset as? AVURLAsset)?.url.absoluteString ?? UUID().uuidString 57 | } 58 | 59 | /// The cache key used by `self`. 60 | public var cacheKey: String { 61 | return "\(internalKey)_\(time.seconds)" 62 | } 63 | 64 | /// Creates an asset image data provider. 65 | /// - Parameters: 66 | /// - assetImageGenerator: The asset image generator controls data providing behaviors. 67 | /// - time: At which time in the asset the image should be generated. 68 | public init(assetImageGenerator: AVAssetImageGenerator, time: CMTime) { 69 | self.assetImageGenerator = assetImageGenerator 70 | self.time = time 71 | } 72 | 73 | /// Creates an asset image data provider. 74 | /// - Parameters: 75 | /// - assetURL: The URL of asset for providing image data. 76 | /// - time: At which time in the asset the image should be generated. 77 | /// 78 | /// This method uses `assetURL` to create an `AVAssetImageGenerator` object and calls 79 | /// the `init(assetImageGenerator:time:)` initializer. 80 | /// 81 | public init(assetURL: URL, time: CMTime) { 82 | let asset = AVAsset(url: assetURL) 83 | let generator = AVAssetImageGenerator(asset: asset) 84 | self.init(assetImageGenerator: generator, time: time) 85 | } 86 | 87 | /// Creates an asset image data provider. 88 | /// 89 | /// - Parameters: 90 | /// - assetURL: The URL of asset for providing image data. 91 | /// - seconds: At which time in seconds in the asset the image should be generated. 92 | /// 93 | /// This method uses `assetURL` to create an `AVAssetImageGenerator` object, uses `seconds` to create a `CMTime`, 94 | /// and calls the `init(assetImageGenerator:time:)` initializer. 95 | /// 96 | public init(assetURL: URL, seconds: TimeInterval) { 97 | let time = CMTime(seconds: seconds, preferredTimescale: 600) 98 | self.init(assetURL: assetURL, time: time) 99 | } 100 | 101 | public func data(handler: @escaping (Result) -> Void) { 102 | assetImageGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: time)]) { 103 | (requestedTime, image, imageTime, result, error) in 104 | if let error = error { 105 | handler(.failure(error)) 106 | return 107 | } 108 | 109 | if result == .cancelled { 110 | handler(.failure(AVAssetImageDataProviderError.userCancelled)) 111 | return 112 | } 113 | 114 | guard let cgImage = image, let data = cgImage.jpegData else { 115 | handler(.failure(AVAssetImageDataProviderError.invalidImage(image))) 116 | return 117 | } 118 | 119 | handler(.success(data)) 120 | } 121 | } 122 | } 123 | 124 | extension CGImage { 125 | var jpegData: Data? { 126 | guard let mutableData = CFDataCreateMutable(nil, 0), 127 | let destination = CGImageDestinationCreateWithData(mutableData, kUTTypeJPEG, 1, nil) 128 | else { 129 | return nil 130 | } 131 | CGImageDestinationAddImage(destination, self, nil) 132 | guard CGImageDestinationFinalize(destination) else { return nil } 133 | return mutableData as Data 134 | } 135 | } 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /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 | extension Resource { 42 | 43 | /// Converts `self` to a valid `Source` based on its `downloadURL` scheme. A `.provider` with 44 | /// `LocalFileImageDataProvider` associated will be returned if the URL points to a local file. Otherwise, 45 | /// `.network` is returned. 46 | public func convertToSource(overrideCacheKey: String? = nil) -> Source { 47 | return downloadURL.isFileURL ? 48 | .provider(LocalFileImageDataProvider(fileURL: downloadURL, cacheKey: overrideCacheKey ?? downloadURL.localFileCacheKey)) : 49 | .network(ImageResource(downloadURL: downloadURL, cacheKey: overrideCacheKey ?? cacheKey)) 50 | } 51 | } 52 | 53 | /// ImageResource is a simple combination of `downloadURL` and `cacheKey`. 54 | /// When passed to image view set methods, Kingfisher will try to download the target 55 | /// image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache. 56 | public struct ImageResource: Resource { 57 | 58 | // MARK: - Initializers 59 | 60 | /// Creates an image resource. 61 | /// 62 | /// - Parameters: 63 | /// - downloadURL: The target image URL from where the image can be downloaded. 64 | /// - cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key. 65 | /// Default is `nil`. 66 | public init(downloadURL: URL, cacheKey: String? = nil) { 67 | self.downloadURL = downloadURL 68 | self.cacheKey = cacheKey ?? downloadURL.absoluteString 69 | } 70 | 71 | // MARK: Protocol Conforming 72 | 73 | /// The key used in cache. 74 | public let cacheKey: String 75 | 76 | /// The target image URL. 77 | public let downloadURL: URL 78 | } 79 | 80 | /// URL conforms to `Resource` in Kingfisher. 81 | /// The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`. 82 | /// If you need customize the url and/or cache key, use `ImageResource` instead. 83 | extension URL: Resource { 84 | public var cacheKey: String { return absoluteString } 85 | public var downloadURL: URL { return self } 86 | } 87 | -------------------------------------------------------------------------------- /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 | case .provider(let provider): return provider.contentURL 79 | } 80 | } 81 | } 82 | 83 | extension Source: Hashable { 84 | public static func == (lhs: Source, rhs: Source) -> Bool { 85 | switch (lhs, rhs) { 86 | case (.network(let r1), .network(let r2)): 87 | return r1.cacheKey == r2.cacheKey && r1.downloadURL == r2.downloadURL 88 | case (.provider(let p1), .provider(let p2)): 89 | return p1.cacheKey == p2.cacheKey && p1.contentURL == p2.contentURL 90 | case (.provider(_), .network(_)): 91 | return false 92 | case (.network(_), .provider(_)): 93 | return false 94 | } 95 | } 96 | 97 | public func hash(into hasher: inout Hasher) { 98 | switch self { 99 | case .network(let r): 100 | hasher.combine(r.cacheKey) 101 | hasher.combine(r.downloadURL) 102 | case .provider(let p): 103 | hasher.combine(p.cacheKey) 104 | hasher.combine(p.contentURL) 105 | } 106 | } 107 | } 108 | 109 | extension Source { 110 | var asResource: Resource? { 111 | guard case .network(let resource) = self else { 112 | return nil 113 | } 114 | return resource 115 | } 116 | 117 | var asProvider: ImageDataProvider? { 118 | guard case .provider(let provider) = self else { 119 | return nil 120 | } 121 | return provider 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /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 KFCrossPlatformImage = NSImage 33 | public typealias KFCrossPlatformView = NSView 34 | public typealias KFCrossPlatformColor = NSColor 35 | public typealias KFCrossPlatformImageView = NSImageView 36 | public typealias KFCrossPlatformButton = NSButton 37 | #else 38 | import UIKit 39 | public typealias KFCrossPlatformImage = UIImage 40 | public typealias KFCrossPlatformColor = UIColor 41 | #if !os(watchOS) 42 | public typealias KFCrossPlatformImageView = UIImageView 43 | public typealias KFCrossPlatformView = UIView 44 | public typealias KFCrossPlatformButton = UIButton 45 | #if canImport(TVUIKit) 46 | import TVUIKit 47 | #endif 48 | #if canImport(CarPlay) && !targetEnvironment(macCatalyst) 49 | import CarPlay 50 | #endif 51 | #else 52 | import WatchKit 53 | #endif 54 | #endif 55 | 56 | /// Wrapper for Kingfisher compatible types. This type provides an extension point for 57 | /// convenience methods in Kingfisher. 58 | public struct KingfisherWrapper { 59 | public let base: Base 60 | public init(_ base: Base) { 61 | self.base = base 62 | } 63 | } 64 | 65 | /// Represents an object type that is compatible with Kingfisher. You can use `kf` property to get a 66 | /// value in the namespace of Kingfisher. 67 | public protocol KingfisherCompatible: AnyObject { } 68 | 69 | /// Represents a value type that is compatible with Kingfisher. You can use `kf` property to get a 70 | /// value in the namespace of Kingfisher. 71 | public protocol KingfisherCompatibleValue {} 72 | 73 | extension KingfisherCompatible { 74 | /// Gets a namespace holder for Kingfisher compatible types. 75 | public var kf: KingfisherWrapper { 76 | get { return KingfisherWrapper(self) } 77 | set { } 78 | } 79 | } 80 | 81 | extension KingfisherCompatibleValue { 82 | /// Gets a namespace holder for Kingfisher compatible types. 83 | public var kf: KingfisherWrapper { 84 | get { return KingfisherWrapper(self) } 85 | set { } 86 | } 87 | } 88 | 89 | extension KFCrossPlatformImage: KingfisherCompatible { } 90 | #if !os(watchOS) 91 | extension KFCrossPlatformImageView: KingfisherCompatible { } 92 | extension KFCrossPlatformButton: KingfisherCompatible { } 93 | extension NSTextAttachment: KingfisherCompatible { } 94 | #else 95 | extension WKInterfaceImage: KingfisherCompatible { } 96 | #endif 97 | 98 | #if os(tvOS) && canImport(TVUIKit) 99 | @available(tvOS 12.0, *) 100 | extension TVMonogramView: KingfisherCompatible { } 101 | #endif 102 | 103 | #if canImport(CarPlay) && !targetEnvironment(macCatalyst) 104 | @available(iOS 14.0, *) 105 | extension CPListItem: KingfisherCompatible { } 106 | #endif 107 | -------------------------------------------------------------------------------- /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 | #if !os(watchOS) 28 | 29 | import CoreImage 30 | 31 | // Reuse the same CI Context for all CI drawing. 32 | private let ciContext = CIContext(options: nil) 33 | 34 | /// Represents the type of transformer method, which will be used in to provide a `Filter`. 35 | public typealias Transformer = (CIImage) -> CIImage? 36 | 37 | /// Represents a processor based on a `CIImage` `Filter`. 38 | /// It requires a filter to create an `ImageProcessor`. 39 | public protocol CIImageProcessor: ImageProcessor { 40 | var filter: Filter { get } 41 | } 42 | 43 | extension CIImageProcessor { 44 | 45 | /// Processes the input `ImageProcessItem` with this processor. 46 | /// 47 | /// - Parameters: 48 | /// - item: Input item which will be processed by `self`. 49 | /// - options: Options when processing the item. 50 | /// - Returns: The processed image. 51 | /// 52 | /// - Note: See documentation of `ImageProcessor` protocol for more. 53 | public func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? { 54 | switch item { 55 | case .image(let image): 56 | return image.kf.apply(filter) 57 | case .data: 58 | return (DefaultImageProcessor.default |> self).process(item: item, options: options) 59 | } 60 | } 61 | } 62 | 63 | /// A wrapper struct for a `Transformer` of CIImage filters. A `Filter` 64 | /// value could be used to create a `CIImage` processor. 65 | public struct Filter { 66 | 67 | let transform: Transformer 68 | 69 | public init(transform: @escaping Transformer) { 70 | self.transform = transform 71 | } 72 | 73 | /// Tint filter which will apply a tint color to images. 74 | public static var tint: (KFCrossPlatformColor) -> Filter = { 75 | color in 76 | Filter { 77 | input in 78 | 79 | let colorFilter = CIFilter(name: "CIConstantColorGenerator")! 80 | colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey) 81 | 82 | let filter = CIFilter(name: "CISourceOverCompositing")! 83 | 84 | let colorImage = colorFilter.outputImage 85 | filter.setValue(colorImage, forKey: kCIInputImageKey) 86 | filter.setValue(input, forKey: kCIInputBackgroundImageKey) 87 | 88 | return filter.outputImage?.cropped(to: input.extent) 89 | } 90 | } 91 | 92 | /// Represents color control elements. It is a tuple of 93 | /// `(brightness, contrast, saturation, inputEV)` 94 | public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat) 95 | 96 | /// Color control filter which will apply color control change to images. 97 | public static var colorControl: (ColorElement) -> Filter = { arg -> Filter in 98 | let (brightness, contrast, saturation, inputEV) = arg 99 | return Filter { input in 100 | let paramsColor = [kCIInputBrightnessKey: brightness, 101 | kCIInputContrastKey: contrast, 102 | kCIInputSaturationKey: saturation] 103 | let blackAndWhite = input.applyingFilter("CIColorControls", parameters: paramsColor) 104 | let paramsExposure = [kCIInputEVKey: inputEV] 105 | return blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure) 106 | } 107 | } 108 | } 109 | 110 | extension KingfisherWrapper where Base: KFCrossPlatformImage { 111 | 112 | /// Applies a `Filter` containing `CIImage` transformer to `self`. 113 | /// 114 | /// - Parameter filter: The filter used to transform `self`. 115 | /// - Returns: A transformed image by input `Filter`. 116 | /// 117 | /// - Note: 118 | /// Only CG-based images are supported. If any error happens 119 | /// during transforming, `self` will be returned. 120 | public func apply(_ filter: Filter) -> KFCrossPlatformImage { 121 | 122 | guard let cgImage = cgImage else { 123 | assertionFailure("[Kingfisher] Tint image only works for CG-based image.") 124 | return base 125 | } 126 | 127 | let inputImage = CIImage(cgImage: cgImage) 128 | guard let outputImage = filter.transform(inputImage) else { 129 | return base 130 | } 131 | 132 | guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { 133 | assertionFailure("[Kingfisher] Can not make an tint image within context.") 134 | return base 135 | } 136 | 137 | #if os(macOS) 138 | return fixedForRetinaPixel(cgImage: result, to: size) 139 | #else 140 | return KFCrossPlatformImage(cgImage: result, scale: base.scale, orientation: base.imageOrientation) 141 | #endif 142 | } 143 | 144 | } 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /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 | public class GIFAnimatedImage { 74 | let images: [KFCrossPlatformImage] 75 | let duration: TimeInterval 76 | 77 | init?(from imageSource: CGImageSource, for info: [String: Any], options: ImageCreatingOptions) { 78 | let frameCount = CGImageSourceGetCount(imageSource) 79 | var images = [KFCrossPlatformImage]() 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 | public 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 | public 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 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/Image/GraphicsContext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GraphicsContext.swift 3 | // Kingfisher 4 | // 5 | // Created by taras on 19/04/2021. 6 | // 7 | // Copyright (c) 2021 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) && !targetEnvironment(macCatalyst) 28 | import AppKit 29 | #endif 30 | #if canImport(UIKit) 31 | import UIKit 32 | #endif 33 | 34 | enum GraphicsContext { 35 | static func begin(size: CGSize, scale: CGFloat) { 36 | #if os(macOS) 37 | NSGraphicsContext.saveGraphicsState() 38 | #else 39 | UIGraphicsBeginImageContextWithOptions(size, false, scale) 40 | #endif 41 | } 42 | 43 | static func current(size: CGSize, scale: CGFloat, inverting: Bool, cgImage: CGImage?) -> CGContext? { 44 | #if os(macOS) 45 | guard let rep = NSBitmapImageRep( 46 | bitmapDataPlanes: nil, 47 | pixelsWide: Int(size.width), 48 | pixelsHigh: Int(size.height), 49 | bitsPerSample: cgImage?.bitsPerComponent ?? 8, 50 | samplesPerPixel: 4, 51 | hasAlpha: true, 52 | isPlanar: false, 53 | colorSpaceName: .calibratedRGB, 54 | bytesPerRow: 0, 55 | bitsPerPixel: 0) else 56 | { 57 | assertionFailure("[Kingfisher] Image representation cannot be created.") 58 | return nil 59 | } 60 | rep.size = size 61 | guard let context = NSGraphicsContext(bitmapImageRep: rep) else { 62 | assertionFailure("[Kingfisher] Image context cannot be created.") 63 | return nil 64 | } 65 | 66 | NSGraphicsContext.current = context 67 | return context.cgContext 68 | #else 69 | guard let context = UIGraphicsGetCurrentContext() else { 70 | return nil 71 | } 72 | if inverting { // If drawing a CGImage, we need to make context flipped. 73 | context.scaleBy(x: 1.0, y: -1.0) 74 | context.translateBy(x: 0, y: -size.height) 75 | } 76 | return context 77 | #endif 78 | } 79 | 80 | static func end() { 81 | #if os(macOS) 82 | NSGraphicsContext.restoreGraphicsState() 83 | #else 84 | UIGraphicsEndImageContext() 85 | #endif 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /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 | let bytes = [UInt8](base) 119 | let markerBytes = marker.bytes 120 | for (index, item) in bytes.enumerated() where bytes.count > index + 1 { 121 | guard 122 | item == markerBytes.first, 123 | bytes[index + 1] == markerBytes[1] else { 124 | continue 125 | } 126 | return true 127 | } 128 | return false 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /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 | import Foundation 28 | #if os(iOS) || os(tvOS) 29 | import UIKit 30 | 31 | /// Transition effect which will be used when an image downloaded and set by `UIImageView` 32 | /// extension API in Kingfisher. You can assign an enum value with transition duration as 33 | /// an item in `KingfisherOptionsInfo` to enable the animation transition. 34 | /// 35 | /// Apple's UIViewAnimationOptions is used under the hood. 36 | /// For custom transition, you should specified your own transition options, animations and 37 | /// completion handler as well. 38 | /// 39 | /// - none: No animation transition. 40 | /// - fade: Fade in the loaded image in a given duration. 41 | /// - flipFromLeft: Flip from left transition. 42 | /// - flipFromRight: Flip from right transition. 43 | /// - flipFromTop: Flip from top transition. 44 | /// - flipFromBottom: Flip from bottom transition. 45 | /// - custom: Custom transition. 46 | public enum ImageTransition { 47 | /// No animation transition. 48 | case none 49 | /// Fade in the loaded image in a given duration. 50 | case fade(TimeInterval) 51 | /// Flip from left transition. 52 | case flipFromLeft(TimeInterval) 53 | /// Flip from right transition. 54 | case flipFromRight(TimeInterval) 55 | /// Flip from top transition. 56 | case flipFromTop(TimeInterval) 57 | /// Flip from bottom transition. 58 | case flipFromBottom(TimeInterval) 59 | /// Custom transition defined by a general animation block. 60 | /// - duration: The time duration of this custom transition. 61 | /// - options: `UIView.AnimationOptions` should be used in the transition. 62 | /// - animations: The animation block will be applied when setting image. 63 | /// - completion: A block called when the transition animation finishes. 64 | case custom(duration: TimeInterval, 65 | options: UIView.AnimationOptions, 66 | animations: ((UIImageView, UIImage) -> Void)?, 67 | completion: ((Bool) -> Void)?) 68 | 69 | var duration: TimeInterval { 70 | switch self { 71 | case .none: return 0 72 | case .fade(let duration): return duration 73 | 74 | case .flipFromLeft(let duration): return duration 75 | case .flipFromRight(let duration): return duration 76 | case .flipFromTop(let duration): return duration 77 | case .flipFromBottom(let duration): return duration 78 | 79 | case .custom(let duration, _, _, _): return duration 80 | } 81 | } 82 | 83 | var animationOptions: UIView.AnimationOptions { 84 | switch self { 85 | case .none: return [] 86 | case .fade: return .transitionCrossDissolve 87 | 88 | case .flipFromLeft: return .transitionFlipFromLeft 89 | case .flipFromRight: return .transitionFlipFromRight 90 | case .flipFromTop: return .transitionFlipFromTop 91 | case .flipFromBottom: return .transitionFlipFromBottom 92 | 93 | case .custom(_, let options, _, _): return options 94 | } 95 | } 96 | 97 | var animations: ((UIImageView, UIImage) -> Void)? { 98 | switch self { 99 | case .custom(_, _, let animations, _): return animations 100 | default: return { $0.image = $1 } 101 | } 102 | } 103 | 104 | var completion: ((Bool) -> Void)? { 105 | switch self { 106 | case .custom(_, _, _, let completion): return completion 107 | default: return nil 108 | } 109 | } 110 | } 111 | #else 112 | // Just a placeholder for compiling on macOS. 113 | public enum ImageTransition { 114 | case none 115 | /// This is a placeholder on macOS now. It is for SwiftUI (KFImage) to identify the fade option only. 116 | case fade(TimeInterval) 117 | } 118 | #endif 119 | -------------------------------------------------------------------------------- /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(watchOS) 28 | 29 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 30 | import AppKit 31 | #endif 32 | 33 | #if canImport(UIKit) 34 | import UIKit 35 | #endif 36 | 37 | /// Represents a placeholder type which could be set while loading as well as 38 | /// loading finished without getting an image. 39 | public protocol Placeholder { 40 | 41 | /// How the placeholder should be added to a given image view. 42 | func add(to imageView: KFCrossPlatformImageView) 43 | 44 | /// How the placeholder should be removed from a given image view. 45 | func remove(from imageView: KFCrossPlatformImageView) 46 | } 47 | 48 | /// Default implementation of an image placeholder. The image will be set or 49 | /// reset directly for `image` property of the image view. 50 | extension KFCrossPlatformImage: Placeholder { 51 | /// How the placeholder should be added to a given image view. 52 | public func add(to imageView: KFCrossPlatformImageView) { imageView.image = self } 53 | 54 | /// How the placeholder should be removed from a given image view. 55 | public func remove(from imageView: KFCrossPlatformImageView) { imageView.image = nil } 56 | } 57 | 58 | /// Default implementation of an arbitrary view as placeholder. The view will be 59 | /// added as a subview when adding and be removed from its super view when removing. 60 | /// 61 | /// To use your customize View type as placeholder, simply let it conforming to 62 | /// `Placeholder` by `extension MyView: Placeholder {}`. 63 | extension Placeholder where Self: KFCrossPlatformView { 64 | 65 | /// How the placeholder should be added to a given image view. 66 | public func add(to imageView: KFCrossPlatformImageView) { 67 | imageView.addSubview(self) 68 | translatesAutoresizingMaskIntoConstraints = false 69 | 70 | centerXAnchor.constraint(equalTo: imageView.centerXAnchor).isActive = true 71 | centerYAnchor.constraint(equalTo: imageView.centerYAnchor).isActive = true 72 | heightAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true 73 | widthAnchor.constraint(equalTo: imageView.widthAnchor).isActive = true 74 | } 75 | 76 | /// How the placeholder should be removed from a given image view. 77 | public func remove(from imageView: KFCrossPlatformImageView) { 78 | removeFromSuperview() 79 | } 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /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 | @available(*, deprecated, message: "Typo. Use `AuthenticationChallengeResponsible` instead", renamed: "AuthenticationChallengeResponsible") 30 | public typealias AuthenticationChallengeResponsable = AuthenticationChallengeResponsible 31 | 32 | /// Protocol indicates that an authentication challenge could be handled. 33 | public protocol AuthenticationChallengeResponsible: AnyObject { 34 | 35 | /// Called when a session level authentication challenge is received. 36 | /// This method provide a chance to handle and response to the authentication 37 | /// challenge before downloading could start. 38 | /// 39 | /// - Parameters: 40 | /// - downloader: The downloader which receives this challenge. 41 | /// - challenge: An object that contains the request for authentication. 42 | /// - completionHandler: A handler that your delegate method must call. 43 | /// 44 | /// - Note: This method is a forward from `URLSessionDelegate.urlSession(:didReceiveChallenge:completionHandler:)`. 45 | /// Please refer to the document of it in `URLSessionDelegate`. 46 | func downloader( 47 | _ downloader: ImageDownloader, 48 | didReceive challenge: URLAuthenticationChallenge, 49 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 50 | 51 | /// Called when a task level authentication challenge is received. 52 | /// This method provide a chance to handle and response to the authentication 53 | /// challenge before downloading could start. 54 | /// 55 | /// - Parameters: 56 | /// - downloader: The downloader which receives this challenge. 57 | /// - task: The task whose request requires authentication. 58 | /// - challenge: An object that contains the request for authentication. 59 | /// - completionHandler: A handler that your delegate method must call. 60 | func downloader( 61 | _ downloader: ImageDownloader, 62 | task: URLSessionTask, 63 | didReceive challenge: URLAuthenticationChallenge, 64 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 65 | } 66 | 67 | extension AuthenticationChallengeResponsible { 68 | 69 | public func downloader( 70 | _ downloader: ImageDownloader, 71 | didReceive challenge: URLAuthenticationChallenge, 72 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 73 | { 74 | if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 75 | if let trustedHosts = downloader.trustedHosts, trustedHosts.contains(challenge.protectionSpace.host) { 76 | let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!) 77 | completionHandler(.useCredential, credential) 78 | return 79 | } 80 | } 81 | 82 | completionHandler(.performDefaultHandling, nil) 83 | } 84 | 85 | public func downloader( 86 | _ downloader: ImageDownloader, 87 | task: URLSessionTask, 88 | didReceive challenge: URLAuthenticationChallenge, 89 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 90 | { 91 | completionHandler(.performDefaultHandling, nil) 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /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: KFCrossPlatformImage]() 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 | let finalImage = callback.options.backgroundDecode ? image.kf.decoded : image 65 | result = .success(finalImage) 66 | } else { 67 | let error = KingfisherError.processorError( 68 | reason: .processingFailed(processor: processor, item: .data(data))) 69 | result = .failure(error) 70 | } 71 | onImageProcessed.call((result, callback)) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /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 between cache serialization and the actual use of 30 | /// the image. The `modify(_:)` method will be called after the image retrieved from its source and before it returned 31 | /// to the caller. This modified image is expected to be only used for rendering purpose, any changes applied by the 32 | /// `ImageModifier` will not be serialized or cached. 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: KFCrossPlatformImage) -> KFCrossPlatformImage 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: (KFCrossPlatformImage) throws -> KFCrossPlatformImage 54 | 55 | /// Creates an `AnyImageModifier` with a given `modify` block. 56 | public init(modify: @escaping (KFCrossPlatformImage) throws -> KFCrossPlatformImage) { 57 | block = modify 58 | } 59 | 60 | /// Modify an input `Image`. See `ImageModifier` protocol for more. 61 | public func modify(_ image: KFCrossPlatformImage) -> KFCrossPlatformImage { 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: KFCrossPlatformImage) -> KFCrossPlatformImage { 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: KFCrossPlatformImage) -> KFCrossPlatformImage { 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: KFCrossPlatformImage) -> KFCrossPlatformImage { 113 | return image.withAlignmentRectInsets(alignmentInsets) 114 | } 115 | } 116 | #endif 117 | -------------------------------------------------------------------------------- /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 a redirect request modify block. 56 | public struct AnyRedirectHandler: ImageDownloadRedirectHandler { 57 | 58 | let block: (SessionDataTask, HTTPURLResponse, URLRequest, @escaping (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, @escaping (URLRequest?) -> Void) -> Void) { 74 | block = handle 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /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 in an asynchronous way. 30 | public protocol AsyncImageDownloadRequestModifier { 31 | 32 | /// This 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 | /// When you have done with the modification, call the `reportModified` block with the modified request and the data 36 | /// download will happen with this request. 37 | /// 38 | /// Usually, you pass an `AsyncImageDownloadRequestModifier` as the associated value of 39 | /// `KingfisherOptionsInfoItem.requestModifier` and use it as the `options` parameter in related methods. 40 | /// 41 | /// If you do nothing with the input `request` and return it as is, a downloading process will start with it. 42 | /// 43 | /// - Parameters: 44 | /// - request: The input request contains necessary information like `url`. This request is generated 45 | /// according to your resource url as a GET request. 46 | /// - reportModified: The callback block you need to call after the asynchronous modifying done. 47 | /// 48 | func modified(for request: URLRequest, reportModified: @escaping (URLRequest?) -> Void) 49 | 50 | /// A block will be called when the download task started. 51 | /// 52 | /// If an `AsyncImageDownloadRequestModifier` and the asynchronous modification happens before the download, the 53 | /// related download method will not return a valid `DownloadTask` value. Instead, you can get one from this method. 54 | var onDownloadTaskStarted: ((DownloadTask?) -> Void)? { get } 55 | } 56 | 57 | /// Represents and wraps a method for modifying request before an image download request starts. 58 | public protocol ImageDownloadRequestModifier: AsyncImageDownloadRequestModifier { 59 | 60 | /// This method will be called just before the `request` being sent. 61 | /// This is the last chance you can modify the image download request. You can modify the request for some 62 | /// customizing purpose, such as adding auth token to the header, do basic HTTP auth or something like url mapping. 63 | /// 64 | /// Usually, you pass an `ImageDownloadRequestModifier` as the associated value of 65 | /// `KingfisherOptionsInfoItem.requestModifier` and use it as the `options` parameter in related methods. 66 | /// 67 | /// If you do nothing with the input `request` and return it as is, a downloading process will start with it. 68 | /// 69 | /// - Parameter request: The input request contains necessary information like `url`. This request is generated 70 | /// according to your resource url as a GET request. 71 | /// - Returns: A modified version of request, which you wish to use for downloading an image. If `nil` returned, 72 | /// a `KingfisherError.requestError` with `.emptyRequest` as its reason will occur. 73 | /// 74 | func modified(for request: URLRequest) -> URLRequest? 75 | } 76 | 77 | extension ImageDownloadRequestModifier { 78 | public func modified(for request: URLRequest, reportModified: @escaping (URLRequest?) -> Void) { 79 | let request = modified(for: request) 80 | reportModified(request) 81 | } 82 | 83 | /// This is `nil` for a sync `ImageDownloadRequestModifier` by default. You can get the `DownloadTask` from the 84 | /// return value of downloader method. 85 | public var onDownloadTaskStarted: ((DownloadTask?) -> Void)? { return nil } 86 | } 87 | 88 | /// A wrapper for creating an `ImageDownloadRequestModifier` easier. 89 | /// This type conforms to `ImageDownloadRequestModifier` and wraps an image modify block. 90 | public struct AnyModifier: ImageDownloadRequestModifier { 91 | 92 | let block: (URLRequest) -> URLRequest? 93 | 94 | /// For `ImageDownloadRequestModifier` conformation. 95 | public func modified(for request: URLRequest) -> URLRequest? { 96 | return block(request) 97 | } 98 | 99 | /// Creates a value of `ImageDownloadRequestModifier` which runs `modify` block. 100 | /// 101 | /// - Parameter modify: The request modifying block runs when a request modifying task comes. 102 | /// The return `URLRequest?` value of this block will be used as the image download request. 103 | /// If `nil` returned, a `KingfisherError.requestError` with `.emptyRequest` as its 104 | /// reason will occur. 105 | public init(modify: @escaping (URLRequest) -> URLRequest?) { 106 | block = modify 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/Networking/RetryStrategy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RetryStrategy.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2020/05/04. 6 | // 7 | // Copyright (c) 2020 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 retry context which could be used to determine the current retry status. 30 | public class RetryContext { 31 | 32 | /// The source from which the target image should be retrieved. 33 | public let source: Source 34 | 35 | /// The last error which caused current retry behavior. 36 | public let error: KingfisherError 37 | 38 | /// The retried count before current retry happens. This value is `0` if the current retry is for the first time. 39 | public var retriedCount: Int 40 | 41 | /// A user set value for passing any other information during the retry. If you choose to use `RetryDecision.retry` 42 | /// as the retry decision for `RetryStrategy.retry(context:retryHandler:)`, the associated value of 43 | /// `RetryDecision.retry` will be delivered to you in the next retry. 44 | public internal(set) var userInfo: Any? = nil 45 | 46 | init(source: Source, error: KingfisherError) { 47 | self.source = source 48 | self.error = error 49 | self.retriedCount = 0 50 | } 51 | 52 | @discardableResult 53 | func increaseRetryCount() -> RetryContext { 54 | retriedCount += 1 55 | return self 56 | } 57 | } 58 | 59 | /// Represents decision of behavior on the current retry. 60 | public enum RetryDecision { 61 | /// A retry should happen. The associated `userInfo` will be pass to the next retry in the `RetryContext` parameter. 62 | case retry(userInfo: Any?) 63 | /// There should be no more retry attempt. The image retrieving process will fail with an error. 64 | case stop 65 | } 66 | 67 | /// Defines a retry strategy can be applied to a `.retryStrategy` option. 68 | public protocol RetryStrategy { 69 | 70 | /// Kingfisher calls this method if an error happens during the image retrieving process from a `KingfisherManager`. 71 | /// You implement this method to provide necessary logic based on the `context` parameter. Then you need to call 72 | /// `retryHandler` to pass the retry decision back to Kingfisher. 73 | /// 74 | /// - Parameters: 75 | /// - context: The retry context containing information of current retry attempt. 76 | /// - retryHandler: A block you need to call with a decision of whether the retry should happen or not. 77 | func retry(context: RetryContext, retryHandler: @escaping (RetryDecision) -> Void) 78 | } 79 | 80 | /// A retry strategy that guides Kingfisher to retry when a `.responseError` happens, with a specified max retry count 81 | /// and a certain interval mechanism. 82 | public struct DelayRetryStrategy: RetryStrategy { 83 | 84 | /// Represents the interval mechanism which used in a `DelayRetryStrategy`. 85 | public enum Interval { 86 | /// The next retry attempt should happen in fixed seconds. For example, if the associated value is 3, the 87 | /// attempts happens after 3 seconds after the previous decision is made. 88 | case seconds(TimeInterval) 89 | /// The next retry attempt should happen in an accumulated duration. For example, if the associated value is 3, 90 | /// the attempts happens with interval of 3, 6, 9, 12, ... seconds. 91 | case accumulated(TimeInterval) 92 | /// Uses a block to determine the next interval. The current retry count is given as a parameter. 93 | case custom(block: (_ retriedCount: Int) -> TimeInterval) 94 | 95 | func timeInterval(for retriedCount: Int) -> TimeInterval { 96 | let retryAfter: TimeInterval 97 | switch self { 98 | case .seconds(let interval): 99 | retryAfter = interval 100 | case .accumulated(let interval): 101 | retryAfter = Double(retriedCount + 1) * interval 102 | case .custom(let block): 103 | retryAfter = block(retriedCount) 104 | } 105 | return retryAfter 106 | } 107 | } 108 | 109 | /// The max retry count defined for the retry strategy 110 | public let maxRetryCount: Int 111 | 112 | /// The retry interval mechanism defined for the retry strategy. 113 | public let retryInterval: Interval 114 | 115 | /// Creates a delay retry strategy. 116 | /// - Parameters: 117 | /// - maxRetryCount: The max retry count. 118 | /// - retryInterval: The retry interval mechanism. By default, `.seconds(3)` is used to provide a constant retry 119 | /// interval. 120 | public init(maxRetryCount: Int, retryInterval: Interval = .seconds(3)) { 121 | self.maxRetryCount = maxRetryCount 122 | self.retryInterval = retryInterval 123 | } 124 | 125 | public func retry(context: RetryContext, retryHandler: @escaping (RetryDecision) -> Void) { 126 | // Retry count exceeded. 127 | guard context.retriedCount < maxRetryCount else { 128 | retryHandler(.stop) 129 | return 130 | } 131 | 132 | // User cancel the task. No retry. 133 | guard !context.error.isTaskCancelled else { 134 | retryHandler(.stop) 135 | return 136 | } 137 | 138 | // Only retry for a response error. 139 | guard case KingfisherError.responseError = context.error else { 140 | retryHandler(.stop) 141 | return 142 | } 143 | 144 | let interval = retryInterval.timeInterval(for: context.retriedCount) 145 | if interval == 0 { 146 | retryHandler(.retry(userInfo: nil)) 147 | } else { 148 | DispatchQueue.main.asyncAfter(deadline: .now() + interval) { 149 | retryHandler(.retry(userInfo: nil)) 150 | } 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /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 | // This is a copy of `task.originalRequest?.url`. It is for getting a race-safe behavior for a pitfall on iOS 13. 45 | // Ref: https://github.com/onevcat/Kingfisher/issues/1511 46 | public let originalURL: URL? 47 | 48 | /// The underlying download task. It is only for debugging purpose when you encountered an error. You should not 49 | /// modify the content of this task or start it yourself. 50 | public let task: URLSessionDataTask 51 | private var callbacksStore = [CancelToken: TaskCallback]() 52 | 53 | var callbacks: [SessionDataTask.TaskCallback] { 54 | lock.lock() 55 | defer { lock.unlock() } 56 | return Array(callbacksStore.values) 57 | } 58 | 59 | private var currentToken = 0 60 | private let lock = NSLock() 61 | 62 | let onTaskDone = Delegate<(Result<(Data, URLResponse?), KingfisherError>, [TaskCallback]), Void>() 63 | let onCallbackCancelled = Delegate<(CancelToken, TaskCallback), Void>() 64 | 65 | var started = false 66 | var containsCallbacks: Bool { 67 | // We should be able to use `task.state != .running` to check it. 68 | // However, in some rare cases, cancelling the task does not change 69 | // task state to `.cancelling` immediately, but still in `.running`. 70 | // So we need to check callbacks count to for sure that it is safe to remove the 71 | // task in delegate. 72 | return !callbacks.isEmpty 73 | } 74 | 75 | init(task: URLSessionDataTask) { 76 | self.task = task 77 | self.originalURL = task.originalRequest?.url 78 | mutableData = Data() 79 | } 80 | 81 | func addCallback(_ callback: TaskCallback) -> CancelToken { 82 | lock.lock() 83 | defer { lock.unlock() } 84 | callbacksStore[currentToken] = callback 85 | defer { currentToken += 1 } 86 | return currentToken 87 | } 88 | 89 | func removeCallback(_ token: CancelToken) -> TaskCallback? { 90 | lock.lock() 91 | defer { lock.unlock() } 92 | if let callback = callbacksStore[token] { 93 | callbacksStore[token] = nil 94 | return callback 95 | } 96 | return nil 97 | } 98 | 99 | func resume() { 100 | guard !started else { return } 101 | started = true 102 | task.resume() 103 | } 104 | 105 | func cancel(token: CancelToken) { 106 | guard let callback = removeCallback(token) else { 107 | return 108 | } 109 | onCallbackCancelled.call((token, callback)) 110 | } 111 | 112 | func forceCancel() { 113 | for token in callbacksStore.keys { 114 | cancel(token: token) 115 | } 116 | } 117 | 118 | func didReceiveData(_ data: Data) { 119 | mutableData.append(data) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/SwiftUI/ImageBinder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageBinder.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2019/06/27. 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(SwiftUI) && canImport(Combine) 28 | import SwiftUI 29 | import Combine 30 | 31 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 32 | extension KFImage { 33 | 34 | /// Represents a binder for `KFImage`. It takes responsibility as an `ObjectBinding` and performs 35 | /// image downloading and progress reporting based on `KingfisherManager`. 36 | class ImageBinder: ObservableObject { 37 | 38 | init() {} 39 | 40 | var downloadTask: DownloadTask? 41 | private var loading = false 42 | 43 | var loadingOrSucceeded: Bool { 44 | return loading || loadedImage != nil 45 | } 46 | 47 | // Do not use @Published due to https://github.com/onevcat/Kingfisher/issues/1717. Revert to @Published once 48 | // we can drop iOS 12. 49 | var loaded = false { willSet { objectWillChange.send() } } 50 | var loadedImage: KFCrossPlatformImage? = nil { willSet { objectWillChange.send() } } 51 | var progress: Progress = .init() { willSet { objectWillChange.send() } } 52 | 53 | func start(context: Context) { 54 | guard let source = context.source else { 55 | CallbackQueue.mainCurrentOrAsync.execute { 56 | context.onFailureDelegate.call(KingfisherError.imageSettingError(reason: .emptySource)) 57 | } 58 | return 59 | } 60 | 61 | loading = true 62 | 63 | progress = .init() 64 | downloadTask = KingfisherManager.shared 65 | .retrieveImage( 66 | with: source, 67 | options: context.options, 68 | progressBlock: { size, total in 69 | self.updateProgress(downloaded: size, total: total) 70 | context.onProgressDelegate.call((size, total)) 71 | }, 72 | completionHandler: { [weak self] result in 73 | 74 | guard let self = self else { return } 75 | 76 | CallbackQueue.mainCurrentOrAsync.execute { 77 | self.downloadTask = nil 78 | self.loading = false 79 | } 80 | 81 | switch result { 82 | case .success(let value): 83 | CallbackQueue.mainCurrentOrAsync.execute { 84 | if let fadeDuration = context.fadeTransitionDuration(cacheType: value.cacheType) { 85 | let animation = Animation.linear(duration: fadeDuration) 86 | withAnimation(animation) { self.loaded = true } 87 | } else { 88 | self.loaded = true 89 | } 90 | self.loadedImage = value.image 91 | } 92 | 93 | CallbackQueue.mainAsync.execute { 94 | context.onSuccessDelegate.call(value) 95 | } 96 | case .failure(let error): 97 | CallbackQueue.mainCurrentOrAsync.execute { 98 | if let image = context.options.onFailureImage { 99 | self.loadedImage = image 100 | } 101 | self.loaded = true 102 | } 103 | 104 | CallbackQueue.mainAsync.execute { 105 | context.onFailureDelegate.call(error) 106 | } 107 | } 108 | }) 109 | } 110 | 111 | private func updateProgress(downloaded: Int64, total: Int64) { 112 | progress.totalUnitCount = total 113 | progress.completedUnitCount = downloaded 114 | objectWillChange.send() 115 | } 116 | 117 | /// Cancels the download task if it is in progress. 118 | func cancel() { 119 | downloadTask?.cancel() 120 | downloadTask = nil 121 | loading = false 122 | } 123 | } 124 | } 125 | #endif 126 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/SwiftUI/ImageContext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageContext.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2021/05/08. 6 | // 7 | // Copyright (c) 2021 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(SwiftUI) && canImport(Combine) 28 | import SwiftUI 29 | import Combine 30 | 31 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 32 | extension KFImage { 33 | public class Context { 34 | let source: Source? 35 | var options = KingfisherParsedOptionsInfo( 36 | KingfisherManager.shared.defaultOptions + [.loadDiskFileSynchronously] 37 | ) 38 | 39 | var configurations: [(HoldingView) -> HoldingView] = [] 40 | var renderConfigurations: [(HoldingView.RenderingView) -> Void] = [] 41 | 42 | var cancelOnDisappear: Bool = false 43 | var placeholder: ((Progress) -> AnyView)? = nil 44 | 45 | let onFailureDelegate = Delegate() 46 | let onSuccessDelegate = Delegate() 47 | let onProgressDelegate = Delegate<(Int64, Int64), Void>() 48 | 49 | init(source: Source?) { 50 | self.source = source 51 | } 52 | 53 | func shouldApplyFade(cacheType: CacheType) -> Bool { 54 | options.forceTransition || cacheType == .none 55 | } 56 | 57 | func fadeTransitionDuration(cacheType: CacheType) -> TimeInterval? { 58 | shouldApplyFade(cacheType: cacheType) 59 | ? options.transition.fadeDuration 60 | : nil 61 | } 62 | } 63 | } 64 | 65 | extension ImageTransition { 66 | // Only for fade effect in SwiftUI. 67 | fileprivate var fadeDuration: TimeInterval? { 68 | switch self { 69 | case .fade(let duration): 70 | return duration 71 | default: 72 | return nil 73 | } 74 | } 75 | } 76 | 77 | 78 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 79 | extension KFImage.Context: Hashable { 80 | public static func == (lhs: KFImage.Context, rhs: KFImage.Context) -> Bool { 81 | lhs.source == rhs.source && 82 | lhs.options.processor.identifier == rhs.options.processor.identifier 83 | } 84 | 85 | public func hash(into hasher: inout Hasher) { 86 | hasher.combine(source) 87 | hasher.combine(options.processor.identifier) 88 | } 89 | } 90 | 91 | #if canImport(UIKit) && !os(watchOS) 92 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 93 | extension KFAnimatedImage { 94 | public typealias Context = KFImage.Context 95 | typealias ImageBinder = KFImage.ImageBinder 96 | } 97 | #endif 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/SwiftUI/KFAnimatedImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KFAnimatedImage.swift 3 | // Kingfisher 4 | // 5 | // Created by wangxingbin on 2021/4/29. 6 | // 7 | // Copyright (c) 2021 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(SwiftUI) && canImport(Combine) && canImport(UIKit) && !os(watchOS) 28 | import SwiftUI 29 | import Combine 30 | 31 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 32 | public struct KFAnimatedImage: KFImageProtocol { 33 | public typealias HoldingView = KFAnimatedImageViewRepresenter 34 | public var context: Context 35 | public init(context: KFImage.Context) { 36 | self.context = context 37 | } 38 | 39 | /// Configures current rendering view with a `block`. This block will be applied when the under-hood 40 | /// `AnimatedImageView` is created in `UIViewRepresentable.makeUIView(context:)` 41 | /// 42 | /// - Parameter block: The block applies to the animated image view. 43 | /// - Returns: A `KFAnimatedImage` view that being configured by the `block`. 44 | public func configure(_ block: @escaping (HoldingView.RenderingView) -> Void) -> Self { 45 | context.renderConfigurations.append(block) 46 | return self 47 | } 48 | } 49 | 50 | /// A wrapped `UIViewRepresentable` of `AnimatedImageView` 51 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 52 | public struct KFAnimatedImageViewRepresenter: UIViewRepresentable, KFImageHoldingView { 53 | public typealias RenderingView = AnimatedImageView 54 | public static func created(from image: KFCrossPlatformImage?, context: KFImage.Context) -> KFAnimatedImageViewRepresenter { 55 | KFAnimatedImageViewRepresenter(image: image, context: context) 56 | } 57 | 58 | var image: KFCrossPlatformImage? 59 | let context: KFImage.Context 60 | 61 | public func makeUIView(context: Context) -> AnimatedImageView { 62 | let view = AnimatedImageView() 63 | 64 | self.context.renderConfigurations.forEach { $0(view) } 65 | 66 | view.image = image 67 | 68 | // Allow SwiftUI scale (fit/fill) working fine. 69 | view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) 70 | view.setContentCompressionResistancePriority(.defaultLow, for: .vertical) 71 | return view 72 | } 73 | 74 | public func updateUIView(_ uiView: AnimatedImageView, context: Context) { 75 | uiView.image = image 76 | } 77 | 78 | } 79 | 80 | #if DEBUG 81 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 82 | struct KFAnimatedImage_Previews : PreviewProvider { 83 | static var previews: some View { 84 | Group { 85 | KFAnimatedImage(source: .network(URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher-TestImages/master/DemoAppImage/GIF/1.gif")!)) 86 | .onSuccess { r in 87 | print(r) 88 | } 89 | .placeholder { 90 | ProgressView() 91 | } 92 | .padding() 93 | } 94 | } 95 | } 96 | #endif 97 | #endif 98 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/SwiftUI/KFImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KFImage.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2019/06/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 | #if canImport(SwiftUI) && canImport(Combine) 28 | import SwiftUI 29 | import Combine 30 | 31 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 32 | public struct KFImage: KFImageProtocol { 33 | public var context: Context 34 | public init(context: Context) { 35 | self.context = context 36 | } 37 | } 38 | 39 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 40 | extension Image: KFImageHoldingView { 41 | public typealias RenderingView = Image 42 | public static func created(from image: KFCrossPlatformImage?, context: KFImage.Context) -> Image { 43 | Image(crossPlatformImage: image) 44 | } 45 | } 46 | 47 | // MARK: - Image compatibility. 48 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 49 | extension KFImage { 50 | 51 | public func resizable( 52 | capInsets: EdgeInsets = EdgeInsets(), 53 | resizingMode: Image.ResizingMode = .stretch) -> KFImage 54 | { 55 | configure { $0.resizable(capInsets: capInsets, resizingMode: resizingMode) } 56 | } 57 | 58 | public func renderingMode(_ renderingMode: Image.TemplateRenderingMode?) -> KFImage { 59 | configure { $0.renderingMode(renderingMode) } 60 | } 61 | 62 | public func interpolation(_ interpolation: Image.Interpolation) -> KFImage { 63 | configure { $0.interpolation(interpolation) } 64 | } 65 | 66 | public func antialiased(_ isAntialiased: Bool) -> KFImage { 67 | configure { $0.antialiased(isAntialiased) } 68 | } 69 | 70 | /// Starts the loading process of `self` immediately. 71 | /// 72 | /// By default, a `KFImage` will not load its source until the `onAppear` is called. This is a lazily loading 73 | /// behavior and provides better performance. However, when you refresh the view, the lazy loading also causes a 74 | /// flickering since the loading does not happen immediately. Call this method if you want to start the load at once 75 | /// could help avoiding the flickering, with some performance trade-off. 76 | /// 77 | /// - Deprecated: This is not necessary anymore since `@StateObject` is used for holding the image data. 78 | /// It does nothing now and please just remove it. 79 | /// 80 | /// - Returns: The `Self` value with changes applied. 81 | @available(*, deprecated, message: "This is not necessary anymore since `@StateObject` is used. It does nothing now and please just remove it.") 82 | public func loadImmediately(_ start: Bool = true) -> KFImage { 83 | return self 84 | } 85 | } 86 | 87 | #if DEBUG 88 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 89 | struct KFImage_Previews : PreviewProvider { 90 | static var previews: some View { 91 | Group { 92 | KFImage.url(URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/logo.png")!) 93 | .onSuccess { r in 94 | print(r) 95 | } 96 | .placeholder { p in 97 | ProgressView(p) 98 | } 99 | .resizable() 100 | .aspectRatio(contentMode: .fit) 101 | .padding() 102 | } 103 | } 104 | } 105 | #endif 106 | #endif 107 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/SwiftUI/KFImageOptions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KFImageOptions.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2020/12/20. 6 | // 7 | // Copyright (c) 2020 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(SwiftUI) && canImport(Combine) 28 | import SwiftUI 29 | import Combine 30 | 31 | // MARK: - KFImage creating. 32 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 33 | extension KFImageProtocol { 34 | 35 | /// Creates a `KFImage` for a given `Source`. 36 | /// - Parameters: 37 | /// - source: The `Source` object defines data information from network or a data provider. 38 | /// - Returns: A `KFImage` for future configuration or embedding to a `SwiftUI.View`. 39 | public static func source( 40 | _ source: Source? 41 | ) -> Self 42 | { 43 | Self.init(source: source) 44 | } 45 | 46 | /// Creates a `KFImage` for a given `Resource`. 47 | /// - Parameters: 48 | /// - source: The `Resource` object defines data information like key or URL. 49 | /// - Returns: A `KFImage` for future configuration or embedding to a `SwiftUI.View`. 50 | public static func resource( 51 | _ resource: Resource? 52 | ) -> Self 53 | { 54 | source(resource?.convertToSource()) 55 | } 56 | 57 | /// Creates a `KFImage` for a given `URL`. 58 | /// - Parameters: 59 | /// - url: The URL where the image should be downloaded. 60 | /// - cacheKey: The key used to store the downloaded image in cache. 61 | /// If `nil`, the `absoluteString` of `url` is used as the cache key. 62 | /// - Returns: A `KFImage` for future configuration or embedding to a `SwiftUI.View`. 63 | public static func url( 64 | _ url: URL?, cacheKey: String? = nil 65 | ) -> Self 66 | { 67 | source(url?.convertToSource(overrideCacheKey: cacheKey)) 68 | } 69 | 70 | /// Creates a `KFImage` for a given `ImageDataProvider`. 71 | /// - Parameters: 72 | /// - provider: The `ImageDataProvider` object contains information about the data. 73 | /// - Returns: A `KFImage` for future configuration or embedding to a `SwiftUI.View`. 74 | public static func dataProvider( 75 | _ provider: ImageDataProvider? 76 | ) -> Self 77 | { 78 | source(provider?.convertToSource()) 79 | } 80 | 81 | /// Creates a builder for some given raw data and a cache key. 82 | /// - Parameters: 83 | /// - data: The data object from which the image should be created. 84 | /// - cacheKey: The key used to store the downloaded image in cache. 85 | /// - Returns: A `KFImage` for future configuration or embedding to a `SwiftUI.View`. 86 | public static func data( 87 | _ data: Data?, cacheKey: String 88 | ) -> Self 89 | { 90 | if let data = data { 91 | return dataProvider(RawImageDataProvider(data: data, cacheKey: cacheKey)) 92 | } else { 93 | return dataProvider(nil) 94 | } 95 | } 96 | } 97 | 98 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 99 | extension KFImageProtocol { 100 | /// Sets a placeholder `View` which shows when loading the image, with a progress parameter as input. 101 | /// - Parameter content: A view that describes the placeholder. 102 | /// - Returns: A `KFImage` view that contains `content` as its placeholder. 103 | public func placeholder(@ViewBuilder _ content: @escaping (Progress) -> P) -> Self { 104 | context.placeholder = { progress in 105 | return AnyView(content(progress)) 106 | } 107 | return self 108 | } 109 | 110 | /// Sets a placeholder `View` which shows when loading the image. 111 | /// - Parameter content: A view that describes the placeholder. 112 | /// - Returns: A `KFImage` view that contains `content` as its placeholder. 113 | public func placeholder(@ViewBuilder _ content: @escaping () -> P) -> Self { 114 | placeholder { _ in content() } 115 | } 116 | 117 | /// Sets cancelling the download task bound to `self` when the view disappearing. 118 | /// - Parameter flag: Whether cancel the task or not. 119 | /// - Returns: A `KFImage` view that cancels downloading task when disappears. 120 | public func cancelOnDisappear(_ flag: Bool) -> Self { 121 | context.cancelOnDisappear = flag 122 | return self 123 | } 124 | 125 | /// Sets a fade transition for the image task. 126 | /// - Parameter duration: The duration of the fade transition. 127 | /// - Returns: A `KFImage` with changes applied. 128 | /// 129 | /// Kingfisher will use the fade transition to animate the image in if it is downloaded from web. 130 | /// The transition will not happen when the 131 | /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when 132 | /// the image being retrieved from cache, also call `forceRefresh()` on the returned `KFImage`. 133 | public func fade(duration: TimeInterval) -> Self { 134 | context.options.transition = .fade(duration) 135 | return self 136 | } 137 | } 138 | #endif 139 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/SwiftUI/KFImageProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KFImageProtocol.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2021/05/08. 6 | // 7 | // Copyright (c) 2021 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(SwiftUI) && canImport(Combine) 28 | import SwiftUI 29 | import Combine 30 | 31 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 32 | public protocol KFImageProtocol: View, KFOptionSetter { 33 | associatedtype HoldingView: KFImageHoldingView 34 | var context: KFImage.Context { get set } 35 | init(context: KFImage.Context) 36 | } 37 | 38 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 39 | extension KFImageProtocol { 40 | public var body: some View { 41 | ZStack { 42 | KFImageRenderer( 43 | context: context 44 | ).id(context) 45 | } 46 | } 47 | 48 | /// Creates a Kingfisher compatible image view to load image from the given `Source`. 49 | /// - Parameters: 50 | /// - source: The image `Source` defining where to load the target image. 51 | public init(source: Source?) { 52 | let context = KFImage.Context(source: source) 53 | self.init(context: context) 54 | } 55 | 56 | /// Creates a Kingfisher compatible image view to load image from the given `URL`. 57 | /// - Parameters: 58 | /// - source: The image `Source` defining where to load the target image. 59 | public init(_ url: URL?) { 60 | self.init(source: url?.convertToSource()) 61 | } 62 | 63 | /// Configures current image with a `block`. This block will be lazily applied when creating the final `Image`. 64 | /// - Parameter block: The block applies to loaded image. 65 | /// - Returns: A `KFImage` view that configures internal `Image` with `block`. 66 | public func configure(_ block: @escaping (HoldingView) -> HoldingView) -> Self { 67 | context.configurations.append(block) 68 | return self 69 | } 70 | } 71 | 72 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 73 | public protocol KFImageHoldingView: View { 74 | associatedtype RenderingView 75 | static func created(from image: KFCrossPlatformImage?, context: KFImage.Context) -> Self 76 | } 77 | 78 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 79 | extension KFImageProtocol { 80 | public var options: KingfisherParsedOptionsInfo { 81 | get { context.options } 82 | nonmutating set { context.options = newValue } 83 | } 84 | 85 | public var onFailureDelegate: Delegate { context.onFailureDelegate } 86 | public var onSuccessDelegate: Delegate { context.onSuccessDelegate } 87 | public var onProgressDelegate: Delegate<(Int64, Int64), Void> { context.onProgressDelegate } 88 | 89 | public var delegateObserver: AnyObject { context } 90 | } 91 | 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /Pods/Kingfisher/Sources/SwiftUI/KFImageRenderer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KFImageRenderer.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2021/05/08. 6 | // 7 | // Copyright (c) 2021 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(SwiftUI) && canImport(Combine) 28 | import SwiftUI 29 | import Combine 30 | 31 | /// A Kingfisher compatible SwiftUI `View` to load an image from a `Source`. 32 | /// Declaring a `KFImage` in a `View`'s body to trigger loading from the given `Source`. 33 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 34 | struct KFImageRenderer : View where HoldingView: KFImageHoldingView { 35 | 36 | @StateObject var binder: KFImage.ImageBinder = .init() 37 | let context: KFImage.Context 38 | 39 | var body: some View { 40 | ZStack { 41 | context.configurations 42 | .reduce(HoldingView.created(from: binder.loadedImage, context: context)) { 43 | current, config in config(current) 44 | } 45 | .opacity(binder.loaded ? 1.0 : 0.0) 46 | if binder.loadedImage == nil { 47 | Group { 48 | if let placeholder = context.placeholder, let view = placeholder(binder.progress) { 49 | view 50 | } else { 51 | Color.clear 52 | } 53 | } 54 | .onAppear { [weak binder = self.binder] in 55 | guard let binder = binder else { 56 | return 57 | } 58 | if !binder.loadingOrSucceeded { 59 | binder.start(context: context) 60 | } 61 | } 62 | .onDisappear { [weak binder = self.binder] in 63 | guard let binder = binder else { 64 | return 65 | } 66 | if context.cancelOnDisappear { 67 | binder.cancel() 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 76 | extension Image { 77 | // Creates an Image with either UIImage or NSImage. 78 | init(crossPlatformImage: KFCrossPlatformImage?) { 79 | #if canImport(UIKit) 80 | self.init(uiImage: crossPlatformImage ?? KFCrossPlatformImage()) 81 | #elseif canImport(AppKit) 82 | self.init(nsImage: crossPlatformImage ?? KFCrossPlatformImage()) 83 | #endif 84 | } 85 | } 86 | 87 | #if canImport(UIKit) 88 | @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) 89 | extension UIImage.Orientation { 90 | func toSwiftUI() -> Image.Orientation { 91 | switch self { 92 | case .down: return .down 93 | case .up: return .up 94 | case .left: return .left 95 | case .right: return .right 96 | case .upMirrored: return .upMirrored 97 | case .downMirrored: return .downMirrored 98 | case .leftMirrored: return .leftMirrored 99 | case .rightMirrored: return .rightMirrored 100 | @unknown default: return .up 101 | } 102 | } 103 | } 104 | #endif 105 | #endif 106 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | public typealias ExecutionQueue = CallbackQueue 30 | 31 | /// Represents callback queue behaviors when an calling of closure be dispatched. 32 | /// 33 | /// - asyncMain: Dispatch the calling to `DispatchQueue.main` with an `async` behavior. 34 | /// - currentMainOrAsync: Dispatch the calling to `DispatchQueue.main` with an `async` behavior if current queue is not 35 | /// `.main`. Otherwise, call the closure immediately in current main queue. 36 | /// - untouch: Do not change the calling queue for closure. 37 | /// - dispatch: Dispatches to a specified `DispatchQueue`. 38 | public enum CallbackQueue { 39 | /// Dispatch the calling to `DispatchQueue.main` with an `async` behavior. 40 | case mainAsync 41 | /// Dispatch the calling to `DispatchQueue.main` with an `async` behavior if current queue is not 42 | /// `.main`. Otherwise, call the closure immediately in current main queue. 43 | case mainCurrentOrAsync 44 | /// Do not change the calling queue for closure. 45 | case untouch 46 | /// Dispatches to a specified `DispatchQueue`. 47 | case dispatch(DispatchQueue) 48 | 49 | public func execute(_ block: @escaping () -> Void) { 50 | switch self { 51 | case .mainAsync: 52 | DispatchQueue.main.async { block() } 53 | case .mainCurrentOrAsync: 54 | DispatchQueue.main.safeAsync { block() } 55 | case .untouch: 56 | block() 57 | case .dispatch(let queue): 58 | queue.async { block() } 59 | } 60 | } 61 | 62 | var queue: DispatchQueue { 63 | switch self { 64 | case .mainAsync: return .main 65 | case .mainCurrentOrAsync: return .main 66 | case .untouch: return OperationQueue.current?.underlyingQueue ?? .main 67 | case .dispatch(let queue): return queue 68 | } 69 | } 70 | } 71 | 72 | extension DispatchQueue { 73 | // This method will dispatch the `block` to self. 74 | // If `self` is the main queue, and current thread is main thread, the block 75 | // will be invoked immediately instead of being dispatched. 76 | func safeAsync(_ block: @escaping () -> Void) { 77 | if self === DispatchQueue.main && Thread.isMainThread { 78 | block() 79 | } else { 80 | async { block() } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /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 | /// A class that keeps a weakly reference for `self` when implementing `onXXX` behaviors. 29 | /// Instead of remembering to keep `self` as weak in a stored closure: 30 | /// 31 | /// ```swift 32 | /// // MyClass.swift 33 | /// var onDone: (() -> Void)? 34 | /// func done() { 35 | /// onDone?() 36 | /// } 37 | /// 38 | /// // ViewController.swift 39 | /// var obj: MyClass? 40 | /// 41 | /// func doSomething() { 42 | /// obj = MyClass() 43 | /// obj!.onDone = { [weak self] in 44 | /// self?.reportDone() 45 | /// } 46 | /// } 47 | /// ``` 48 | /// 49 | /// You can create a `Delegate` and observe on `self`. Now, there is no retain cycle inside: 50 | /// 51 | /// ```swift 52 | /// // MyClass.swift 53 | /// let onDone = Delegate<(), Void>() 54 | /// func done() { 55 | /// onDone.call() 56 | /// } 57 | /// 58 | /// // ViewController.swift 59 | /// var obj: MyClass? 60 | /// 61 | /// func doSomething() { 62 | /// obj = MyClass() 63 | /// obj!.onDone.delegate(on: self) { (self, _) 64 | /// // `self` here is shadowed and does not keep a strong ref. 65 | /// // So you can release both `MyClass` instance and `ViewController` instance. 66 | /// self.reportDone() 67 | /// } 68 | /// } 69 | /// ``` 70 | /// 71 | public class Delegate { 72 | public init() {} 73 | 74 | private var block: ((Input) -> Output?)? 75 | public func delegate(on target: T, block: ((T, Input) -> Output)?) { 76 | self.block = { [weak target] input in 77 | guard let target = target else { return nil } 78 | return block?(target, input) 79 | } 80 | } 81 | 82 | public func call(_ input: Input) -> Output? { 83 | return block?(input) 84 | } 85 | 86 | public func callAsFunction(_ input: Input) -> Output? { 87 | return call(input) 88 | } 89 | } 90 | 91 | extension Delegate where Input == Void { 92 | public func call() -> Output? { 93 | return call(()) 94 | } 95 | 96 | public func callAsFunction() -> Output? { 97 | return call() 98 | } 99 | } 100 | 101 | extension Delegate where Input == Void, Output: OptionalProtocol { 102 | public func call() -> Output { 103 | return call(()) 104 | } 105 | 106 | public func callAsFunction() -> Output { 107 | return call() 108 | } 109 | } 110 | 111 | extension Delegate where Output: OptionalProtocol { 112 | public func call(_ input: Input) -> Output { 113 | if let result = block?(input) { 114 | return result 115 | } else { 116 | return Output._createNil 117 | } 118 | } 119 | 120 | public func callAsFunction(_ input: Input) -> Output { 121 | return call(input) 122 | } 123 | } 124 | 125 | public protocol OptionalProtocol { 126 | static var _createNil: Self { get } 127 | } 128 | extension Optional : OptionalProtocol { 129 | public static var _createNil: Optional { 130 | return nil 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /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 CGFloat { 30 | var isEven: Bool { 31 | return truncatingRemainder(dividingBy: 2.0) == 0 32 | } 33 | } 34 | 35 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 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 KFCrossPlatformImage { 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 | -------------------------------------------------------------------------------- /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 | // These helper methods are not public since we do not want them to be exposed or cause any conflicting. 30 | // However, they are just wrapper of `ResultUtil` static methods. 31 | extension Result where Failure: Error { 32 | 33 | /// Evaluates the given transform closures to create a single output value. 34 | /// 35 | /// - Parameters: 36 | /// - onSuccess: A closure that transforms the success value. 37 | /// - onFailure: A closure that transforms the error value. 38 | /// - Returns: A single `Output` value. 39 | func match( 40 | onSuccess: (Success) -> Output, 41 | onFailure: (Failure) -> Output) -> Output 42 | { 43 | switch self { 44 | case let .success(value): 45 | return onSuccess(value) 46 | case let .failure(error): 47 | return onFailure(error) 48 | } 49 | } 50 | 51 | func matchSuccess(with folder: (Success?) -> Output) -> Output { 52 | return match( 53 | onSuccess: { value in return folder(value) }, 54 | onFailure: { _ in return folder(nil) } 55 | ) 56 | } 57 | 58 | func matchFailure(with folder: (Error?) -> Output) -> Output { 59 | return match( 60 | onSuccess: { _ in return folder(nil) }, 61 | onFailure: { error in return folder(error) } 62 | ) 63 | } 64 | 65 | func match(with folder: (Success?, Error?) -> Output) -> Output { 66 | return match( 67 | onSuccess: { return folder($0, nil) }, 68 | onFailure: { return folder(nil, $0) } 69 | ) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kingfisher (7.1.2) 3 | 4 | DEPENDENCIES: 5 | - Kingfisher (~> 7.0) 6 | 7 | SPEC REPOS: 8 | trunk: 9 | - Kingfisher 10 | 11 | SPEC CHECKSUMS: 12 | Kingfisher: 44ed6a8504763f27bab46163adfac83f5deb240c 13 | 14 | PODFILE CHECKSUM: 492d6753b0c9aa2c1e4e2a3ea542771bb3abfd47 15 | 16 | COCOAPODS: 1.11.2 17 | -------------------------------------------------------------------------------- /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 | 7.1.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Kingfisher : NSObject 3 | @end 4 | @implementation PodsDummy_Kingfisher 5 | @end 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 14 | FOUNDATION_EXPORT double KingfisherVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Kingfisher/Kingfisher.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CFNetwork" -weak_framework "Combine" -weak_framework "SwiftUI" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher 11 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 12 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 13 | SKIP_INSTALL = YES 14 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Kingfisher/Kingfisher.release.xcconfig: -------------------------------------------------------------------------------- 1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 5 | OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CFNetwork" -weak_framework "Combine" -weak_framework "SwiftUI" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT} 10 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher 11 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 12 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 13 | SKIP_INSTALL = YES 14 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 15 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-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 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-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) 2019 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 | Generated by CocoaPods - https://cocoapods.org 30 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-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) 2019 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 | Generated by CocoaPods - https://cocoapods.org 50 | Title 51 | 52 | Type 53 | PSGroupSpecifier 54 | 55 | 56 | StringsTable 57 | Acknowledgements 58 | Title 59 | Acknowledgements 60 | 61 | 62 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_MoviesAPP : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_MoviesAPP 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP-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_MoviesAPPVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_MoviesAPPVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CFNetwork" -framework "Kingfisher" -weak_framework "Combine" -weak_framework "SwiftUI" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_MoviesAPP { 2 | umbrella header "Pods-MoviesAPP-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-MoviesAPP/Pods-MoviesAPP.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" 6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' 7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift 8 | OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CFNetwork" -framework "Kingfisher" -weak_framework "Combine" -weak_framework "SwiftUI" 9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 10 | PODS_BUILD_DIR = ${BUILD_DIR} 11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 13 | PODS_ROOT = ${SRCROOT}/Pods 14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates 15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # viewcoe-live-coding-dio --------------------------------------------------------------------------------