├── .gitignore ├── .travis.yml ├── Example ├── Podfile ├── Podfile.lock ├── Pods │ ├── Kingfisher │ │ ├── LICENSE │ │ ├── README.md │ │ └── Sources │ │ │ ├── Cache │ │ │ ├── CacheSerializer.swift │ │ │ ├── DiskStorage.swift │ │ │ ├── FormatIndicatedCacheSerializer.swift │ │ │ ├── ImageCache.swift │ │ │ ├── MemoryStorage.swift │ │ │ └── Storage.swift │ │ │ ├── Extensions │ │ │ ├── ImageView+Kingfisher.swift │ │ │ └── UIButton+Kingfisher.swift │ │ │ ├── General │ │ │ ├── Deprecated.swift │ │ │ ├── ImageSource │ │ │ │ ├── ImageDataProvider.swift │ │ │ │ ├── Resource.swift │ │ │ │ └── Source.swift │ │ │ ├── Kingfisher.swift │ │ │ ├── KingfisherError.swift │ │ │ ├── KingfisherManager.swift │ │ │ └── KingfisherOptionsInfo.swift │ │ │ ├── Image │ │ │ ├── Filter.swift │ │ │ ├── GIFAnimatedImage.swift │ │ │ ├── Image.swift │ │ │ ├── ImageDrawing.swift │ │ │ ├── ImageFormat.swift │ │ │ ├── ImageProcessor.swift │ │ │ ├── ImageTransition.swift │ │ │ └── Placeholder.swift │ │ │ ├── Kingfisher.h │ │ │ ├── Networking │ │ │ ├── AuthenticationChallengeResponsable.swift │ │ │ ├── ImageDataProcessor.swift │ │ │ ├── ImageDownloader.swift │ │ │ ├── ImageDownloaderDelegate.swift │ │ │ ├── ImageModifier.swift │ │ │ ├── ImagePrefetcher.swift │ │ │ ├── RequestModifier.swift │ │ │ ├── SessionDataTask.swift │ │ │ └── SessionDelegate.swift │ │ │ ├── Utility │ │ │ ├── Box.swift │ │ │ ├── CallbackQueue.swift │ │ │ ├── Delegate.swift │ │ │ ├── ExtensionHelpers.swift │ │ │ ├── Result.swift │ │ │ ├── Runtime.swift │ │ │ ├── SizeExtensions.swift │ │ │ └── String+MD5.swift │ │ │ └── Views │ │ │ ├── AnimatedImageView.swift │ │ │ └── Indicator.swift │ ├── Local Podspecs │ │ └── ShelfView.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ └── Target Support Files │ │ ├── Kingfisher │ │ ├── Info.plist │ │ ├── Kingfisher-dummy.m │ │ ├── Kingfisher-prefix.pch │ │ ├── Kingfisher-umbrella.h │ │ ├── Kingfisher.modulemap │ │ └── Kingfisher.xcconfig │ │ ├── Pods-ShelfView_Example │ │ ├── Info.plist │ │ ├── Pods-ShelfView_Example-acknowledgements.markdown │ │ ├── Pods-ShelfView_Example-acknowledgements.plist │ │ ├── Pods-ShelfView_Example-dummy.m │ │ ├── Pods-ShelfView_Example-frameworks.sh │ │ ├── Pods-ShelfView_Example-resources.sh │ │ ├── Pods-ShelfView_Example-umbrella.h │ │ ├── Pods-ShelfView_Example.debug.xcconfig │ │ ├── Pods-ShelfView_Example.modulemap │ │ └── Pods-ShelfView_Example.release.xcconfig │ │ ├── Pods-ShelfView_Tests │ │ ├── Info.plist │ │ ├── Pods-ShelfView_Tests-acknowledgements.markdown │ │ ├── Pods-ShelfView_Tests-acknowledgements.plist │ │ ├── Pods-ShelfView_Tests-dummy.m │ │ ├── Pods-ShelfView_Tests-frameworks.sh │ │ ├── Pods-ShelfView_Tests-resources.sh │ │ ├── Pods-ShelfView_Tests-umbrella.h │ │ ├── Pods-ShelfView_Tests.debug.xcconfig │ │ ├── Pods-ShelfView_Tests.modulemap │ │ └── Pods-ShelfView_Tests.release.xcconfig │ │ └── ShelfView │ │ ├── Info.plist │ │ ├── ResourceBundle-ShelfView-Info.plist │ │ ├── ShelfView-dummy.m │ │ ├── ShelfView-prefix.pch │ │ ├── ShelfView-umbrella.h │ │ ├── ShelfView.modulemap │ │ └── ShelfView.xcconfig ├── ShelfView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── ShelfView-Example.xcscheme ├── ShelfView.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── ShelfView │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── PlainShelfController.swift │ └── SectionShelfController.swift └── Tests │ ├── Info.plist │ └── Tests.swift ├── LICENSE ├── README.md ├── ShelfView.podspec ├── ShelfView ├── Assets │ ├── .gitkeep │ ├── center.png │ ├── header.png │ ├── left.png │ ├── right.png │ └── spine.png └── Classes │ ├── .gitkeep │ ├── Dimens.swift │ ├── PlainShelfView.swift │ ├── SectionShelfView.swift │ ├── ShelfCellView.swift │ ├── ShelfHeaderCellView.swift │ ├── ShelfViewDelegate.swift │ ├── ShelfViewModel.swift │ └── Utils.swift ├── _Pods.xcodeproj ├── ipad.png ├── iphone.png └── note.png /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | # Pods/ 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/ShelfView.xcworkspace -scheme ShelfView-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'ShelfView_Example' do 4 | pod 'ShelfView', :path => '../' 5 | 6 | target 'ShelfView_Tests' do 7 | inherit! :search_paths 8 | 9 | 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kingfisher (5.0.1) 3 | - ShelfView (1.1): 4 | - Kingfisher (~> 5.0) 5 | 6 | DEPENDENCIES: 7 | - ShelfView (from `../`) 8 | 9 | SPEC REPOS: 10 | https://github.com/cocoapods/specs.git: 11 | - Kingfisher 12 | 13 | EXTERNAL SOURCES: 14 | ShelfView: 15 | :path: "../" 16 | 17 | SPEC CHECKSUMS: 18 | Kingfisher: 4f771421b9208185217550528a06fd48756cb7c2 19 | ShelfView: 9ca6d37cf8d92b39b2e7d44c3f798aebd00da1ba 20 | 21 | PODFILE CHECKSUM: bcca6e02436573d29fd62d0bb8934cf4a1245178 22 | 23 | COCOAPODS: 1.5.3 24 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Wei Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Cache/CacheSerializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CacheSerializer.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/09/02. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// An `CacheSerializer` is used to convert some data to an image object after 30 | /// retrieving it from disk storage, and vice versa, to convert an image to data object 31 | /// for storing to the disk storage. 32 | public protocol CacheSerializer { 33 | 34 | /// Gets the serialized data from a provided image 35 | /// and optional original data for caching to disk. 36 | /// 37 | /// - Parameters: 38 | /// - image: The image needed to be serialized. 39 | /// - original: The original data which is just downloaded. 40 | /// If the image is retrieved from cache instead of 41 | /// downloaded, it will be `nil`. 42 | /// - Returns: The data object for storing to disk, or `nil` when no valid 43 | /// data could be serialized. 44 | func data(with image: Image, original: Data?) -> Data? 45 | 46 | /// Gets an image from provided serialized data. 47 | /// 48 | /// - Parameters: 49 | /// - data: The data from which an image should be deserialized. 50 | /// - options: The parsed options for deserialization. 51 | /// - Returns: An image deserialized or `nil` when no valid image 52 | /// could be deserialized. 53 | func image(with data: Data, options: KingfisherParsedOptionsInfo) -> Image? 54 | 55 | /// Gets an image deserialized from provided data. 56 | /// 57 | /// - Parameters: 58 | /// - data: The data from which an image should be deserialized. 59 | /// - options: Options for deserialization. 60 | /// - Returns: An image deserialized or `nil` when no valid image 61 | /// could be deserialized. 62 | /// - Note: 63 | /// This method is deprecated. Please implement the version with 64 | /// `KingfisherParsedOptionsInfo` as parameter instead. 65 | @available(*, deprecated, 66 | message: "Deprecated. Implement the method with same name but with `KingfisherParsedOptionsInfo` instead.") 67 | func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? 68 | } 69 | 70 | extension CacheSerializer { 71 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { 72 | return image(with: data, options: KingfisherParsedOptionsInfo(options)) 73 | } 74 | } 75 | 76 | /// Represents a basic and default `CacheSerializer` used in Kingfisher disk cache system. 77 | /// It could serialize and deserialize images in PNG, JPEG and GIF format. For 78 | /// image other than these formats, a normalized `pngRepresentation` will be used. 79 | public struct DefaultCacheSerializer: CacheSerializer { 80 | 81 | /// The default general cache serializer used across Kingfisher's cache. 82 | public static let `default` = DefaultCacheSerializer() 83 | private init() {} 84 | 85 | /// - Parameters: 86 | /// - image: The image needed to be serialized. 87 | /// - original: The original data which is just downloaded. 88 | /// If the image is retrieved from cache instead of 89 | /// downloaded, it will be `nil`. 90 | /// - Returns: The data object for storing to disk, or `nil` when no valid 91 | /// data could be serialized. 92 | /// 93 | /// - Note: 94 | /// Only when `original` contains valid PNG, JPEG and GIF format data, the `image` will be 95 | /// converted to the corresponding data type. Otherwise, if the `original` is provided but it is not 96 | /// a valid format, the `original` data will be used for cache. 97 | /// 98 | /// If `original` is `nil`, the input `image` will be encoded as PNG data. 99 | public func data(with image: Image, original: Data?) -> Data? { 100 | let imageFormat = original?.kf.imageFormat ?? .unknown 101 | 102 | let data: Data? 103 | switch imageFormat { 104 | case .PNG: data = image.kf.pngRepresentation() 105 | case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0) 106 | case .GIF: data = image.kf.gifRepresentation() 107 | case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation() 108 | } 109 | 110 | return data 111 | } 112 | 113 | /// Gets an image deserialized from provided data. 114 | /// 115 | /// - Parameters: 116 | /// - data: The data from which an image should be deserialized. 117 | /// - options: Options for deserialization. 118 | /// - Returns: An image deserialized or `nil` when no valid image 119 | /// could be deserialized. 120 | public func image(with data: Data, options: KingfisherParsedOptionsInfo) -> Image? { 121 | return KingfisherWrapper.image(data: data, options: options.imageCreatingOptions) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Cache/FormatIndicatedCacheSerializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Junyu Kuang on 5/28/17. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// `FormatIndicatedCacheSerializer` lets you indicate an image format for serialized caches. 30 | /// 31 | /// It could serialize and deserialize PNG, JPEG and GIF images. For 32 | /// image other than these formats, a normalized `pngRepresentation` will be used. 33 | /// 34 | /// Example: 35 | /// ```` 36 | /// let profileImageSize = CGSize(width: 44, height: 44) 37 | /// 38 | /// // A round corner image. 39 | /// let imageProcessor = RoundCornerImageProcessor( 40 | /// cornerRadius: profileImageSize.width / 2, targetSize: profileImageSize) 41 | /// 42 | /// let optionsInfo: KingfisherOptionsInfo = [ 43 | /// .cacheSerializer(FormatIndicatedCacheSerializer.png), 44 | /// .processor(imageProcessor)] 45 | /// 46 | /// A URL pointing to a JPEG image. 47 | /// let url = URL(string: "https://example.com/image.jpg")! 48 | /// 49 | /// // Image will be always cached as PNG format to preserve alpha channel for round rectangle. 50 | /// // So when you load it from cache again later, it will be still round cornered. 51 | /// // Otherwise, the corner part would be filled by white color (since JPEG does not contain an alpha channel). 52 | /// imageView.kf.setImage(with: url, options: optionsInfo) 53 | /// ```` 54 | public struct FormatIndicatedCacheSerializer: CacheSerializer { 55 | 56 | /// A `FormatIndicatedCacheSerializer` which converts image from and to PNG format. If the image cannot be 57 | /// represented by PNG format, it will fallback to its real format which is determined by `original` data. 58 | public static let png = FormatIndicatedCacheSerializer(imageFormat: .PNG) 59 | 60 | /// A `FormatIndicatedCacheSerializer` which converts image from and to JPEG format. If the image cannot be 61 | /// represented by JPEG format, it will fallback to its real format which is determined by `original` data. 62 | public static let jpeg = FormatIndicatedCacheSerializer(imageFormat: .JPEG) 63 | 64 | /// A `FormatIndicatedCacheSerializer` which converts image from and to GIF format. If the image cannot be 65 | /// represented by GIF format, it will fallback to its real format which is determined by `original` data. 66 | public static let gif = FormatIndicatedCacheSerializer(imageFormat: .GIF) 67 | 68 | /// The indicated image format. 69 | private let imageFormat: ImageFormat 70 | 71 | /// Creates data which represents the given `image` under a format. 72 | public func data(with image: Image, original: Data?) -> Data? { 73 | 74 | func imageData(withFormat imageFormat: ImageFormat) -> Data? { 75 | switch imageFormat { 76 | case .PNG: return image.kf.pngRepresentation() 77 | case .JPEG: return image.kf.jpegRepresentation(compressionQuality: 1.0) 78 | case .GIF: return image.kf.gifRepresentation() 79 | case .unknown: return nil 80 | } 81 | } 82 | 83 | // generate data with indicated image format 84 | if let data = imageData(withFormat: imageFormat) { 85 | return data 86 | } 87 | 88 | let originalFormat = original?.kf.imageFormat ?? .unknown 89 | 90 | // generate data with original image's format 91 | if originalFormat != imageFormat, let data = imageData(withFormat: originalFormat) { 92 | return data 93 | } 94 | 95 | return original ?? image.kf.normalized.kf.pngRepresentation() 96 | } 97 | 98 | /// Same implementation as `DefaultCacheSerializer`. 99 | public func image(with data: Data, options: KingfisherParsedOptionsInfo) -> Image? { 100 | return KingfisherWrapper.image(data: data, options: options.imageCreatingOptions) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Cache/Storage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Storage.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2018/10/15. 6 | // 7 | // Copyright (c) 2018年 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Represents the expiration strategy used in storage. 30 | /// 31 | /// - never: The item never expires. 32 | /// - seconds: The item expires after a time duration of given seconds from now. 33 | /// - days: The item expires after a time duration of given days from now. 34 | /// - date: The item expires after a given date. 35 | public enum StorageExpiration { 36 | /// The item never expires. 37 | case never 38 | /// The item expires after a time duration of given seconds from now. 39 | case seconds(TimeInterval) 40 | /// The item expires after a time duration of given days from now. 41 | case days(Int) 42 | /// The item expires after a given date. 43 | case date(Date) 44 | /// Indicates the item is already expired. Use this to skip cache. 45 | case expired 46 | 47 | func estimatedExpirationSince(_ date: Date) -> Date { 48 | switch self { 49 | case .never: return .distantFuture 50 | case .seconds(let seconds): return date.addingTimeInterval(seconds) 51 | case .days(let days): return date.addingTimeInterval(TimeInterval(60 * 60 * 24 * days)) 52 | case .date(let ref): return ref 53 | case .expired: return .distantPast 54 | } 55 | } 56 | 57 | var estimatedExpirationSinceNow: Date { 58 | return estimatedExpirationSince(Date()) 59 | } 60 | 61 | var isExpired: Bool { 62 | return timeInterval <= 0 63 | } 64 | 65 | var timeInterval: TimeInterval { 66 | switch self { 67 | case .never: return .infinity 68 | case .seconds(let seconds): return seconds 69 | case .days(let days): return TimeInterval(60 * 60 * 24 * days) 70 | case .date(let ref): return ref.timeIntervalSinceNow 71 | case .expired: return -(.infinity) 72 | } 73 | } 74 | } 75 | 76 | /// Represents types which cost in memory can be calculated. 77 | public protocol CacheCostCalculable { 78 | var cacheCost: Int { get } 79 | } 80 | 81 | /// Represents types which can be converted to and from data. 82 | public protocol DataTransformable { 83 | func toData() throws -> Data 84 | static func fromData(_ data: Data) throws -> Self 85 | static var empty: Self { get } 86 | } 87 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/General/ImageSource/ImageDataProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageDataProvider.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2018/11/13. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Represents a data provider to provide image data to Kingfisher when setting with 30 | /// `Source.provider` source. Compared to `Source.network` member, it gives a chance 31 | /// to load some image data in your own way, as long as you can provide the data 32 | /// representation for the image. 33 | public protocol ImageDataProvider { 34 | 35 | /// The key used in cache. 36 | var cacheKey: String { get } 37 | 38 | /// Provides the data which represents image. Kingfisher uses the data you pass in the 39 | /// handler to process images and caches it for later use. 40 | /// 41 | /// - Parameter handler: The handler you should call when you prepared your data. 42 | /// If the data is loaded successfully, call the handler with 43 | /// a `.success` with the data associated. Otherwise, call it 44 | /// with a `.failure` and pass the error. 45 | /// 46 | /// - Note: 47 | /// If the `handler` is called with a `.failure` with error, a `dataProviderError` of 48 | /// `ImageSettingErrorReason` will be finally thrown out to you as the `KingfisherError` 49 | /// from the framework. 50 | func data(handler: @escaping (Result) -> Void) 51 | } 52 | 53 | /// Represents an image data provider for loading from a local file URL on disk. 54 | /// Uses this type for adding a disk image to Kingfisher. Compared to loading it 55 | /// directly, you can get benefit of using Kingfisher's extension methods, as well 56 | /// as applying `ImageProcessor`s and storing the image to `ImageCache` of Kingfisher. 57 | public struct LocalFileImageDataProvider: ImageDataProvider { 58 | 59 | // MARK: Public Properties 60 | 61 | /// The file URL from which the image be loaded. 62 | public let fileURL: URL 63 | 64 | // MARK: Initializers 65 | 66 | /// Creates an image data provider by supplying the target local file URL. 67 | /// 68 | /// - Parameters: 69 | /// - fileURL: The file URL from which the image be loaded. 70 | /// - cacheKey: The key is used for caching the image data. By default, 71 | /// the `absoluteString` of `fileURL` is used. 72 | public init(fileURL: URL, cacheKey: String? = nil) { 73 | self.fileURL = fileURL 74 | self.cacheKey = cacheKey ?? fileURL.absoluteString 75 | } 76 | 77 | // MARK: Protocol Conforming 78 | 79 | /// The key used in cache. 80 | public var cacheKey: String 81 | 82 | public func data(handler: (Result) -> Void) { 83 | handler( Result { try Data(contentsOf: fileURL) } ) 84 | } 85 | } 86 | 87 | /// Represents an image data provider for loading image from a given Base64 encoded string. 88 | public struct Base64ImageDataProvider: ImageDataProvider { 89 | 90 | // MARK: Public Properties 91 | /// The encoded Base64 string for the image. 92 | public let base64String: String 93 | 94 | // MARK: Initializers 95 | 96 | /// Creates an image data provider by supplying the Base64 encoded string. 97 | /// 98 | /// - Parameters: 99 | /// - base64String: The Base64 encoded string for an image. 100 | /// - cacheKey: The key is used for caching the image data. You need a different key for any different image. 101 | public init(base64String: String, cacheKey: String) { 102 | self.base64String = base64String 103 | self.cacheKey = cacheKey 104 | } 105 | 106 | // MARK: Protocol Conforming 107 | 108 | /// The key used in cache. 109 | public var cacheKey: String 110 | 111 | public func data(handler: (Result) -> Void) { 112 | let data = Data(base64Encoded: base64String)! 113 | handler(.success(data)) 114 | } 115 | } 116 | 117 | /// Represents an image data provider for a raw data object. 118 | public struct RawImageDataProvider: ImageDataProvider { 119 | 120 | // MARK: Public Properties 121 | 122 | /// The raw data object to provide to Kingfisher image loader. 123 | public let data: Data 124 | 125 | // MARK: Initializers 126 | 127 | /// Creates an image data provider by the given raw `data` value and a `cacheKey` be used in Kingfisher cache. 128 | /// 129 | /// - Parameters: 130 | /// - data: The raw data reprensents an image. 131 | /// - cacheKey: The key is used for caching the image data. You need a different key for any different image. 132 | public init(data: Data, cacheKey: String) { 133 | self.data = data 134 | self.cacheKey = cacheKey 135 | } 136 | 137 | // MARK: Protocol Conforming 138 | 139 | /// The key used in cache. 140 | public var cacheKey: String 141 | 142 | public func data(handler: @escaping (Result) -> Void) { 143 | handler(.success(data)) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/General/ImageSource/Resource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Resource.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Represents an image resource at a certain url and a given cache key. 30 | /// Kingfisher will use a `Resource` to download a resource from network and cache it with the cache key when 31 | /// using `Source.network` as its image setting source. 32 | public protocol Resource { 33 | 34 | /// The key used in cache. 35 | var cacheKey: String { get } 36 | 37 | /// The target image URL. 38 | var downloadURL: URL { get } 39 | } 40 | 41 | /// ImageResource is a simple combination of `downloadURL` and `cacheKey`. 42 | /// When passed to image view set methods, Kingfisher will try to download the target 43 | /// image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache. 44 | public struct ImageResource: Resource { 45 | 46 | // MARK: - Initializers 47 | 48 | /// Creates an image resource. 49 | /// 50 | /// - Parameters: 51 | /// - downloadURL: The target image URL from where the image can be downloaded. 52 | /// - cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key. 53 | /// Default is `nil`. 54 | public init(downloadURL: URL, cacheKey: String? = nil) { 55 | self.downloadURL = downloadURL 56 | self.cacheKey = cacheKey ?? downloadURL.absoluteString 57 | } 58 | 59 | // MARK: Protocol Conforming 60 | 61 | /// The key used in cache. 62 | public let cacheKey: String 63 | 64 | /// The target image URL. 65 | public let downloadURL: URL 66 | } 67 | 68 | /// URL conforms to `Resource` in Kingfisher. 69 | /// The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`. 70 | /// If you need customize the url and/or cache key, use `ImageResource` instead. 71 | extension URL: Resource { 72 | public var cacheKey: String { return absoluteString } 73 | public var downloadURL: URL { return self } 74 | } 75 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/General/ImageSource/Source.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Source.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2018/11/17. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Represents an image setting source for Kingfisher methods. 30 | /// 31 | /// A `Source` value indicates the way how the target image can be retrieved and cached. 32 | /// 33 | /// - network: The target image should be got from network remotely. The associated `Resource` 34 | /// value defines detail information like image URL and cache key. 35 | /// - provider: The target image should be provided in a data format. Normally, it can be an image 36 | /// from local storage or in any other encoding format (like Base64). 37 | public enum Source { 38 | 39 | /// Represents the source task identifier when setting an image to a view with extension methods. 40 | public enum Identifier { 41 | 42 | /// The underlying value type of source identifier. 43 | public typealias Value = UInt 44 | static var current: Value = 0 45 | static func next() -> Value { 46 | current += 1 47 | return current 48 | } 49 | } 50 | 51 | // MARK: Member Cases 52 | 53 | /// The target image should be got from network remotely. The associated `Resource` 54 | /// value defines detail information like image URL and cache key. 55 | case network(Resource) 56 | 57 | /// The target image should be provided in a data format. Normally, it can be an image 58 | /// from local storage or in any other encoding format (like Base64). 59 | case provider(ImageDataProvider) 60 | 61 | // MARK: Getting Properties 62 | 63 | /// The cache key defined for this source value. 64 | public var cacheKey: String { 65 | switch self { 66 | case .network(let resource): return resource.cacheKey 67 | case .provider(let provider): return provider.cacheKey 68 | } 69 | } 70 | 71 | /// The URL defined for this source value. 72 | /// 73 | /// For a `.network` source, it is the `downloadURL` of associated `Resource` instance. 74 | /// For a `.provider` value, it is always `nil`. 75 | public var url: URL? { 76 | switch self { 77 | case .network(let resource): return resource.downloadURL 78 | // `ImageDataProvider` does not provide a URL. All it cares is how to get the data back. 79 | case .provider(_): return nil 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/General/Kingfisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Kingfisher.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 16/9/14. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | import ImageIO 29 | 30 | #if os(macOS) 31 | import AppKit 32 | public typealias Image = NSImage 33 | public typealias View = NSView 34 | public typealias Color = NSColor 35 | public typealias ImageView = NSImageView 36 | public typealias Button = NSButton 37 | #else 38 | import UIKit 39 | public typealias Image = UIImage 40 | public typealias Color = UIColor 41 | #if !os(watchOS) 42 | public typealias ImageView = UIImageView 43 | public typealias View = UIView 44 | public typealias Button = UIButton 45 | #else 46 | import WatchKit 47 | #endif 48 | #endif 49 | 50 | /// Wrapper for Kingfisher compatible types. This type provides an extension point for 51 | /// connivence methods in Kingfisher. 52 | public struct KingfisherWrapper { 53 | public let base: Base 54 | public init(_ base: Base) { 55 | self.base = base 56 | } 57 | } 58 | 59 | /// Represents a type which is compatible with Kingfisher. You can use `kf` property to get a 60 | /// value in the namespace of Kingfisher. 61 | public protocol KingfisherCompatible { } 62 | 63 | public extension KingfisherCompatible { 64 | 65 | /// Gets a namespace holder for Kingfisher compatible types. 66 | public var kf: KingfisherWrapper { 67 | get { return KingfisherWrapper(self) } 68 | set { } 69 | } 70 | } 71 | 72 | extension Image: KingfisherCompatible { } 73 | #if !os(watchOS) 74 | extension ImageView: KingfisherCompatible { } 75 | extension Button: KingfisherCompatible { } 76 | #else 77 | extension WKInterfaceImage: KingfisherCompatible { } 78 | #endif 79 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Image/Filter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Filter.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/08/31. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import CoreImage 28 | 29 | // Reuse the same CI Context for all CI drawing. 30 | private let ciContext = CIContext(options: nil) 31 | 32 | /// Represents the type of transformer method, which will be used in to provide a `Filter`. 33 | public typealias Transformer = (CIImage) -> CIImage? 34 | 35 | /// Represents a processor based on a `CIImage` `Filter`. 36 | /// It requires a filter to create an `ImageProcessor`. 37 | public protocol CIImageProcessor: ImageProcessor { 38 | var filter: Filter { get } 39 | } 40 | 41 | extension CIImageProcessor { 42 | 43 | /// Processes the input `ImageProcessItem` with this processor. 44 | /// 45 | /// - Parameters: 46 | /// - item: Input item which will be processed by `self`. 47 | /// - options: Options when processing the item. 48 | /// - Returns: The processed image. 49 | /// 50 | /// - Note: See documentation of `ImageProcessor` protocol for more. 51 | public func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> Image? { 52 | switch item { 53 | case .image(let image): 54 | return image.kf.apply(filter) 55 | case .data: 56 | return (DefaultImageProcessor.default >> self).process(item: item, options: options) 57 | } 58 | } 59 | } 60 | 61 | /// A wrapper struct for a `Transformer` of CIImage filters. A `Filter` 62 | /// value could be used to create a `CIImage` processor. 63 | public struct Filter { 64 | 65 | let transform: Transformer 66 | 67 | public init(transform: @escaping Transformer) { 68 | self.transform = transform 69 | } 70 | 71 | /// Tint filter which will apply a tint color to images. 72 | public static var tint: (Color) -> Filter = { 73 | color in 74 | Filter { 75 | input in 76 | 77 | let colorFilter = CIFilter(name: "CIConstantColorGenerator")! 78 | colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey) 79 | 80 | let filter = CIFilter(name: "CISourceOverCompositing")! 81 | 82 | let colorImage = colorFilter.outputImage 83 | filter.setValue(colorImage, forKey: kCIInputImageKey) 84 | filter.setValue(input, forKey: kCIInputBackgroundImageKey) 85 | 86 | return filter.outputImage?.cropped(to: input.extent) 87 | } 88 | } 89 | 90 | /// Represents color control elements. It is a tuple of 91 | /// `(brightness, contrast, saturation, inputEV)` 92 | public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat) 93 | 94 | /// Color control filter which will apply color control change to images. 95 | public static var colorControl: (ColorElement) -> Filter = { arg -> Filter in 96 | let (brightness, contrast, saturation, inputEV) = arg 97 | return Filter { input in 98 | let paramsColor = [kCIInputBrightnessKey: brightness, 99 | kCIInputContrastKey: contrast, 100 | kCIInputSaturationKey: saturation] 101 | let blackAndWhite = input.applyingFilter("CIColorControls", parameters: paramsColor) 102 | let paramsExposure = [kCIInputEVKey: inputEV] 103 | return blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure) 104 | } 105 | } 106 | } 107 | 108 | extension KingfisherWrapper where Base: Image { 109 | 110 | /// Applies a `Filter` containing `CIImage` transformer to `self`. 111 | /// 112 | /// - Parameter filter: The filter used to transform `self`. 113 | /// - Returns: A transformed image by input `Filter`. 114 | /// 115 | /// - Note: 116 | /// Only CG-based images are supported. If any error happens 117 | /// during transforming, `self` will be returned. 118 | public func apply(_ filter: Filter) -> Image { 119 | 120 | guard let cgImage = cgImage else { 121 | assertionFailure("[Kingfisher] Tint image only works for CG-based image.") 122 | return base 123 | } 124 | 125 | let inputImage = CIImage(cgImage: cgImage) 126 | guard let outputImage = filter.transform(inputImage) else { 127 | return base 128 | } 129 | 130 | guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { 131 | assertionFailure("[Kingfisher] Can not make an tint image within context.") 132 | return base 133 | } 134 | 135 | #if os(macOS) 136 | return fixedForRetinaPixel(cgImage: result, to: size) 137 | #else 138 | return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation) 139 | #endif 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Image/GIFAnimatedImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimatedImage.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2018/09/26. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | import ImageIO 29 | 30 | /// Represents a set of image creating options used in Kingfisher. 31 | public struct ImageCreatingOptions { 32 | 33 | /// The target scale of image needs to be created. 34 | public let scale: CGFloat 35 | 36 | /// The expected animation duration if an animated image being created. 37 | public let duration: TimeInterval 38 | 39 | /// For an animated image, whether or not all frames should be loaded before displaying. 40 | public let preloadAll: Bool 41 | 42 | /// For an animated image, whether or not only the first image should be 43 | /// loaded as a static image. It is useful for preview purpose of an animated image. 44 | public let onlyFirstFrame: Bool 45 | 46 | /// Creates an `ImageCreatingOptions` object. 47 | /// 48 | /// - Parameters: 49 | /// - scale: The target scale of image needs to be created. Default is `1.0`. 50 | /// - duration: The expected animation duration if an animated image being created. 51 | /// A value less or equal to `0.0` means the animated image duration will 52 | /// be determined by the frame data. Default is `0.0`. 53 | /// - preloadAll: For an animated image, whether or not all frames should be loaded before displaying. 54 | /// Default is `false`. 55 | /// - onlyFirstFrame: For an animated image, whether or not only the first image should be 56 | /// loaded as a static image. It is useful for preview purpose of an animated image. 57 | /// Default is `false`. 58 | public init( 59 | scale: CGFloat = 1.0, 60 | duration: TimeInterval = 0.0, 61 | preloadAll: Bool = false, 62 | onlyFirstFrame: Bool = false) 63 | { 64 | self.scale = scale 65 | self.duration = duration 66 | self.preloadAll = preloadAll 67 | self.onlyFirstFrame = onlyFirstFrame 68 | } 69 | } 70 | 71 | // Represents the decoding for a GIF image. This class extracts frames from an `imageSource`, then 72 | // hold the images for later use. 73 | class GIFAnimatedImage { 74 | let images: [Image] 75 | let duration: TimeInterval 76 | 77 | init?(from imageSource: CGImageSource, for info: [String: Any], options: ImageCreatingOptions) { 78 | let frameCount = CGImageSourceGetCount(imageSource) 79 | var images = [Image]() 80 | var gifDuration = 0.0 81 | 82 | for i in 0 ..< frameCount { 83 | guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, info as CFDictionary) else { 84 | return nil 85 | } 86 | 87 | if frameCount == 1 { 88 | gifDuration = .infinity 89 | } else { 90 | // Get current animated GIF frame duration 91 | guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil) 92 | as? [String: Any] else 93 | { 94 | return nil 95 | } 96 | 97 | let gifInfo = properties[kCGImagePropertyGIFDictionary as String] as? [String: Any] 98 | gifDuration += GIFAnimatedImage.getFrameDuration(from: gifInfo) 99 | } 100 | images.append(KingfisherWrapper.image(cgImage: imageRef, scale: options.scale, refImage: nil)) 101 | if options.onlyFirstFrame { break } 102 | } 103 | self.images = images 104 | self.duration = gifDuration 105 | } 106 | 107 | //Calculates frame duration for a gif frame out of the kCGImagePropertyGIFDictionary dictionary. 108 | static func getFrameDuration(from gifInfo: [String: Any]?) -> TimeInterval { 109 | let defaultFrameDuration = 0.1 110 | guard let gifInfo = gifInfo else { return defaultFrameDuration } 111 | 112 | let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as? NSNumber 113 | let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber 114 | let duration = unclampedDelayTime ?? delayTime 115 | 116 | guard let frameDuration = duration else { return defaultFrameDuration } 117 | return frameDuration.doubleValue > 0.011 ? frameDuration.doubleValue : defaultFrameDuration 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Image/ImageFormat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageFormat.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2018/09/28. 6 | // 7 | // Copyright (c) 2018 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 | 53 | 54 | extension Data: KingfisherCompatible {} 55 | 56 | // MARK: - Misc Helpers 57 | extension KingfisherWrapper where Base == Data { 58 | /// Gets the image format corresponding to the data. 59 | public var imageFormat: ImageFormat { 60 | var buffer = [UInt8](repeating: 0, count: 8) 61 | (base as NSData).getBytes(&buffer, length: 8) 62 | if buffer == ImageFormat.HeaderData.PNG { 63 | return .PNG 64 | } else if buffer[0] == ImageFormat.HeaderData.JPEG_SOI[0] && 65 | buffer[1] == ImageFormat.HeaderData.JPEG_SOI[1] && 66 | buffer[2] == ImageFormat.HeaderData.JPEG_IF[0] 67 | { 68 | return .JPEG 69 | } else if buffer[0] == ImageFormat.HeaderData.GIF[0] && 70 | buffer[1] == ImageFormat.HeaderData.GIF[1] && 71 | buffer[2] == ImageFormat.HeaderData.GIF[2] 72 | { 73 | return .GIF 74 | } 75 | 76 | return .unknown 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Image/ImageTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransition.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/9/18. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(iOS) || os(tvOS) 28 | import UIKit 29 | 30 | /// Transition effect which will be used when an image downloaded and set by `UIImageView` 31 | /// extension API in Kingfisher. You can assign an enum value with transition duration as 32 | /// an item in `KingfisherOptionsInfo` to enable the animation transition. 33 | /// 34 | /// Apple's UIViewAnimationOptions is used under the hood. 35 | /// For custom transition, you should specified your own transition options, animations and 36 | /// completion handler as well. 37 | /// 38 | /// - none: No animation transition. 39 | /// - fade: Fade in the loaded image in a given duration. 40 | /// - flipFromLeft: Flip from left transition. 41 | /// - flipFromRight: Flip from right transition. 42 | /// - flipFromTop: Flip from top transition. 43 | /// - flipFromBottom: Flip from bottom transition. 44 | /// - custom: Custom transition. 45 | public enum ImageTransition { 46 | /// No animation transition. 47 | case none 48 | /// Fade in the loaded image in a given duration. 49 | case fade(TimeInterval) 50 | /// Flip from left transition. 51 | case flipFromLeft(TimeInterval) 52 | /// Flip from right transition. 53 | case flipFromRight(TimeInterval) 54 | /// Flip from top transition. 55 | case flipFromTop(TimeInterval) 56 | /// Flip from bottom transition. 57 | case flipFromBottom(TimeInterval) 58 | /// Custom transition defined by a general animation block. 59 | /// - duration: The time duration of this custom transition. 60 | /// - options: `UIView.AnimationOptions` should be used in the transition. 61 | /// - animations: The animation block will be applied when setting image. 62 | /// - completion: A block called when the transition animation finishes. 63 | case custom(duration: TimeInterval, 64 | options: UIView.AnimationOptions, 65 | animations: ((UIImageView, UIImage) -> Void)?, 66 | completion: ((Bool) -> Void)?) 67 | 68 | var duration: TimeInterval { 69 | switch self { 70 | case .none: return 0 71 | case .fade(let duration): return duration 72 | 73 | case .flipFromLeft(let duration): return duration 74 | case .flipFromRight(let duration): return duration 75 | case .flipFromTop(let duration): return duration 76 | case .flipFromBottom(let duration): return duration 77 | 78 | case .custom(let duration, _, _, _): return duration 79 | } 80 | } 81 | 82 | var animationOptions: UIView.AnimationOptions { 83 | switch self { 84 | case .none: return [] 85 | case .fade: return .transitionCrossDissolve 86 | 87 | case .flipFromLeft: return .transitionFlipFromLeft 88 | case .flipFromRight: return .transitionFlipFromRight 89 | case .flipFromTop: return .transitionFlipFromTop 90 | case .flipFromBottom: return .transitionFlipFromBottom 91 | 92 | case .custom(_, let options, _, _): return options 93 | } 94 | } 95 | 96 | var animations: ((UIImageView, UIImage) -> Void)? { 97 | switch self { 98 | case .custom(_, _, let animations, _): return animations 99 | default: return { $0.image = $1 } 100 | } 101 | } 102 | 103 | var completion: ((Bool) -> Void)? { 104 | switch self { 105 | case .custom(_, _, _, let completion): return completion 106 | default: return nil 107 | } 108 | } 109 | } 110 | #else 111 | // Just a placeholder for compiling on macOS. 112 | public enum ImageTransition { 113 | case none 114 | } 115 | #endif 116 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Image/Placeholder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Placeholder.swift 3 | // Kingfisher 4 | // 5 | // Created by Tieme van Veen on 28/08/2017. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | import AppKit 29 | #else 30 | import UIKit 31 | #endif 32 | 33 | /// Represents a placeholder type which could be set while loading as well as 34 | /// loading finished without getting an image. 35 | public protocol Placeholder { 36 | 37 | /// How the placeholder should be added to a given image view. 38 | func add(to imageView: ImageView) 39 | 40 | /// How the placeholder should be removed from a given image view. 41 | func remove(from imageView: ImageView) 42 | } 43 | 44 | /// Default implementation of an image placeholder. The image will be set or 45 | /// reset directly for `image` property of the image view. 46 | extension Image: Placeholder { 47 | /// How the placeholder should be added to a given image view. 48 | public func add(to imageView: ImageView) { imageView.image = self } 49 | 50 | /// How the placeholder should be removed from a given image view. 51 | public func remove(from imageView: ImageView) { imageView.image = nil } 52 | } 53 | 54 | /// Default implementation of an arbitrary view as placeholder. The view will be 55 | /// added as a subview when adding and be removed from its super view when removing. 56 | /// 57 | /// To use your customize View type as placeholder, simply let it conforming to 58 | /// `Placeholder` by `extension MyView: Placeholder {}`. 59 | extension Placeholder where Self: View { 60 | 61 | /// How the placeholder should be added to a given image view. 62 | public func add(to imageView: ImageView) { 63 | imageView.addSubview(self) 64 | translatesAutoresizingMaskIntoConstraints = false 65 | 66 | centerXAnchor.constraint(equalTo: imageView.centerXAnchor).isActive = true 67 | centerYAnchor.constraint(equalTo: imageView.centerYAnchor).isActive = true 68 | heightAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true 69 | widthAnchor.constraint(equalTo: imageView.widthAnchor).isActive = true 70 | } 71 | 72 | /// How the placeholder should be removed from a given image view. 73 | public func remove(from imageView: ImageView) { 74 | removeFromSuperview() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Kingfisher.h: -------------------------------------------------------------------------------- 1 | // 2 | // Kingfisher.h 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | //! Project version number for Kingfisher. 30 | FOUNDATION_EXPORT double KingfisherVersionNumber; 31 | 32 | //! Project version string for Kingfisher. 33 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 34 | 35 | // In this header, you should import all the public headers of your framework using statements like #import 36 | 37 | 38 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Networking/AuthenticationChallengeResponsable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthenticationChallengeResponsable.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2018/10/11. 6 | // 7 | // Copyright (c) 2018年 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Protocol indicates that an authentication challenge could be handled. 30 | public protocol AuthenticationChallengeResponsable: AnyObject { 31 | 32 | /// Called when a session level authentication challenge is received. 33 | /// This method provide a chance to handle and response to the authentication 34 | /// challenge before downloading could start. 35 | /// 36 | /// - Parameters: 37 | /// - downloader: The downloader which receives this challenge. 38 | /// - challenge: An object that contains the request for authentication. 39 | /// - completionHandler: A handler that your delegate method must call. 40 | /// 41 | /// - Note: This method is a forward from `URLSessionDelegate.urlSession(:didReceiveChallenge:completionHandler:)`. 42 | /// Please refer to the document of it in `URLSessionDelegate`. 43 | func downloader( 44 | _ downloader: ImageDownloader, 45 | didReceive challenge: URLAuthenticationChallenge, 46 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 47 | 48 | /// Called when a task level authentication challenge is received. 49 | /// This method provide a chance to handle and response to the authentication 50 | /// challenge before downloading could start. 51 | /// 52 | /// - Parameters: 53 | /// - downloader: The downloader which receives this challenge. 54 | /// - task: The task whose request requires authentication. 55 | /// - challenge: An object that contains the request for authentication. 56 | /// - completionHandler: A handler that your delegate method must call. 57 | func downloader( 58 | _ downloader: ImageDownloader, 59 | task: URLSessionTask, 60 | didReceive challenge: URLAuthenticationChallenge, 61 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 62 | } 63 | 64 | extension AuthenticationChallengeResponsable { 65 | 66 | public func downloader( 67 | _ downloader: ImageDownloader, 68 | didReceive challenge: URLAuthenticationChallenge, 69 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 70 | { 71 | if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 72 | if let trustedHosts = downloader.trustedHosts, trustedHosts.contains(challenge.protectionSpace.host) { 73 | let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!) 74 | completionHandler(.useCredential, credential) 75 | return 76 | } 77 | } 78 | 79 | completionHandler(.performDefaultHandling, nil) 80 | } 81 | 82 | public func downloader( 83 | _ downloader: ImageDownloader, 84 | task: URLSessionTask, 85 | didReceive challenge: URLAuthenticationChallenge, 86 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 87 | { 88 | completionHandler(.performDefaultHandling, nil) 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Networking/ImageDataProcessor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageDataProcessor.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2018/10/11. 6 | // 7 | // Copyright (c) 2018年 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | private let sharedProcessingQueue: CallbackQueue = 30 | .dispatch(DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Process")) 31 | 32 | // Handles image processing work on an own process queue. 33 | class ImageDataProcessor { 34 | let data: Data 35 | let callbacks: [SessionDataTask.TaskCallback] 36 | let queue: CallbackQueue 37 | 38 | // Note: We have an optimization choice there, to reduce queue dispatch by checking callback 39 | // queue settings in each option... 40 | let onImageProcessed = Delegate<(Result, SessionDataTask.TaskCallback), Void>() 41 | 42 | init(data: Data, callbacks: [SessionDataTask.TaskCallback], processingQueue: CallbackQueue?) { 43 | self.data = data 44 | self.callbacks = callbacks 45 | self.queue = processingQueue ?? sharedProcessingQueue 46 | } 47 | 48 | func process() { 49 | queue.execute(doProcess) 50 | } 51 | 52 | private func doProcess() { 53 | var processedImages = [String: Image]() 54 | for callback in callbacks { 55 | let processor = callback.options.processor 56 | var image = processedImages[processor.identifier] 57 | if image == nil { 58 | image = processor.process(item: .data(data), options: callback.options) 59 | processedImages[processor.identifier] = image 60 | } 61 | 62 | let result: Result 63 | if let image = image { 64 | let imageModifier = callback.options.imageModifier 65 | var finalImage = imageModifier.modify(image) 66 | if callback.options.backgroundDecode { 67 | finalImage = finalImage.kf.decoded 68 | } 69 | result = .success(finalImage) 70 | } else { 71 | let error = KingfisherError.processorError( 72 | reason: .processingFailed(processor: processor, item: .data(data))) 73 | result = .failure(error) 74 | } 75 | onImageProcessed.call((result, callback)) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Networking/ImageDownloaderDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageDownloaderDelegate.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2018/10/11. 6 | // 7 | // Copyright (c) 2018年 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Protocol of `ImageDownloader`. This protocol provides a set of methods which are related to image downloader 30 | /// working stages and rules. 31 | public protocol ImageDownloaderDelegate: AnyObject { 32 | 33 | /// Called when the `ImageDownloader` object will start downloading an image from a specified URL. 34 | /// 35 | /// - Parameters: 36 | /// - downloader: The `ImageDownloader` object which is used for the downloading operation. 37 | /// - url: URL of the starting request. 38 | /// - request: The request object for the download process. 39 | /// 40 | func imageDownloader(_ downloader: ImageDownloader, willDownloadImageForURL url: URL, with request: URLRequest?) 41 | 42 | /// Called when the `ImageDownloader` completes a downloading request with success or failure. 43 | /// 44 | /// - Parameters: 45 | /// - downloader: The `ImageDownloader` object which is used for the downloading operation. 46 | /// - url: URL of the original request URL. 47 | /// - response: The response object of the downloading process. 48 | /// - error: The error in case of failure. 49 | /// 50 | func imageDownloader( 51 | _ downloader: ImageDownloader, 52 | didFinishDownloadingImageForURL url: URL, 53 | with response: URLResponse?, 54 | error: Error?) 55 | 56 | /// Called when the `ImageDownloader` object successfully downloaded image data from specified URL. This is 57 | /// your last chance to verify or modify the downloaded data before Kingfisher tries to perform addition 58 | /// processing on the image data. 59 | /// 60 | /// - Parameters: 61 | /// - downloader: The `ImageDownloader` object which is used for the downloading operation. 62 | /// - data: The original downloaded data. 63 | /// - url: The URL of the original request URL. 64 | /// - Returns: The data from which Kingfisher should use to create an image. You need to provide valid data 65 | /// which content is one of the supported image file format. Kingfisher will perform process on this 66 | /// data and try to convert it to an image object. 67 | /// - Note: 68 | /// This can be used to pre-process raw image data before creation of `Image` instance (i.e. 69 | /// decrypting or verification). If `nil` returned, the processing is interrupted and a `KingfisherError` with 70 | /// `ResponseErrorReason.dataModifyingFailed` will be raised. You could use this fact to stop the image 71 | /// processing flow if you find the data is corrupted or malformed. 72 | func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? 73 | 74 | /// Called when the `ImageDownloader` object successfully downloads and processes an image from specified URL. 75 | /// 76 | /// - Parameters: 77 | /// - downloader: The `ImageDownloader` object which is used for the downloading operation. 78 | /// - image: The downloaded and processed image. 79 | /// - url: URL of the original request URL. 80 | /// - response: The original response object of the downloading process. 81 | /// 82 | func imageDownloader( 83 | _ downloader: ImageDownloader, 84 | didDownload image: Image, 85 | for url: URL, 86 | with response: URLResponse?) 87 | 88 | /// Checks if a received HTTP status code is valid or not. 89 | /// By default, a status code in range 200..<400 is considered as valid. 90 | /// If an invalid code is received, the downloader will raise an `KingfisherError` with 91 | /// `ResponseErrorReason.invalidHTTPStatusCode` as its reason. 92 | /// 93 | /// - Parameters: 94 | /// - code: The received HTTP status code. 95 | /// - downloader: The `ImageDownloader` object asks for validate status code. 96 | /// - Returns: Returns a value to indicate whether this HTTP status code is valid or not. 97 | /// - Note: If the default 200 to 400 valid code does not suit your need, 98 | /// you can implement this method to change that behavior. 99 | func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool 100 | } 101 | 102 | // Default implementation for `ImageDownloaderDelegate`. 103 | extension ImageDownloaderDelegate { 104 | public func imageDownloader( 105 | _ downloader: ImageDownloader, 106 | willDownloadImageForURL url: URL, 107 | with request: URLRequest?) {} 108 | 109 | public func imageDownloader( 110 | _ downloader: ImageDownloader, 111 | didFinishDownloadingImageForURL url: URL, 112 | with response: URLResponse?, 113 | error: Error?) {} 114 | 115 | public func imageDownloader( 116 | _ downloader: ImageDownloader, 117 | didDownload image: Image, 118 | for url: URL, 119 | with response: URLResponse?) {} 120 | 121 | public func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool { 122 | return (200..<400).contains(code) 123 | } 124 | public func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? { 125 | return data 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Networking/ImageModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Ethan Gill on 2017/11/28. 6 | // 7 | // Copyright (c) 2018 Ethan Gill 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// An `ImageModifier` can be used to change properties on an image in between 30 | /// cache serialization and use of the image. The modified returned image will be 31 | /// only used for current rendering purpose, the serialization data will not contain 32 | /// the changes applied by the `ImageModifier`. 33 | public protocol ImageModifier { 34 | /// Modify an input `Image`. 35 | /// 36 | /// - parameter image: Image which will be modified by `self` 37 | /// 38 | /// - returns: The modified image. 39 | /// 40 | /// - Note: The return value will be unmodified if modifying is not possible on 41 | /// the current platform. 42 | /// - Note: Most modifiers support UIImage or NSImage, but not CGImage. 43 | func modify(_ image: Image) -> Image 44 | } 45 | 46 | extension ImageModifier { 47 | func modify(_ image: Image?) -> Image? { 48 | guard let image = image else { 49 | return nil 50 | } 51 | return modify(image) 52 | } 53 | } 54 | 55 | /// The default modifier. 56 | /// It does nothing and returns the image as is. 57 | public struct DefaultImageModifier: ImageModifier { 58 | 59 | /// A default `DefaultImageModifier` which can be used everywhere. 60 | public static let `default` = DefaultImageModifier() 61 | private init() {} 62 | 63 | /// Modifies an input `Image`. See `ImageModifier` protocol for more. 64 | public func modify(_ image: Image) -> Image { return image } 65 | } 66 | 67 | /// A wrapper for creating an `ImageModifier` easier. 68 | /// This type conforms to `ImageModifier` and wraps an image modify block. 69 | /// If the `block` throws an error, the original image will be used. 70 | public struct AnyImageModifier: ImageModifier { 71 | 72 | /// A block which modifies images, or returns the original image 73 | /// if modification cannot be performed with an error. 74 | let block: (Image) throws -> Image 75 | 76 | /// Creates an `AnyImageModifier` with a given `modify` block. 77 | public init(modify: @escaping (Image) throws -> Image) { 78 | block = modify 79 | } 80 | 81 | /// Modify an input `Image`. See `ImageModifier` protocol for more. 82 | public func modify(_ image: Image) -> Image { 83 | return (try? block(image)) ?? image 84 | } 85 | } 86 | 87 | #if os(iOS) || os(tvOS) || os(watchOS) 88 | import UIKit 89 | 90 | /// Modifier for setting the rendering mode of images. 91 | public struct RenderingModeImageModifier: ImageModifier { 92 | 93 | /// The rendering mode to apply to the image. 94 | public let renderingMode: UIImage.RenderingMode 95 | 96 | /// Creates a `RenderingModeImageModifier`. 97 | /// 98 | /// - Parameter renderingMode: The rendering mode to apply to the image. Default is `.automatic`. 99 | public init(renderingMode: UIImage.RenderingMode = .automatic) { 100 | self.renderingMode = renderingMode 101 | } 102 | 103 | /// Modify an input `Image`. See `ImageModifier` protocol for more. 104 | public func modify(_ image: Image) -> Image { 105 | return image.withRenderingMode(renderingMode) 106 | } 107 | } 108 | 109 | /// Modifier for setting the `flipsForRightToLeftLayoutDirection` property of images. 110 | public struct FlipsForRightToLeftLayoutDirectionImageModifier: ImageModifier { 111 | 112 | /// Creates a `FlipsForRightToLeftLayoutDirectionImageModifier`. 113 | public init() {} 114 | 115 | /// Modify an input `Image`. See `ImageModifier` protocol for more. 116 | public func modify(_ image: Image) -> Image { 117 | return image.imageFlippedForRightToLeftLayoutDirection() 118 | } 119 | } 120 | 121 | /// Modifier for setting the `alignmentRectInsets` property of images. 122 | public struct AlignmentRectInsetsImageModifier: ImageModifier { 123 | 124 | /// The alignment insets to apply to the image 125 | public let alignmentInsets: UIEdgeInsets 126 | 127 | /// Creates an `AlignmentRectInsetsImageModifier`. 128 | public init(alignmentInsets: UIEdgeInsets) { 129 | self.alignmentInsets = alignmentInsets 130 | } 131 | 132 | /// Modify an input `Image`. See `ImageModifier` protocol for more. 133 | public func modify(_ image: Image) -> Image { 134 | return image.withAlignmentRectInsets(alignmentInsets) 135 | } 136 | } 137 | #endif 138 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Networking/RequestModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/09/05. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Represents and wraps a method for modifying request before an image download request starts. 30 | public protocol ImageDownloadRequestModifier { 31 | 32 | /// A method will be called just before the `request` being sent. 33 | /// This is the last chance you can modify the image download request. You can modify the request for some 34 | /// customizing purpose, such as adding auth token to the header, do basic HTTP auth or something like url mapping. 35 | /// 36 | /// Usually, you pass an `ImageDownloadRequestModifier` as the associated value of 37 | /// `KingfisherOptionsInfoItem.requestModifier` and use it as the `options` parameter in related methods. 38 | /// 39 | /// If you do nothing with the input `request` and return it as is, a downloading process will start with it. 40 | /// 41 | /// - Parameter request: The input request contains necessary information like `url`. This request is generated 42 | /// according to your resource url as a GET request. 43 | /// - Returns: A modified version of request, which you wish to use for downloading an image. If `nil` returned, 44 | /// a `KingfisherError.requestError` with `.emptyRequest` as its reason will occur. 45 | /// 46 | func modified(for request: URLRequest) -> URLRequest? 47 | } 48 | 49 | struct NoModifier: ImageDownloadRequestModifier { 50 | static let `default` = NoModifier() 51 | private init() {} 52 | func modified(for request: URLRequest) -> URLRequest? { 53 | return request 54 | } 55 | } 56 | 57 | /// A wrapper for creating an `ImageDownloadRequestModifier` easier. 58 | /// This type conforms to `ImageDownloadRequestModifier` and wraps an image modify block. 59 | public struct AnyModifier: ImageDownloadRequestModifier { 60 | 61 | let block: (URLRequest) -> URLRequest? 62 | 63 | /// For `ImageDownloadRequestModifier` conformation. 64 | public func modified(for request: URLRequest) -> URLRequest? { 65 | return block(request) 66 | } 67 | 68 | /// Creates a value of `ImageDownloadRequestModifier` which runs `modify` block. 69 | /// 70 | /// - Parameter modify: The request modifying block runs when a request modifying task comes. 71 | /// The return `URLRequest?` value of this block will be used as the image download request. 72 | /// If `nil` returned, a `KingfisherError.requestError` with `.emptyRequest` as its 73 | /// reason will occur. 74 | public init(modify: @escaping (URLRequest) -> URLRequest?) { 75 | block = modify 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Networking/SessionDataTask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SessionDataTask.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2018/11/1. 6 | // 7 | // Copyright (c) 2018年 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 onProgress: Delegate<(Int64, Int64), Void>? 38 | let onCompleted: Delegate, Void>? 39 | let options: KingfisherParsedOptionsInfo 40 | } 41 | 42 | /// Downloaded raw data of current task. 43 | public private(set) var mutableData: Data 44 | 45 | /// The underlying download task. It is only for debugging purpose when you encountered an error. You should not 46 | /// modify the content of this task or start it yourself. 47 | public let task: URLSessionDataTask 48 | private var callbacksStore = [CancelToken: TaskCallback]() 49 | 50 | var callbacks: Dictionary.Values { 51 | return callbacksStore.values 52 | } 53 | 54 | private var currentToken = 0 55 | private let lock = NSLock() 56 | 57 | let onTaskDone = Delegate<(Result<(Data, URLResponse?), KingfisherError>, [TaskCallback]), Void>() 58 | let onCallbackCancelled = Delegate<(CancelToken, TaskCallback), Void>() 59 | 60 | var started = false 61 | var containsCallbacks: Bool { 62 | // We should be able to use `task.state != .running` to check it. 63 | // However, in some rare cases, cancelling the task does not change 64 | // task state to `.cancelling` immediately, but still in `.running`. 65 | // So we need to check callbacks count to for sure that it is safe to remove the 66 | // task in delegate. 67 | return !callbacks.isEmpty 68 | } 69 | 70 | init(task: URLSessionDataTask) { 71 | self.task = task 72 | mutableData = Data() 73 | } 74 | 75 | func addCallback(_ callback: TaskCallback) -> CancelToken { 76 | lock.lock() 77 | defer { lock.unlock() } 78 | callbacksStore[currentToken] = callback 79 | defer { currentToken += 1 } 80 | return currentToken 81 | } 82 | 83 | func removeCallback(_ token: CancelToken) -> TaskCallback? { 84 | lock.lock() 85 | defer { lock.unlock() } 86 | if let callback = callbacksStore[token] { 87 | callbacksStore[token] = nil 88 | return callback 89 | } 90 | return nil 91 | } 92 | 93 | func resume() { 94 | guard !started else { return } 95 | started = true 96 | task.resume() 97 | } 98 | 99 | func cancel(token: CancelToken) { 100 | guard let callback = removeCallback(token) else { 101 | return 102 | } 103 | if callbacksStore.count == 0 { 104 | task.cancel() 105 | } 106 | onCallbackCancelled.call((token, callback)) 107 | } 108 | 109 | func forceCancel() { 110 | for token in callbacksStore.keys { 111 | cancel(token: token) 112 | } 113 | } 114 | 115 | func didReceiveData(_ data: Data) { 116 | mutableData.append(data) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Utility/Box.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Box.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2018/3/17. 6 | // Copyright (c) 2018 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 | let value: T 30 | 31 | init(_ value: T) { 32 | self.value = value 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Utility/CallbackQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CallbackQueue.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2018/10/15. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Represents callback queue behaviors when an calling of closure be dispatched. 30 | /// 31 | /// - asyncMain: Dispatch the calling to `DispatchQueue.main` with an `async` behavior. 32 | /// - currentMainOrAsync: Dispatch the calling to `DispatchQueue.main` with an `async` behavior if current queue is not 33 | /// `.main`. Otherwise, call the closure immediately in current main queue. 34 | /// - untouch: Do not change the calling queue for closure. 35 | /// - dispatch: Dispatches to a specified `DispatchQueue`. 36 | public enum CallbackQueue { 37 | /// Dispatch the calling to `DispatchQueue.main` with an `async` behavior. 38 | case mainAsync 39 | /// Dispatch the calling to `DispatchQueue.main` with an `async` behavior if current queue is not 40 | /// `.main`. Otherwise, call the closure immediately in current main queue. 41 | case mainCurrentOrAsync 42 | /// Do not change the calling queue for closure. 43 | case untouch 44 | /// Dispatches to a specified `DispatchQueue`. 45 | case dispatch(DispatchQueue) 46 | 47 | public func execute(_ block: @escaping () -> Void) { 48 | switch self { 49 | case .mainAsync: 50 | DispatchQueue.main.async { block() } 51 | case .mainCurrentOrAsync: 52 | DispatchQueue.main.safeAsync { block() } 53 | case .untouch: 54 | block() 55 | case .dispatch(let queue): 56 | queue.async { block() } 57 | } 58 | } 59 | 60 | var queue: DispatchQueue { 61 | switch self { 62 | case .mainAsync: return .main 63 | case .mainCurrentOrAsync: return .main 64 | case .untouch: return OperationQueue.current?.underlyingQueue ?? .main 65 | case .dispatch(let queue): return queue 66 | } 67 | } 68 | } 69 | 70 | extension DispatchQueue { 71 | // This method will dispatch the `block` to self. 72 | // If `self` is the main queue, and current thread is main thread, the block 73 | // will be invoked immediately instead of being dispatched. 74 | func safeAsync(_ block: @escaping ()->()) { 75 | if self === DispatchQueue.main && Thread.isMainThread { 76 | block() 77 | } else { 78 | async { block() } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Utility/Delegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Delegate.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2018/10/10. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// A delegate helper type to "shadow" weak `self`, to prevent creating an unexpected retain cycle. 30 | class Delegate { 31 | init() {} 32 | 33 | private var block: ((Input) -> Output?)? 34 | 35 | func delegate(on target: T, block: ((T, Input) -> Output)?) { 36 | // The `target` is weak inside block, so you do not need to worry about it in the caller side. 37 | self.block = { [weak target] input in 38 | guard let target = target else { return nil } 39 | return block?(target, input) 40 | } 41 | } 42 | 43 | func call(_ input: Input) -> Output? { 44 | return block?(input) 45 | } 46 | } 47 | 48 | extension Delegate where Input == Void { 49 | // To make syntax better for `Void` input. 50 | func call() -> Output? { 51 | return call(()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Utility/ExtensionHelpers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionHelpers.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2018/09/28. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | extension Float { 30 | var isEven: Bool { 31 | return truncatingRemainder(dividingBy: 2.0) == 0 32 | } 33 | } 34 | 35 | #if canImport(AppKit) 36 | import AppKit 37 | extension NSBezierPath { 38 | convenience init(roundedRect rect: NSRect, topLeftRadius: CGFloat, topRightRadius: CGFloat, 39 | bottomLeftRadius: CGFloat, bottomRightRadius: CGFloat) 40 | { 41 | self.init() 42 | 43 | let maxCorner = min(rect.width, rect.height) / 2 44 | 45 | let radiusTopLeft = min(maxCorner, max(0, topLeftRadius)) 46 | let radiusTopRight = min(maxCorner, max(0, topRightRadius)) 47 | let radiusBottomLeft = min(maxCorner, max(0, bottomLeftRadius)) 48 | let radiusBottomRight = min(maxCorner, max(0, bottomRightRadius)) 49 | 50 | guard !rect.isEmpty else { 51 | return 52 | } 53 | 54 | let topLeft = NSPoint(x: rect.minX, y: rect.maxY) 55 | let topRight = NSPoint(x: rect.maxX, y: rect.maxY) 56 | let bottomRight = NSPoint(x: rect.maxX, y: rect.minY) 57 | 58 | move(to: NSPoint(x: rect.midX, y: rect.maxY)) 59 | appendArc(from: topLeft, to: rect.origin, radius: radiusTopLeft) 60 | appendArc(from: rect.origin, to: bottomRight, radius: radiusBottomLeft) 61 | appendArc(from: bottomRight, to: topRight, radius: radiusBottomRight) 62 | appendArc(from: topRight, to: topLeft, radius: radiusTopRight) 63 | close() 64 | } 65 | 66 | convenience init(roundedRect rect: NSRect, byRoundingCorners corners: RectCorner, radius: CGFloat) { 67 | let radiusTopLeft = corners.contains(.topLeft) ? radius : 0 68 | let radiusTopRight = corners.contains(.topRight) ? radius : 0 69 | let radiusBottomLeft = corners.contains(.bottomLeft) ? radius : 0 70 | let radiusBottomRight = corners.contains(.bottomRight) ? radius : 0 71 | 72 | self.init(roundedRect: rect, topLeftRadius: radiusTopLeft, topRightRadius: radiusTopRight, 73 | bottomLeftRadius: radiusBottomLeft, bottomRightRadius: radiusBottomRight) 74 | } 75 | } 76 | 77 | extension Image { 78 | // macOS does not support scale. This is just for code compatibility across platforms. 79 | convenience init?(data: Data, scale: CGFloat) { 80 | self.init(data: data) 81 | } 82 | } 83 | #endif 84 | 85 | #if canImport(UIKit) 86 | import UIKit 87 | extension RectCorner { 88 | var uiRectCorner: UIRectCorner { 89 | 90 | var result: UIRectCorner = [] 91 | 92 | if contains(.topLeft) { result.insert(.topLeft) } 93 | if contains(.topRight) { result.insert(.topRight) } 94 | if contains(.bottomLeft) { result.insert(.bottomLeft) } 95 | if contains(.bottomRight) { result.insert(.bottomRight) } 96 | 97 | return result 98 | } 99 | } 100 | #endif 101 | 102 | extension Date { 103 | var isPast: Bool { 104 | return isPast(referenceDate: Date()) 105 | } 106 | 107 | var isFuture: Bool { 108 | return !isPast 109 | } 110 | 111 | func isPast(referenceDate: Date) -> Bool { 112 | return timeIntervalSince(referenceDate) <= 0 113 | } 114 | 115 | func isFuture(referenceDate: Date) -> Bool { 116 | return !isPast(referenceDate: referenceDate) 117 | } 118 | 119 | // `Date` in memory is a wrap for `TimeInterval`. But in file attribute it can only accept `Int` number. 120 | // By default the system will `round` it. But it is not friendly for testing purpose. 121 | // So we always `ceil` the value when used for file attributes. 122 | var fileAttributeDate: Date { 123 | return Date(timeIntervalSince1970: ceil(timeIntervalSince1970)) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Utility/Runtime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Runtime.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2018/10/12. 6 | // 7 | // Copyright (c) 2018年 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | func getAssociatedObject(_ object: Any, _ key: UnsafeRawPointer) -> T? { 30 | return objc_getAssociatedObject(object, key) as? T 31 | } 32 | 33 | func setRetainedAssociatedObject(_ object: Any, _ key: UnsafeRawPointer, _ value: T) { 34 | objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 35 | } 36 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Utility/SizeExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SizeExtensions.swift 3 | // Kingfisher 4 | // 5 | // Created by onevcat on 2018/09/28. 6 | // 7 | // Copyright (c) 2018 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: KingfisherCompatible {} 30 | extension KingfisherWrapper where Base == CGSize { 31 | 32 | /// Returns a size by resizing the `base` size to a target size under a given content mode. 33 | /// 34 | /// - Parameters: 35 | /// - size: The target size to resize to. 36 | /// - contentMode: Content mode of the target size should be when resizing. 37 | /// - Returns: The resized size under the given `ContentMode`. 38 | public func resize(to size: CGSize, for contentMode: ContentMode) -> CGSize { 39 | switch contentMode { 40 | case .aspectFit: 41 | return constrained(size) 42 | case .aspectFill: 43 | return filling(size) 44 | case .none: 45 | return size 46 | } 47 | } 48 | 49 | /// Returns a size by resizing the `base` size by making it aspect fitting the given `size`. 50 | /// 51 | /// - Parameter size: The size in which the `base` should fit in. 52 | /// - Returns: The size fitted in by the input `size`, while keeps `base` aspect. 53 | public func constrained(_ size: CGSize) -> CGSize { 54 | let aspectWidth = round(aspectRatio * size.height) 55 | let aspectHeight = round(size.width / aspectRatio) 56 | 57 | return aspectWidth > size.width ? 58 | CGSize(width: size.width, height: aspectHeight) : 59 | CGSize(width: aspectWidth, height: size.height) 60 | } 61 | 62 | /// Returns a size by resizing the `base` size by making it aspect filling the given `size`. 63 | /// 64 | /// - Parameter size: The size in which the `base` should fill. 65 | /// - Returns: The size be filled by the input `size`, while keeps `base` aspect. 66 | public func filling(_ size: CGSize) -> CGSize { 67 | let aspectWidth = round(aspectRatio * size.height) 68 | let aspectHeight = round(size.width / aspectRatio) 69 | 70 | return aspectWidth < size.width ? 71 | CGSize(width: size.width, height: aspectHeight) : 72 | CGSize(width: aspectWidth, height: size.height) 73 | } 74 | 75 | /// Returns a `CGRect` for which the `base` size is constrained to an input `size` at a given `anchor` point. 76 | /// 77 | /// - Parameters: 78 | /// - size: The size in which the `base` should be constrained to. 79 | /// - anchor: An anchor point in which the size constraint should happen. 80 | /// - Returns: The result `CGRect` for the constraint operation. 81 | public func constrainedRect(for size: CGSize, anchor: CGPoint) -> CGRect { 82 | 83 | let unifiedAnchor = CGPoint(x: anchor.x.clamped(to: 0.0...1.0), 84 | y: anchor.y.clamped(to: 0.0...1.0)) 85 | 86 | let x = unifiedAnchor.x * base.width - unifiedAnchor.x * size.width 87 | let y = unifiedAnchor.y * base.height - unifiedAnchor.y * size.height 88 | let r = CGRect(x: x, y: y, width: size.width, height: size.height) 89 | 90 | let ori = CGRect(origin: .zero, size: base) 91 | return ori.intersection(r) 92 | } 93 | 94 | private var aspectRatio: CGFloat { 95 | return base.height == 0.0 ? 1.0 : base.width / base.height 96 | } 97 | } 98 | 99 | extension CGRect { 100 | func scaled(_ scale: CGFloat) -> CGRect { 101 | return CGRect(x: origin.x * scale, y: origin.y * scale, 102 | width: size.width * scale, height: size.height * scale) 103 | } 104 | } 105 | 106 | extension Comparable { 107 | func clamped(to limits: ClosedRange) -> Self { 108 | return min(max(self, limits.lowerBound), limits.upperBound) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Utility/String+MD5.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+MD5.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 18//25. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | import CommonCrypto 29 | 30 | extension String: KingfisherCompatible { } 31 | extension KingfisherWrapper where Base == String { 32 | var md5: String { 33 | guard let data = base.data(using: .utf8) else { 34 | return base 35 | } 36 | var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH)) 37 | _ = data.withUnsafeBytes { bytes in 38 | return CC_MD5(bytes, CC_LONG(data.count), &digest) 39 | } 40 | 41 | return digest.map { String(format: "%02x", $0) }.joined() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/Sources/Views/Indicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Indicator.swift 3 | // Kingfisher 4 | // 5 | // Created by João D. Moreira on 30/08/16. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if canImport(AppKit) 28 | import AppKit 29 | public typealias IndicatorView = NSView 30 | #else 31 | import UIKit 32 | public typealias IndicatorView = UIView 33 | #endif 34 | 35 | /// Represents the activity indicator type which should be added to 36 | /// an image view when an image is being downloaded. 37 | /// 38 | /// - none: No indicator. 39 | /// - activity: Uses the system activity indicator. 40 | /// - image: Uses an image as indicator. GIF is supported. 41 | /// - custom: Uses a custom indicator. The type of associated value should conform to the `Indicator` protocol. 42 | public enum IndicatorType { 43 | /// No indicator. 44 | case none 45 | /// Uses the system activity indicator. 46 | case activity 47 | /// Uses an image as indicator. GIF is supported. 48 | case image(imageData: Data) 49 | /// Uses a custom indicator. The type of associated value should conform to the `Indicator` protocol. 50 | case custom(indicator: Indicator) 51 | } 52 | 53 | /// An indicator type which can be used to show the download task is in progress. 54 | public protocol Indicator { 55 | 56 | /// Called when the indicator should start animating. 57 | func startAnimatingView() 58 | 59 | /// Called when the indicator should stop animating. 60 | func stopAnimatingView() 61 | 62 | /// Center offset of the indicator. Kingfisher will use this value to determine the position of 63 | /// indicator in the super view. 64 | var centerOffset: CGPoint { get } 65 | 66 | /// The indicator view which would be added to the super view. 67 | var view: IndicatorView { get } 68 | } 69 | 70 | extension Indicator { 71 | 72 | /// Default implementation of `centerOffset` of `Indicator`. The default value is `.zero`, means that there is 73 | /// no offset for the indicator view. 74 | public var centerOffset: CGPoint { return .zero } 75 | } 76 | 77 | // Displays a NSProgressIndicator / UIActivityIndicatorView 78 | final class ActivityIndicator: Indicator { 79 | 80 | #if os(macOS) 81 | private let activityIndicatorView: NSProgressIndicator 82 | #else 83 | private let activityIndicatorView: UIActivityIndicatorView 84 | #endif 85 | private var animatingCount = 0 86 | 87 | var view: IndicatorView { 88 | return activityIndicatorView 89 | } 90 | 91 | func startAnimatingView() { 92 | if animatingCount == 0 { 93 | #if os(macOS) 94 | activityIndicatorView.startAnimation(nil) 95 | #else 96 | activityIndicatorView.startAnimating() 97 | #endif 98 | activityIndicatorView.isHidden = false 99 | } 100 | animatingCount += 1 101 | } 102 | 103 | func stopAnimatingView() { 104 | animatingCount = max(animatingCount - 1, 0) 105 | if animatingCount == 0 { 106 | #if os(macOS) 107 | activityIndicatorView.stopAnimation(nil) 108 | #else 109 | activityIndicatorView.stopAnimating() 110 | #endif 111 | activityIndicatorView.isHidden = true 112 | } 113 | } 114 | 115 | init() { 116 | #if os(macOS) 117 | activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16)) 118 | activityIndicatorView.controlSize = .small 119 | activityIndicatorView.style = .spinning 120 | #else 121 | #if os(tvOS) 122 | let indicatorStyle = UIActivityIndicatorView.Style.white 123 | #else 124 | let indicatorStyle = UIActivityIndicatorView.Style.gray 125 | #endif 126 | #if swift(>=4.2) 127 | activityIndicatorView = UIActivityIndicatorView(style: indicatorStyle) 128 | #else 129 | activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: indicatorStyle) 130 | #endif 131 | #endif 132 | } 133 | } 134 | 135 | // MARK: - ImageIndicator 136 | // Displays an ImageView. Supports gif 137 | final class ImageIndicator: Indicator { 138 | private let animatedImageIndicatorView: ImageView 139 | 140 | var view: IndicatorView { 141 | return animatedImageIndicatorView 142 | } 143 | 144 | init?( 145 | imageData data: Data, 146 | processor: ImageProcessor = DefaultImageProcessor.default, 147 | options: KingfisherParsedOptionsInfo? = nil) 148 | { 149 | var options = options ?? KingfisherParsedOptionsInfo(nil) 150 | // Use normal image view to show animations, so we need to preload all animation data. 151 | if !options.preloadAllAnimationData { 152 | options.preloadAllAnimationData = true 153 | } 154 | 155 | guard let image = processor.process(item: .data(data), options: options) else { 156 | return nil 157 | } 158 | 159 | animatedImageIndicatorView = ImageView() 160 | animatedImageIndicatorView.image = image 161 | 162 | #if os(macOS) 163 | // Need for gif to animate on macOS 164 | animatedImageIndicatorView.imageScaling = .scaleNone 165 | animatedImageIndicatorView.canDrawSubviewsIntoLayer = true 166 | #else 167 | animatedImageIndicatorView.contentMode = .center 168 | #endif 169 | } 170 | 171 | func startAnimatingView() { 172 | #if os(macOS) 173 | animatedImageIndicatorView.animates = true 174 | #else 175 | animatedImageIndicatorView.startAnimating() 176 | #endif 177 | animatedImageIndicatorView.isHidden = false 178 | } 179 | 180 | func stopAnimatingView() { 181 | #if os(macOS) 182 | animatedImageIndicatorView.animates = false 183 | #else 184 | animatedImageIndicatorView.stopAnimating() 185 | #endif 186 | animatedImageIndicatorView.isHidden = true 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/ShelfView.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ShelfView", 3 | "version": "1.1", 4 | "summary": "iOS custom view to display books on shelf.", 5 | "description": "iOS custom view to display books on shelf, mimicking a real library.", 6 | "homepage": "https://github.com/tdscientist/ShelfView-iOS", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "Adeyinka Adediji": "tdscientist@gmail.com" 13 | }, 14 | "source": { 15 | "git": "https://github.com/tdscientist/ShelfView-iOS.git", 16 | "tag": "1.1" 17 | }, 18 | "social_media_url": "https://twitter.com/tdscientist", 19 | "platforms": { 20 | "ios": "10.0" 21 | }, 22 | "source_files": "ShelfView/Classes/**/*", 23 | "resource_bundles": { 24 | "ShelfView": [ 25 | "ShelfView/Assets/*.png" 26 | ] 27 | }, 28 | "frameworks": "UIKit", 29 | "dependencies": { 30 | "Kingfisher": [ 31 | "~> 5.0" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kingfisher (5.0.1) 3 | - ShelfView (1.1): 4 | - Kingfisher (~> 5.0) 5 | 6 | DEPENDENCIES: 7 | - ShelfView (from `../`) 8 | 9 | SPEC REPOS: 10 | https://github.com/cocoapods/specs.git: 11 | - Kingfisher 12 | 13 | EXTERNAL SOURCES: 14 | ShelfView: 15 | :path: "../" 16 | 17 | SPEC CHECKSUMS: 18 | Kingfisher: 4f771421b9208185217550528a06fd48756cb7c2 19 | ShelfView: 9ca6d37cf8d92b39b2e7d44c3f798aebd00da1ba 20 | 21 | PODFILE CHECKSUM: bcca6e02436573d29fd62d0bb8934cf4a1245178 22 | 23 | COCOAPODS: 1.5.3 24 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 5.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Kingfisher : NSObject 3 | @end 4 | @implementation PodsDummy_Kingfisher 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "Kingfisher.h" 14 | 15 | FOUNDATION_EXPORT double KingfisherVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap: -------------------------------------------------------------------------------- 1 | framework module Kingfisher { 2 | umbrella header "Kingfisher-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = -framework "Accelerate" -framework "CFNetwork" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Example/Pods-ShelfView_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Kingfisher 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2018 Wei Wang 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | 30 | ## ShelfView 31 | 32 | Copyright (c) 2017 Adeyinka Adediji 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in 42 | all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 50 | THE SOFTWARE. 51 | 52 | Generated by CocoaPods - https://cocoapods.org 53 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Example/Pods-ShelfView_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2018 Wei Wang 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | 40 | License 41 | MIT 42 | Title 43 | Kingfisher 44 | Type 45 | PSGroupSpecifier 46 | 47 | 48 | FooterText 49 | Copyright (c) 2017 Adeyinka Adediji <tdscientist@gmail.com> 50 | 51 | Permission is hereby granted, free of charge, to any person obtaining a copy 52 | of this software and associated documentation files (the "Software"), to deal 53 | in the Software without restriction, including without limitation the rights 54 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 55 | copies of the Software, and to permit persons to whom the Software is 56 | furnished to do so, subject to the following conditions: 57 | 58 | The above copyright notice and this permission notice shall be included in 59 | all copies or substantial portions of the Software. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 63 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 64 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 65 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 67 | THE SOFTWARE. 68 | 69 | License 70 | MIT 71 | Title 72 | ShelfView 73 | Type 74 | PSGroupSpecifier 75 | 76 | 77 | FooterText 78 | Generated by CocoaPods - https://cocoapods.org 79 | Title 80 | 81 | Type 82 | PSGroupSpecifier 83 | 84 | 85 | StringsTable 86 | Acknowledgements 87 | Title 88 | Acknowledgements 89 | 90 | 91 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Example/Pods-ShelfView_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ShelfView_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ShelfView_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Example/Pods-ShelfView_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | 145 | if [[ "$CONFIGURATION" == "Debug" ]]; then 146 | install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework" 147 | install_framework "${BUILT_PRODUCTS_DIR}/ShelfView/ShelfView.framework" 148 | fi 149 | if [[ "$CONFIGURATION" == "Release" ]]; then 150 | install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework" 151 | install_framework "${BUILT_PRODUCTS_DIR}/ShelfView/ShelfView.framework" 152 | fi 153 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 154 | wait 155 | fi 156 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Example/Pods-ShelfView_Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Example/Pods-ShelfView_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ShelfView_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ShelfView_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Example/Pods-ShelfView_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/ShelfView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ShelfView/ShelfView.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" -framework "ShelfView" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Example/Pods-ShelfView_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_ShelfView_Example { 2 | umbrella header "Pods-ShelfView_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Example/Pods-ShelfView_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/ShelfView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ShelfView/ShelfView.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" -framework "ShelfView" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Tests/Pods-ShelfView_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Tests/Pods-ShelfView_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Tests/Pods-ShelfView_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_ShelfView_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_ShelfView_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Tests/Pods-ShelfView_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 145 | wait 146 | fi 147 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Tests/Pods-ShelfView_Tests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Tests/Pods-ShelfView_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_ShelfView_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_ShelfView_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Tests/Pods-ShelfView_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/ShelfView" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ShelfView/ShelfView.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Tests/Pods-ShelfView_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_ShelfView_Tests { 2 | umbrella header "Pods-ShelfView_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-ShelfView_Tests/Pods-ShelfView_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/ShelfView" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ShelfView/ShelfView.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ShelfView/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.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ShelfView/ResourceBundle-ShelfView-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleIdentifier 8 | ${PRODUCT_BUNDLE_IDENTIFIER} 9 | CFBundleInfoDictionaryVersion 10 | 6.0 11 | CFBundleName 12 | ${PRODUCT_NAME} 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ShelfView/ShelfView-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_ShelfView : NSObject 3 | @end 4 | @implementation PodsDummy_ShelfView 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ShelfView/ShelfView-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ShelfView/ShelfView-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 ShelfViewVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char ShelfViewVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ShelfView/ShelfView.modulemap: -------------------------------------------------------------------------------- 1 | framework module ShelfView { 2 | umbrella header "ShelfView-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/ShelfView/ShelfView.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/ShelfView 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | OTHER_LDFLAGS = -framework "UIKit" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /Example/ShelfView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/ShelfView.xcodeproj/xcshareddata/xcschemes/ShelfView-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Example/ShelfView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/ShelfView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/ShelfView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ShelfView 4 | // 5 | // Created by tdscientist on 09/23/2017. 6 | // Copyright (c) 2017 tdscientist. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 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 | func applicationWillResignActive(_ application: UIApplication) { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | func applicationDidEnterBackground(_ application: UIApplication) { 26 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 27 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 28 | } 29 | 30 | func applicationWillEnterForeground(_ application: UIApplication) { 31 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 32 | } 33 | 34 | func applicationDidBecomeActive(_ application: UIApplication) { 35 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 36 | } 37 | 38 | func applicationWillTerminate(_ application: UIApplication) { 39 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Example/ShelfView/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/ShelfView/Base.lproj/Main.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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /Example/ShelfView/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /Example/ShelfView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 2 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UILaunchStoryboardName 31 | Main 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UIStatusBarStyle 39 | UIStatusBarStyleDefault 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | UISupportedInterfaceOrientations~ipad 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationPortraitUpsideDown 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Example/ShelfView/PlainShelfController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlainShelfController.swift 3 | // ShelfView 4 | // 5 | // Created by tdscientist on 09/23/2017. 6 | // Copyright (c) 2017 tdscientist. All rights reserved. 7 | // 8 | 9 | import ShelfView 10 | 11 | class PlainShelfController: UIViewController, PlainShelfViewDelegate { 12 | let statusBarHeight = UIApplication.shared.statusBarFrame.height 13 | var bookModel = [BookModel]() 14 | var shelfView: PlainShelfView! 15 | @IBOutlet var motherView: UIView! 16 | 17 | override func viewDidAppear(_ animated: Bool) { 18 | super.viewDidAppear(animated) 19 | shelfView.translatesAutoresizingMaskIntoConstraints = false 20 | shelfView.leftAnchor.constraint(equalTo: motherView.leftAnchor, constant: 0).isActive = true 21 | shelfView.rightAnchor.constraint(equalTo: motherView.rightAnchor, constant: 0).isActive = true 22 | shelfView.topAnchor.constraint(equalTo: motherView.topAnchor, constant: 0).isActive = true 23 | shelfView.bottomAnchor.constraint(equalTo: motherView.bottomAnchor, constant: 0).isActive = true 24 | } 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | bookModel.append(BookModel(bookCoverSource: "http://www.aidanf.net/images/learn-swift/cover-web.png", bookId: "0", bookTitle: "Learn Swift")) 30 | bookModel.append(BookModel(bookCoverSource: "https://images-na.ssl-images-amazon.com/images/I/41bUdNhz6pL._SX346_BO1,204,203,200_.jpg", bookId: "1", bookTitle: "Beginning iOS")) 31 | bookModel.append(BookModel(bookCoverSource: "https://www.packtpub.com/sites/default/files/1414OS_5764_Mastering%20Swift%203%20-%20Linux.jpg", bookId: "2", bookTitle: "Mastering Swift 3 - Linux")) 32 | bookModel.append(BookModel(bookCoverSource: "https://files.kerching.raywenderlich.com/uploads/c7f72825-5849-4d76-ba21-8d9486296119.png", bookId: "3", bookTitle: "iOS Apprentice")) 33 | 34 | shelfView = PlainShelfView(frame: CGRect(x: 0, y: statusBarHeight, width: 350, height: 500), bookModel: bookModel, bookSource: PlainShelfView.BOOK_SOURCE_URL) 35 | 36 | delay(3) { 37 | let books = [ 38 | BookModel(bookCoverSource: "https://www.packtpub.com/sites/default/files/5723cov_.png", bookId: "4", bookTitle: "Learning Xcode 8"), 39 | BookModel(bookCoverSource: "https://files.kerching.raywenderlich.com/uploads/bc41c949-c745-455e-8922-1b196fcf5e80.png", bookId: "5", bookTitle: "iOS Animations"), 40 | BookModel(bookCoverSource: "http://www.appsmith.dk/wp-content/uploads/2014/12/cover-small.jpg", bookId: "6", bookTitle: "Beginning iOS Development") 41 | ] 42 | self.shelfView.addBooks(bookModel: books) 43 | } 44 | 45 | delay(5) { 46 | let books = [ 47 | BookModel(bookCoverSource: "https://codewithchris.com/img/SwiftCourseThumbnail_v2.jpg", bookId: "7", bookTitle: "How To Make iPhone Apps"), 48 | BookModel(bookCoverSource: "http://whatpixel.com/images/2016/08/learning-swift-book-cover.jpg", bookId: "8", bookTitle: "Learning Swift"), 49 | BookModel(bookCoverSource: "https://www.packtpub.com/sites/default/files/9781785288197.png", bookId: "9", bookTitle: "Learning iOS UI Development") 50 | ] 51 | self.shelfView.addBooks(bookModel: books) 52 | } 53 | 54 | shelfView.delegate = self 55 | motherView.addSubview(shelfView) 56 | } 57 | 58 | func onBookClicked(_ shelfView: PlainShelfView, index: Int, bookId: String, bookTitle: String) { 59 | print("I just clicked \"\(bookTitle)\" with bookId \(bookId), at index \(index)") 60 | } 61 | 62 | func delay(_ delay: Double, closure: @escaping () -> ()) { 63 | DispatchQueue.main.asyncAfter( 64 | deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), 65 | execute: closure 66 | ) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Example/ShelfView/SectionShelfController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SectionShelfController.swift 3 | // ShelfView_Example 4 | // 5 | // Created by Adeyinka Adediji on 26/12/2018. 6 | // Copyright © 2018 CocoaPods. All rights reserved. 7 | // 8 | 9 | import ShelfView 10 | 11 | class SectionShelfController: UIViewController, SectionShelfViewDelegate { 12 | let statusBarHeight = UIApplication.shared.statusBarFrame.height 13 | var shelfView: SectionShelfView! 14 | @IBOutlet var motherView: UIView! 15 | 16 | override func viewDidAppear(_ animated: Bool) { 17 | super.viewDidAppear(animated) 18 | shelfView.translatesAutoresizingMaskIntoConstraints = false 19 | shelfView.leftAnchor.constraint(equalTo: motherView.leftAnchor, constant: 0).isActive = true 20 | shelfView.rightAnchor.constraint(equalTo: motherView.rightAnchor, constant: 0).isActive = true 21 | shelfView.topAnchor.constraint(equalTo: motherView.topAnchor, constant: 0).isActive = true 22 | shelfView.bottomAnchor.constraint(equalTo: motherView.bottomAnchor, constant: 0).isActive = true 23 | } 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | 28 | let books = [ 29 | BookModel(bookCoverSource: "https://files.kerching.raywenderlich.com/covers/d5693015-46b6-44f8-bf7b-7a222b28d9fe.png", bookId: "0", bookTitle: "Realm: Building Modern Swift Apps with Realm"), 30 | BookModel(bookCoverSource: "https://images-na.ssl-images-amazon.com/images/I/51R5htMRGTL._SX258_BO1,204,203,200_.jpg", bookId: "1", bookTitle: "iOS Animations by Tutorials Third Edition: iOS 10 and Swift 3 edition"), 31 | BookModel(bookCoverSource: "https://images-na.ssl-images-amazon.com/images/I/511HN-qmrWL._SX258_BO1,204,203,200_.jpg", bookId: "2", bookTitle: "RxSwift: Reactive Programming with Swift"), 32 | BookModel(bookCoverSource: "https://images-na.ssl-images-amazon.com/images/I/51%2Bc7iUzcsL._SX258_BO1,204,203,200_.jpg", bookId: "3", bookTitle: "iOS Games by Tutorials"), 33 | BookModel(bookCoverSource: "https://images-na.ssl-images-amazon.com/images/I/518idGjJQiL._SX404_BO1,204,203,200_.jpg", bookId: "4", bookTitle: "Advanced Apple Debugging & Reverse Engineering Second Edition: Exploring Apple code through LLDB, Python and DTrace 2nd Edition"), 34 | BookModel(bookCoverSource: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSGjc98oOw7zHh2Twgj0QB84bq4y0uYEm8NVq3JlWXwEsxET4Tc", bookId: "5", bookTitle: "Data Structures and Algorithms in Swift"), 35 | BookModel(bookCoverSource: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTYEkCQ_wu8HoGJzzs_gUH_FVusgI2RhntBKQ-WkmqnDJZnriwY6Q", bookId: "6", bookTitle: "iOS 10 by Tutorials: Learning the new iOS APIs with Swift 3") 36 | ] 37 | let bookModelSectionArray = [BookModelSection(sectionName: "RAYWENDERLICH", sectionId: "0", sectionBooks: books)] 38 | 39 | shelfView = SectionShelfView(frame: CGRect(x: 0, y: statusBarHeight, width: 350, height: 500), bookModelSection: bookModelSectionArray, bookSource: SectionShelfView.BOOK_SOURCE_URL) 40 | 41 | delay(3) { 42 | let books = [ 43 | BookModel(bookCoverSource: "https://www.packtpub.com/sites/default/files/cover_1.png", bookId: "7", bookTitle: "Swift iOS Programming for Kids"), 44 | BookModel(bookCoverSource: "https://www.oreilly.com/library/view/learn-ios-11/9781788390750/assets/d8e98fbc-9bdf-4d9a-ade9-e15e0b3d3265.png", bookId: "8", bookTitle: "Mastering Swift 4"), 45 | BookModel(bookCoverSource: "https://www.packtpub.com/sites/default/files/9781785288197.png", bookId: "9", bookTitle: "Learning iOS UI Development"), 46 | BookModel(bookCoverSource: "https://d1ldz4te4covpm.cloudfront.net/sites/default/files/imagecache/featured_book_block/9781784399801.png", bookId: "10", bookTitle: "Learning Unity iOS Game Development"), 47 | BookModel(bookCoverSource: "https://www.safaribooksonline.com/library/cover/9781788475709/360h/", bookId: "11", bookTitle: "Test-Driven iOS Development with Swift 4 - Third Edition [Book]"), 48 | BookModel(bookCoverSource: "https://www.packtpub.com/sites/default/files/B04036_iOS%20Game%20Development%20Essentials_.jpg", bookId: "12", bookTitle: "iOS 9 Game Development Essentials"), 49 | BookModel(bookCoverSource: "https://www.packtpub.com/sites/default/files/B05768.png", bookId: "13", bookTitle: "Swift 4 Programming Cookbook"), 50 | BookModel(bookCoverSource: "https://www.packtpub.com/sites/default/files/9781785886386.png", bookId: "14", bookTitle: "Apple Pay Essentials") 51 | ] 52 | let bookModelSection = [BookModelSection(sectionName: "PACKT", sectionId: "1", sectionBooks: books)] 53 | self.shelfView.addBooks(bookModelSection: bookModelSection) 54 | } 55 | 56 | shelfView.delegate = self 57 | motherView.addSubview(shelfView) 58 | } 59 | 60 | func onBookClicked(_ shelfView: SectionShelfView, section: Int, index: Int, sectionId: String, sectionTitle: String, bookId: String, bookTitle: String) { 61 | print("I just clicked \"\(bookTitle)\" with bookId \(bookId), at index \(index). Section details --> section \(section), sectionId \(sectionId), sectionTitle \(sectionTitle)") 62 | } 63 | 64 | func delay(_ delay: Double, closure: @escaping () -> ()) { 65 | DispatchQueue.main.asyncAfter( 66 | deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), 67 | execute: closure 68 | ) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import XCTest 3 | import ShelfView 4 | 5 | class Tests: XCTestCase { 6 | 7 | override func setUp() { 8 | super.setUp() 9 | // Put setup code here. This method is called before the invocation of each test method in the class. 10 | } 11 | 12 | override func tearDown() { 13 | // Put teardown code here. This method is called after the invocation of each test method in the class. 14 | super.tearDown() 15 | } 16 | 17 | func testExample() { 18 | // This is an example of a functional test case. 19 | XCTAssert(true, "Pass") 20 | } 21 | 22 | func testPerformanceExample() { 23 | // This is an example of a performance test case. 24 | self.measure() { 25 | // Put the code you want to measure the time of here. 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Adeyinka Adediji 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ShelfView (iOS) ## 2 | 3 | *iOS custom view to display books on shelf* ` ` **([Android version is available here](https://github.com/tdscientist/ShelfView))** 4 | 5 | 6 | 7 | Requirements 8 | -------- 9 | 10 | * iOS 10.0+ 11 | * Swift 4.2 12 | 13 | 14 | Installation 15 | -------- 16 | 17 | `ShelfView` is available through [CocoaPods](https://cocoapods.org/). To install 18 | it, simply add the following line to your Podfile: 19 | 20 | ```ruby 21 | pod 'ShelfView' 22 | ``` 23 | 24 | 25 | **Note** 26 | -------- 27 | Because of book covers whose URLs are `http`, update your `info.plist` as follows: 28 | 29 | * add `App Transport Security Settings` to the list 30 | * add `Allow Arbitrary Loads` to the security settings added above; set it to `YES`. 31 | 32 | 33 | 34 | Plain Shelf 35 | -------- 36 | 37 | ```swift 38 | import ShelfView 39 | 40 | class PlainShelfController: UIViewController, PlainShelfViewDelegate { 41 | var shelfView: PlainShelfView! 42 | 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | 46 | let books = [ 47 | BookModel(bookCoverSource: "https://files.kerching.raywenderlich.com/covers/d5693015-46b6-44f8-bf7b-7a222b28d9fe.png", 48 | bookId: "0", 49 | bookTitle: "Realm: Building Modern Swift Apps with Realm"), 50 | BookModel(bookCoverSource: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTYEkCQ_wu8HoGJzzs_gUH_FVusgI2RhntBKQ-WkmqnDJZnriwY6Q", 51 | bookId: "1", 52 | bookTitle: "iOS 10 by Tutorials: Learning the new iOS APIs with Swift 3") 53 | ] 54 | 55 | shelfView = PlainShelfView(frame: CGRect(x: 0, y: 0, width: 350, height: 500), 56 | bookModel: books, bookSource: PlainShelfView.BOOK_SOURCE_URL) 57 | 58 | shelfView.delegate = self 59 | self.view.addSubview(shelfView) 60 | } 61 | 62 | func onBookClicked(_ shelfView: PlainShelfView, index: Int, bookId: String, bookTitle: String) { 63 | print("I just clicked \"\(bookTitle)\" with bookId \(bookId), at index \(index)") 64 | } 65 | 66 | } 67 | ``` 68 | 69 | 70 | 71 | Section Shelf 72 | -------- 73 | 74 | ```swift 75 | import ShelfView 76 | 77 | class SectionShelfController: UIViewController, SectionShelfViewDelegate { 78 | var shelfView: SectionShelfView! 79 | 80 | override func viewDidLoad() { 81 | super.viewDidLoad() 82 | 83 | let books = [ 84 | BookModel(bookCoverSource: "https://files.kerching.raywenderlich.com/covers/d5693015-46b6-44f8-bf7b-7a222b28d9fe.png", 85 | bookId: "0", 86 | bookTitle: "Realm: Building Modern Swift Apps with Realm"), 87 | BookModel(bookCoverSource: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTYEkCQ_wu8HoGJzzs_gUH_FVusgI2RhntBKQ-WkmqnDJZnriwY6Q", 88 | bookId: "1", 89 | bookTitle: "iOS 10 by Tutorials: Learning the new iOS APIs with Swift 3") 90 | ] 91 | let bookModelSectionArray = [BookModelSection(sectionName: "RAYWENDERLICH", 92 | sectionId: "0", 93 | sectionBooks: books)] 94 | 95 | shelfView = SectionShelfView(frame: CGRect(x: 0, y: 0, width: 350, height: 500), 96 | bookModelSection: bookModelSectionArray, 97 | bookSource: SectionShelfView.BOOK_SOURCE_URL) 98 | 99 | shelfView.delegate = self 100 | self.view.addSubview(shelfView) 101 | } 102 | 103 | func onBookClicked(_ shelfView: SectionShelfView, section: Int, index: Int, 104 | sectionId: String, sectionTitle: String, bookId: String, 105 | bookTitle: String) { 106 | print("I just clicked \"\(bookTitle)\" with bookId \(bookId), at index \(index). Section details --> section \(section), sectionId \(sectionId), sectionTitle \(sectionTitle)") 107 | } 108 | 109 | } 110 | ``` 111 | 112 | **Add more books to ShelfView** 113 | * Plain Shelf 114 | ```swift 115 | addBooks(bookModel: [BookModel]) 116 | ``` 117 | * Section Shelf 118 | ```swift 119 | addBooks(bookModelSection: [BookModelSection]) 120 | ``` 121 | 122 | **Reload books on ShelfView** 123 | * Plain Shelf 124 | ```swift 125 | reloadBooks(bookModel: [BookModel]) 126 | ``` 127 | * Section Shelf 128 | ```swift 129 | reloadBooks(bookModelSection: [BookModelSection]) 130 | ``` 131 | 132 | 133 | 134 | **Loading book covers from other sources** 135 | 136 | * iPhone/iPad document directory 137 | 138 | ```swift 139 | let books = [ 140 | BookModel(bookCoverSource: "bookcover0.png", bookId: "0", bookTitle: "Book Title 0"), 141 | BookModel(bookCoverSource: "bookcover1.png", bookId: "1", bookTitle: "Book Title 1") 142 | ] 143 | shelfView = PlainShelfView(frame: CGRect(x: 0, y: 0, width: 350, height: 500), 144 | bookModel: books, bookSource: PlainShelfView.BOOK_SOURCE_DEVICE_DOCUMENTS) 145 | ``` 146 | 147 | 148 | 149 | * iPhone/iPad library directory 150 | 151 | ```swift 152 | let books = [ 153 | BookModel(bookCoverSource: "bookcover0.png", bookId: "0", bookTitle: "Book Title 0"), 154 | BookModel(bookCoverSource: "bookcover1.png", bookId: "1", bookTitle: "Book Title 1") 155 | ] 156 | shelfView = PlainShelfView(frame: CGRect(x: 0, y: 0, width: 350, height: 500), 157 | bookModel: books, bookSource: PlainShelfView.BOOK_SOURCE_DEVICE_LIBRARY) 158 | ``` 159 | 160 | * iPhone/iPad cache directory 161 | 162 | ```swift 163 | let books = [ 164 | BookModel(bookCoverSource: "bookcover0.png", bookId: "0", bookTitle: "Book Title 0"), 165 | BookModel(bookCoverSource: "bookcover1.png", bookId: "1", bookTitle: "Book Title 1") 166 | ] 167 | shelfView = PlainShelfView(frame: CGRect(x: 0, y: 0, width: 350, height: 500), 168 | bookModel: books, bookSource: PlainShelfView.BOOK_SOURCE_DEVICE_CACHE) 169 | ``` 170 | 171 | * Directly from your project's source code 172 | 173 | ```swift 174 | let books = [ 175 | BookModel(bookCoverSource: "bookcover0.png", bookId: "0", bookTitle: "Book Title 0"), 176 | BookModel(bookCoverSource: "bookcover1.png", bookId: "1", bookTitle: "Book Title 1") 177 | ] 178 | shelfView = PlainShelfView(frame: CGRect(x: 0, y: 0, width: 350, height: 500), 179 | bookModel: books, bookSource: PlainShelfView.BOOK_SOURCE_RAW) 180 | ``` 181 | 182 | 183 | License 184 | -------- 185 | 186 | `ShelfView` is available under the MIT license. See the LICENSE file for more info. 187 | 188 | Author 189 | -------- 190 | 191 | Adeyinka Adediji _(tdscientist@gmail.com)_ 192 | 193 | 194 | Contributions & Bug Reporting 195 | -------- 196 | 197 | tdscientist@gmail.com 198 | 199 | Credits 200 | -------- 201 | 202 | * [Kingfisher](https://github.com/onevcat/Kingfisher) 203 | 204 | -------------------------------------------------------------------------------- /ShelfView.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint ShelfView.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'ShelfView' 11 | s.version = '1.1' 12 | s.summary = 'iOS custom view to display books on shelf.' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | iOS custom view to display books on shelf, mimicking a real library. 22 | DESC 23 | 24 | s.homepage = 'https://github.com/tdscientist/ShelfView-iOS' 25 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 26 | s.license = { :type => 'MIT', :file => 'LICENSE' } 27 | s.author = { 'Adeyinka Adediji' => 'tdscientist@gmail.com' } 28 | s.source = { :git => 'https://github.com/tdscientist/ShelfView-iOS.git', :tag => s.version.to_s } 29 | s.social_media_url = 'https://twitter.com/tdscientist' 30 | 31 | s.swift_version = '4.2' 32 | s.ios.deployment_target = '10.0' 33 | 34 | s.source_files = 'ShelfView/Classes/**/*' 35 | 36 | s.resource_bundles = { 37 | 'ShelfView' => ['ShelfView/Assets/*.png'] 38 | } 39 | 40 | # s.public_header_files = 'Pod/Classes/**/*.h' 41 | s.frameworks = 'UIKit' 42 | s.dependency 'Kingfisher', '~> 5.0' 43 | end 44 | -------------------------------------------------------------------------------- /ShelfView/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdscientist/ShelfView-iOS/80b85c0548fc50a3f1cd43534a973858fbe9df2f/ShelfView/Assets/.gitkeep -------------------------------------------------------------------------------- /ShelfView/Assets/center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdscientist/ShelfView-iOS/80b85c0548fc50a3f1cd43534a973858fbe9df2f/ShelfView/Assets/center.png -------------------------------------------------------------------------------- /ShelfView/Assets/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdscientist/ShelfView-iOS/80b85c0548fc50a3f1cd43534a973858fbe9df2f/ShelfView/Assets/header.png -------------------------------------------------------------------------------- /ShelfView/Assets/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdscientist/ShelfView-iOS/80b85c0548fc50a3f1cd43534a973858fbe9df2f/ShelfView/Assets/left.png -------------------------------------------------------------------------------- /ShelfView/Assets/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdscientist/ShelfView-iOS/80b85c0548fc50a3f1cd43534a973858fbe9df2f/ShelfView/Assets/right.png -------------------------------------------------------------------------------- /ShelfView/Assets/spine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdscientist/ShelfView-iOS/80b85c0548fc50a3f1cd43534a973858fbe9df2f/ShelfView/Assets/spine.png -------------------------------------------------------------------------------- /ShelfView/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdscientist/ShelfView-iOS/80b85c0548fc50a3f1cd43534a973858fbe9df2f/ShelfView/Classes/.gitkeep -------------------------------------------------------------------------------- /ShelfView/Classes/Dimens.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dimens.swift 3 | // ShelfView 4 | // 5 | // Created by Adeyinka Adediji on 11/09/2017. 6 | // Copyright © 2017 Adeyinka Adediji. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Dimens { 12 | static let gridItemWidth = 150 13 | static let gridItemHeight = 200 14 | static let bookWidth = Double(130) 15 | static let bookHeight = Double(160) 16 | } 17 | -------------------------------------------------------------------------------- /ShelfView/Classes/ShelfCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShelfCellView.swift 3 | // ShelfView 4 | // 5 | // Created by Adeyinka Adediji on 11/09/2017. 6 | // Copyright © 2017 Adeyinka Adediji. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ShelfCellView: UICollectionViewCell { 12 | let shelfBackground = UIImageView() 13 | let bookBackground = UIView() 14 | var bookCover = UIImageView() 15 | let indicator = UIActivityIndicatorView() 16 | let spine = UIImageView() 17 | 18 | override init(frame: CGRect) { 19 | super.init(frame: frame) 20 | 21 | addSubview(shelfBackground) 22 | addSubview(bookBackground) 23 | 24 | bookBackground.addSubview(bookCover) 25 | bookBackground.addSubview(spine) 26 | bookBackground.addSubview(indicator) 27 | 28 | bookCover.layer.shadowColor = UIColor.black.cgColor 29 | bookCover.layer.shadowRadius = 10 30 | bookCover.layer.shadowOffset = CGSize(width: 0, height: 0) 31 | bookCover.layer.shadowOpacity = 0.7 32 | 33 | indicator.color = .magenta 34 | spine.image = Utils().loadImage(name: "spine") 35 | spine.isHidden = true 36 | shelfBackground.isUserInteractionEnabled = true 37 | bookCover.isUserInteractionEnabled = true 38 | } 39 | 40 | required init?(coder aDecoder: NSCoder) { 41 | fatalError("init(coder:) has not been implemented") 42 | } 43 | 44 | static var identifier: String { 45 | return String(describing: self) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ShelfView/Classes/ShelfHeaderCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShelfHeaderCellView.swift 3 | // ShelfView 4 | // 5 | // Created by Adeyinka Adediji on 28/12/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | class ShelfHeaderCellView: UICollectionReusableView { 11 | let header = UIImageView() 12 | let headerLabel = UILabel() 13 | 14 | override init(frame: CGRect) { 15 | super.init(frame: frame) 16 | header.image = Utils().loadImage(name: "header")?.resizableImage(withCapInsets: UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)) 17 | header.contentMode = .scaleToFill 18 | headerLabel.textAlignment = .center 19 | headerLabel.shadowColor = .brown 20 | headerLabel.shadowOffset = CGSize(width: 0.0, height: 1.0) 21 | addSubview(header) 22 | addSubview(headerLabel) 23 | } 24 | 25 | required init?(coder aDecoder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | 29 | static var identifier: String { 30 | return String(describing: self) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ShelfView/Classes/ShelfViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShelfViewDelegate.swift 3 | // ShelfView 4 | // 5 | // Created by Adeyinka Adediji on 14/09/2017. 6 | // Copyright © 2017 Adeyinka Adediji. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol PlainShelfViewDelegate: class { 12 | func onBookClicked(_ shelfView: PlainShelfView, index: Int, bookId: String, bookTitle: String) 13 | } 14 | 15 | public protocol SectionShelfViewDelegate: class { 16 | func onBookClicked(_ shelfView: SectionShelfView, section: Int, index: Int, sectionId: String, sectionTitle: String, bookId: String, bookTitle: String) 17 | } 18 | -------------------------------------------------------------------------------- /ShelfView/Classes/ShelfViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BookModel.swift 3 | // ShelfView 4 | // 5 | // Created by Adeyinka Adediji on 11/09/2017. 6 | // Copyright © 2017 Adeyinka Adediji. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct BookModel { 12 | var bookCoverSource: String 13 | var bookId: String 14 | var bookTitle: String 15 | 16 | public init(bookCoverSource: String, bookId: String, bookTitle: String) { 17 | self.bookCoverSource = bookCoverSource 18 | self.bookId = bookId 19 | self.bookTitle = bookTitle 20 | if bookCoverSource.isEmpty { 21 | fatalError("bookCoverSource must not be empty") 22 | } 23 | if bookId.isEmpty { 24 | fatalError("bookId must not be empty") 25 | } 26 | if bookTitle.isEmpty { 27 | fatalError("bookTitle must not be empty") 28 | } 29 | } 30 | } 31 | 32 | public struct BookModelSection { 33 | var sectionName: String 34 | var sectionId: String 35 | var sectionBooks: [BookModel] 36 | 37 | public init(sectionName: String, sectionId: String, sectionBooks: [BookModel]) { 38 | self.sectionName = sectionName 39 | self.sectionId = sectionId 40 | self.sectionBooks = sectionBooks 41 | } 42 | } 43 | 44 | struct ShelfModel { 45 | var bookCoverSource: String 46 | var bookId: String 47 | var bookTitle: String 48 | var show: Bool 49 | var type: String 50 | 51 | public init(bookCoverSource: String, bookId: String, bookTitle: String, show: Bool, type: String) { 52 | self.bookCoverSource = bookCoverSource 53 | self.bookId = bookId 54 | self.bookTitle = bookTitle 55 | self.show = show 56 | self.type = type 57 | } 58 | } 59 | 60 | struct ShelfModelSection { 61 | var sectionName: String 62 | var sectionId: String 63 | var sectionShelf: [ShelfModel] 64 | 65 | public init(sectionName: String, sectionId: String, sectionShelf: [ShelfModel]) { 66 | self.sectionName = sectionName 67 | self.sectionId = sectionId 68 | self.sectionShelf = sectionShelf 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ShelfView/Classes/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // ShelfView 4 | // 5 | // Created by Adeyinka Adediji on 11/09/2017. 6 | // Copyright © 2017 Adeyinka Adediji. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension String { 12 | func trim() -> String { 13 | return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 14 | } 15 | } 16 | 17 | extension UIColor { 18 | convenience init(_ hex: String) { 19 | var cString = hex.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).uppercased() 20 | 21 | if cString.hasPrefix("#") { 22 | cString = String(cString.suffix(from: cString.index(cString.startIndex, offsetBy: 1))) 23 | } 24 | 25 | var rgbValue: UInt32 = 0 26 | Scanner(string: cString).scanHexInt32(&rgbValue) 27 | 28 | let components = ( 29 | R: CGFloat((rgbValue & 0xFF0000) >> 16) / 255, 30 | G: CGFloat((rgbValue & 0x00FF00) >> 8) / 255, 31 | B: CGFloat(rgbValue & 0x0000FF) / 255 32 | ) 33 | self.init(red: components.R, green: components.G, blue: components.B, alpha: 1) 34 | } 35 | } 36 | 37 | class Utils { 38 | func delay(_ delay: Double, closure: @escaping () -> ()) { 39 | DispatchQueue.main.asyncAfter( 40 | deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), 41 | execute: closure 42 | ) 43 | } 44 | 45 | func loadImage(name: String) -> UIImage? { 46 | let podBundle = Bundle(for: Utils.self) 47 | if let url = podBundle.url(forResource: "ShelfView", withExtension: "bundle") { 48 | let bundle = Bundle(url: url) 49 | return UIImage(named: name, in: bundle, compatibleWith: nil) 50 | } 51 | return nil 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdscientist/ShelfView-iOS/80b85c0548fc50a3f1cd43534a973858fbe9df2f/ipad.png -------------------------------------------------------------------------------- /iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdscientist/ShelfView-iOS/80b85c0548fc50a3f1cd43534a973858fbe9df2f/iphone.png -------------------------------------------------------------------------------- /note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdscientist/ShelfView-iOS/80b85c0548fc50a3f1cd43534a973858fbe9df2f/note.png --------------------------------------------------------------------------------