├── .gitignore ├── .gitmodules ├── .swift-version ├── .travis.yml ├── Cartfile ├── Cartfile.resolved ├── Example └── VulcanSample │ ├── Podfile │ ├── Podfile.lock │ ├── VulcanSample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── VulcanSample.xcworkspace │ └── contents.xcworkspacedata │ ├── VulcanSample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift │ └── VulcanSampleTests │ ├── Info.plist │ └── VulcanSampleTests.swift ├── LICENSE ├── README.md ├── Sources ├── Vulcan │ ├── ImageCachable.swift │ ├── ImageComposable.swift │ ├── ImageDecorder.swift │ ├── ImageDownloader.swift │ ├── ImageResult.swift │ ├── Info.plist │ ├── UIImageView+Vulcan.swift │ ├── Vulcan.h │ └── Vulcan.swift └── WebP │ ├── UIImage+WebP.swift │ ├── WebPDecoder.h │ └── WebPDecoder.m ├── Tests └── VulcanTests │ ├── ImageDownloaderTests.swift │ ├── Info.plist │ └── VulcanTests.swift ├── Vulcan.podspec ├── Vulcan.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── Vulcan.xcscheme │ └── VulcanTests.xcscheme └── assets ├── demo_01.gif ├── demo_02.gif ├── sample_100.jpg └── sample_1024.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Carthage/Checkouts/SwiftWebP"] 2 | path = Carthage/Checkouts/SwiftWebP 3 | url = https://github.com/jinSasaki/SwiftWebP.git 4 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 3.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | script: 3 | - xcodebuild build-for-testing test-without-building -scheme VulcanTests -configuration Debug -sdk iphonesimulator -destination "name=iPhone 7" ENABLE_TESTABILITY=YES 4 | osx_image: xcode9 5 | xcode_project: Vulcan.xcodeproj 6 | xcode_scheme: VulcanTests 7 | xcode_sdk: iphonesimulator 8 | env: 9 | global: 10 | - FRAMEWORK_NAME=Vulcan 11 | before_install: 12 | - brew update 13 | - brew outdated carthage || brew upgrade carthage 14 | before_script: 15 | # bootstrap the dependencies for the project 16 | # you can remove if you don't have dependencies 17 | - carthage bootstrap 18 | before_deploy: 19 | - carthage build --no-skip-current 20 | - carthage archive $FRAMEWORK_NAME 21 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "jinSasaki/SwiftWebP" ~> 0.1.2 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "jinSasaki/SwiftWebP" "0.1.2" 2 | -------------------------------------------------------------------------------- /Example/VulcanSample/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '8.0' 3 | 4 | target 'VulcanSample' do 5 | use_frameworks! 6 | pod 'Vulcan', :path => './../../' 7 | 8 | target 'VulcanSampleTests' do 9 | inherit! :search_paths 10 | # Pods for testing 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /Example/VulcanSample/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - libwebp (0.5.1): 3 | - libwebp/core (= 0.5.1) 4 | - libwebp/dec (= 0.5.1) 5 | - libwebp/demux (= 0.5.1) 6 | - libwebp/dsp (= 0.5.1) 7 | - libwebp/enc (= 0.5.1) 8 | - libwebp/mux (= 0.5.1) 9 | - libwebp/utils (= 0.5.1) 10 | - libwebp/webp (= 0.5.1) 11 | - libwebp/core (0.5.1): 12 | - libwebp/webp 13 | - libwebp/dec (0.5.1): 14 | - libwebp/core 15 | - libwebp/demux (0.5.1): 16 | - libwebp/core 17 | - libwebp/dsp (0.5.1): 18 | - libwebp/core 19 | - libwebp/enc (0.5.1): 20 | - libwebp/core 21 | - libwebp/mux (0.5.1): 22 | - libwebp/core 23 | - libwebp/utils (0.5.1): 24 | - libwebp/core 25 | - libwebp/webp (0.5.1) 26 | - Vulcan (0.3.1): 27 | - Vulcan/WebP (= 0.3.1) 28 | - Vulcan/WebP (0.3.1): 29 | - libwebp 30 | 31 | DEPENDENCIES: 32 | - Vulcan (from `./../../`) 33 | 34 | EXTERNAL SOURCES: 35 | Vulcan: 36 | :path: "./../../" 37 | 38 | SPEC CHECKSUMS: 39 | libwebp: b9126f8982a95370cf4eaad9bb3fefd57f052c15 40 | Vulcan: 0a87b737409800640b81eb1e2a8337f668ffafd9 41 | 42 | PODFILE CHECKSUM: c323f0221e55238f40fe28e70dfb9e6098a35837 43 | 44 | COCOAPODS: 1.2.1 45 | -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2F87EFEFD0C4AEA62DEEAF93 /* Pods_VulcanSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C968BFD133D58AB6517D4F /* Pods_VulcanSample.framework */; }; 11 | 6DF14C68A88125091E5095CD /* Pods_VulcanSampleTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58FDEA4FBF78AEA3405734AD /* Pods_VulcanSampleTests.framework */; }; 12 | 938206841DB9128B0004D948 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 938206831DB9128B0004D948 /* AppDelegate.swift */; }; 13 | 938206861DB9128B0004D948 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 938206851DB9128B0004D948 /* ViewController.swift */; }; 14 | 938206891DB9128B0004D948 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 938206871DB9128B0004D948 /* Main.storyboard */; }; 15 | 9382068B1DB9128B0004D948 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9382068A1DB9128B0004D948 /* Assets.xcassets */; }; 16 | 9382068E1DB9128B0004D948 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9382068C1DB9128B0004D948 /* LaunchScreen.storyboard */; }; 17 | 938206991DB9128B0004D948 /* VulcanSampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 938206981DB9128B0004D948 /* VulcanSampleTests.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 938206951DB9128B0004D948 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 938206781DB9128B0004D948 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 9382067F1DB9128B0004D948; 26 | remoteInfo = VulcanSample; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 32C968BFD133D58AB6517D4F /* Pods_VulcanSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VulcanSample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 4757AAA526B18F55E9A63C6B /* Pods-VulcanSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VulcanSample.release.xcconfig"; path = "Pods/Target Support Files/Pods-VulcanSample/Pods-VulcanSample.release.xcconfig"; sourceTree = ""; }; 33 | 52E67D6403C71C95B83EEE19 /* Pods-VulcanSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VulcanSample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-VulcanSample/Pods-VulcanSample.debug.xcconfig"; sourceTree = ""; }; 34 | 58FDEA4FBF78AEA3405734AD /* Pods_VulcanSampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VulcanSampleTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 6468223F9327BAD8AFF8FE57 /* Pods-VulcanSampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VulcanSampleTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-VulcanSampleTests/Pods-VulcanSampleTests.release.xcconfig"; sourceTree = ""; }; 36 | 7D73FA2B21D4D765374EDFA1 /* Pods-VulcanSampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VulcanSampleTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-VulcanSampleTests/Pods-VulcanSampleTests.debug.xcconfig"; sourceTree = ""; }; 37 | 938206801DB9128B0004D948 /* VulcanSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VulcanSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 938206831DB9128B0004D948 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 39 | 938206851DB9128B0004D948 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 40 | 938206881DB9128B0004D948 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 41 | 9382068A1DB9128B0004D948 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 42 | 9382068D1DB9128B0004D948 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 43 | 9382068F1DB9128B0004D948 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 938206941DB9128B0004D948 /* VulcanSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VulcanSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 938206981DB9128B0004D948 /* VulcanSampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VulcanSampleTests.swift; sourceTree = ""; }; 46 | 9382069A1DB9128B0004D948 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 9382067D1DB9128B0004D948 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | 2F87EFEFD0C4AEA62DEEAF93 /* Pods_VulcanSample.framework in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | 938206911DB9128B0004D948 /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | 6DF14C68A88125091E5095CD /* Pods_VulcanSampleTests.framework in Frameworks */, 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | 22B8CF244E0CCBD06A3CCFF3 /* Pods */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 52E67D6403C71C95B83EEE19 /* Pods-VulcanSample.debug.xcconfig */, 73 | 4757AAA526B18F55E9A63C6B /* Pods-VulcanSample.release.xcconfig */, 74 | 7D73FA2B21D4D765374EDFA1 /* Pods-VulcanSampleTests.debug.xcconfig */, 75 | 6468223F9327BAD8AFF8FE57 /* Pods-VulcanSampleTests.release.xcconfig */, 76 | ); 77 | name = Pods; 78 | sourceTree = ""; 79 | }; 80 | 938206771DB9128B0004D948 = { 81 | isa = PBXGroup; 82 | children = ( 83 | 938206821DB9128B0004D948 /* VulcanSample */, 84 | 938206971DB9128B0004D948 /* VulcanSampleTests */, 85 | 938206811DB9128B0004D948 /* Products */, 86 | 22B8CF244E0CCBD06A3CCFF3 /* Pods */, 87 | 968395BBD6ADE2B8E26D52C9 /* Frameworks */, 88 | ); 89 | sourceTree = ""; 90 | }; 91 | 938206811DB9128B0004D948 /* Products */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 938206801DB9128B0004D948 /* VulcanSample.app */, 95 | 938206941DB9128B0004D948 /* VulcanSampleTests.xctest */, 96 | ); 97 | name = Products; 98 | sourceTree = ""; 99 | }; 100 | 938206821DB9128B0004D948 /* VulcanSample */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 938206831DB9128B0004D948 /* AppDelegate.swift */, 104 | 938206851DB9128B0004D948 /* ViewController.swift */, 105 | 938206871DB9128B0004D948 /* Main.storyboard */, 106 | 9382068A1DB9128B0004D948 /* Assets.xcassets */, 107 | 9382068C1DB9128B0004D948 /* LaunchScreen.storyboard */, 108 | 9382068F1DB9128B0004D948 /* Info.plist */, 109 | ); 110 | path = VulcanSample; 111 | sourceTree = ""; 112 | }; 113 | 938206971DB9128B0004D948 /* VulcanSampleTests */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 938206981DB9128B0004D948 /* VulcanSampleTests.swift */, 117 | 9382069A1DB9128B0004D948 /* Info.plist */, 118 | ); 119 | path = VulcanSampleTests; 120 | sourceTree = ""; 121 | }; 122 | 968395BBD6ADE2B8E26D52C9 /* Frameworks */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 32C968BFD133D58AB6517D4F /* Pods_VulcanSample.framework */, 126 | 58FDEA4FBF78AEA3405734AD /* Pods_VulcanSampleTests.framework */, 127 | ); 128 | name = Frameworks; 129 | sourceTree = ""; 130 | }; 131 | /* End PBXGroup section */ 132 | 133 | /* Begin PBXNativeTarget section */ 134 | 9382067F1DB9128B0004D948 /* VulcanSample */ = { 135 | isa = PBXNativeTarget; 136 | buildConfigurationList = 9382069D1DB9128B0004D948 /* Build configuration list for PBXNativeTarget "VulcanSample" */; 137 | buildPhases = ( 138 | D3169C72333DBE7F2FD347BC /* [CP] Check Pods Manifest.lock */, 139 | 9382067C1DB9128B0004D948 /* Sources */, 140 | 9382067D1DB9128B0004D948 /* Frameworks */, 141 | 9382067E1DB9128B0004D948 /* Resources */, 142 | E9076A1363E9BEFF745FF0B0 /* [CP] Embed Pods Frameworks */, 143 | 4684778010421A4561D34957 /* [CP] Copy Pods Resources */, 144 | ); 145 | buildRules = ( 146 | ); 147 | dependencies = ( 148 | ); 149 | name = VulcanSample; 150 | productName = VulcanSample; 151 | productReference = 938206801DB9128B0004D948 /* VulcanSample.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | 938206931DB9128B0004D948 /* VulcanSampleTests */ = { 155 | isa = PBXNativeTarget; 156 | buildConfigurationList = 938206A01DB9128B0004D948 /* Build configuration list for PBXNativeTarget "VulcanSampleTests" */; 157 | buildPhases = ( 158 | C1663C9EC473C2AE1B3B31A9 /* [CP] Check Pods Manifest.lock */, 159 | 938206901DB9128B0004D948 /* Sources */, 160 | 938206911DB9128B0004D948 /* Frameworks */, 161 | 938206921DB9128B0004D948 /* Resources */, 162 | 60DBE744F9FB64BF711FC610 /* [CP] Embed Pods Frameworks */, 163 | CFD1D7BF855DDF6BEA2848AE /* [CP] Copy Pods Resources */, 164 | ); 165 | buildRules = ( 166 | ); 167 | dependencies = ( 168 | 938206961DB9128B0004D948 /* PBXTargetDependency */, 169 | ); 170 | name = VulcanSampleTests; 171 | productName = VulcanSampleTests; 172 | productReference = 938206941DB9128B0004D948 /* VulcanSampleTests.xctest */; 173 | productType = "com.apple.product-type.bundle.unit-test"; 174 | }; 175 | /* End PBXNativeTarget section */ 176 | 177 | /* Begin PBXProject section */ 178 | 938206781DB9128B0004D948 /* Project object */ = { 179 | isa = PBXProject; 180 | attributes = { 181 | LastSwiftUpdateCheck = 0800; 182 | LastUpgradeCheck = 0800; 183 | ORGANIZATIONNAME = Sasakky; 184 | TargetAttributes = { 185 | 9382067F1DB9128B0004D948 = { 186 | CreatedOnToolsVersion = 8.0; 187 | ProvisioningStyle = Automatic; 188 | }; 189 | 938206931DB9128B0004D948 = { 190 | CreatedOnToolsVersion = 8.0; 191 | ProvisioningStyle = Automatic; 192 | TestTargetID = 9382067F1DB9128B0004D948; 193 | }; 194 | }; 195 | }; 196 | buildConfigurationList = 9382067B1DB9128B0004D948 /* Build configuration list for PBXProject "VulcanSample" */; 197 | compatibilityVersion = "Xcode 3.2"; 198 | developmentRegion = English; 199 | hasScannedForEncodings = 0; 200 | knownRegions = ( 201 | en, 202 | Base, 203 | ); 204 | mainGroup = 938206771DB9128B0004D948; 205 | productRefGroup = 938206811DB9128B0004D948 /* Products */; 206 | projectDirPath = ""; 207 | projectRoot = ""; 208 | targets = ( 209 | 9382067F1DB9128B0004D948 /* VulcanSample */, 210 | 938206931DB9128B0004D948 /* VulcanSampleTests */, 211 | ); 212 | }; 213 | /* End PBXProject section */ 214 | 215 | /* Begin PBXResourcesBuildPhase section */ 216 | 9382067E1DB9128B0004D948 /* Resources */ = { 217 | isa = PBXResourcesBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | 9382068E1DB9128B0004D948 /* LaunchScreen.storyboard in Resources */, 221 | 9382068B1DB9128B0004D948 /* Assets.xcassets in Resources */, 222 | 938206891DB9128B0004D948 /* Main.storyboard in Resources */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | 938206921DB9128B0004D948 /* Resources */ = { 227 | isa = PBXResourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXResourcesBuildPhase section */ 234 | 235 | /* Begin PBXShellScriptBuildPhase section */ 236 | 4684778010421A4561D34957 /* [CP] Copy Pods Resources */ = { 237 | isa = PBXShellScriptBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | ); 241 | inputPaths = ( 242 | ); 243 | name = "[CP] Copy Pods Resources"; 244 | outputPaths = ( 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | shellPath = /bin/sh; 248 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-VulcanSample/Pods-VulcanSample-resources.sh\"\n"; 249 | showEnvVarsInLog = 0; 250 | }; 251 | 60DBE744F9FB64BF711FC610 /* [CP] Embed Pods Frameworks */ = { 252 | isa = PBXShellScriptBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | ); 256 | inputPaths = ( 257 | ); 258 | name = "[CP] Embed Pods Frameworks"; 259 | outputPaths = ( 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | shellPath = /bin/sh; 263 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-VulcanSampleTests/Pods-VulcanSampleTests-frameworks.sh\"\n"; 264 | showEnvVarsInLog = 0; 265 | }; 266 | C1663C9EC473C2AE1B3B31A9 /* [CP] Check Pods Manifest.lock */ = { 267 | isa = PBXShellScriptBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | ); 271 | inputPaths = ( 272 | ); 273 | name = "[CP] Check Pods Manifest.lock"; 274 | outputPaths = ( 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | shellPath = /bin/sh; 278 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 279 | showEnvVarsInLog = 0; 280 | }; 281 | CFD1D7BF855DDF6BEA2848AE /* [CP] Copy Pods Resources */ = { 282 | isa = PBXShellScriptBuildPhase; 283 | buildActionMask = 2147483647; 284 | files = ( 285 | ); 286 | inputPaths = ( 287 | ); 288 | name = "[CP] Copy Pods Resources"; 289 | outputPaths = ( 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | shellPath = /bin/sh; 293 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-VulcanSampleTests/Pods-VulcanSampleTests-resources.sh\"\n"; 294 | showEnvVarsInLog = 0; 295 | }; 296 | D3169C72333DBE7F2FD347BC /* [CP] Check Pods Manifest.lock */ = { 297 | isa = PBXShellScriptBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | ); 301 | inputPaths = ( 302 | ); 303 | name = "[CP] Check Pods Manifest.lock"; 304 | outputPaths = ( 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | shellPath = /bin/sh; 308 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 309 | showEnvVarsInLog = 0; 310 | }; 311 | E9076A1363E9BEFF745FF0B0 /* [CP] Embed Pods Frameworks */ = { 312 | isa = PBXShellScriptBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | ); 316 | inputPaths = ( 317 | ); 318 | name = "[CP] Embed Pods Frameworks"; 319 | outputPaths = ( 320 | ); 321 | runOnlyForDeploymentPostprocessing = 0; 322 | shellPath = /bin/sh; 323 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-VulcanSample/Pods-VulcanSample-frameworks.sh\"\n"; 324 | showEnvVarsInLog = 0; 325 | }; 326 | /* End PBXShellScriptBuildPhase section */ 327 | 328 | /* Begin PBXSourcesBuildPhase section */ 329 | 9382067C1DB9128B0004D948 /* Sources */ = { 330 | isa = PBXSourcesBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | 938206861DB9128B0004D948 /* ViewController.swift in Sources */, 334 | 938206841DB9128B0004D948 /* AppDelegate.swift in Sources */, 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | }; 338 | 938206901DB9128B0004D948 /* Sources */ = { 339 | isa = PBXSourcesBuildPhase; 340 | buildActionMask = 2147483647; 341 | files = ( 342 | 938206991DB9128B0004D948 /* VulcanSampleTests.swift in Sources */, 343 | ); 344 | runOnlyForDeploymentPostprocessing = 0; 345 | }; 346 | /* End PBXSourcesBuildPhase section */ 347 | 348 | /* Begin PBXTargetDependency section */ 349 | 938206961DB9128B0004D948 /* PBXTargetDependency */ = { 350 | isa = PBXTargetDependency; 351 | target = 9382067F1DB9128B0004D948 /* VulcanSample */; 352 | targetProxy = 938206951DB9128B0004D948 /* PBXContainerItemProxy */; 353 | }; 354 | /* End PBXTargetDependency section */ 355 | 356 | /* Begin PBXVariantGroup section */ 357 | 938206871DB9128B0004D948 /* Main.storyboard */ = { 358 | isa = PBXVariantGroup; 359 | children = ( 360 | 938206881DB9128B0004D948 /* Base */, 361 | ); 362 | name = Main.storyboard; 363 | sourceTree = ""; 364 | }; 365 | 9382068C1DB9128B0004D948 /* LaunchScreen.storyboard */ = { 366 | isa = PBXVariantGroup; 367 | children = ( 368 | 9382068D1DB9128B0004D948 /* Base */, 369 | ); 370 | name = LaunchScreen.storyboard; 371 | sourceTree = ""; 372 | }; 373 | /* End PBXVariantGroup section */ 374 | 375 | /* Begin XCBuildConfiguration section */ 376 | 9382069B1DB9128B0004D948 /* Debug */ = { 377 | isa = XCBuildConfiguration; 378 | buildSettings = { 379 | ALWAYS_SEARCH_USER_PATHS = NO; 380 | CLANG_ANALYZER_NONNULL = YES; 381 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 382 | CLANG_CXX_LIBRARY = "libc++"; 383 | CLANG_ENABLE_MODULES = YES; 384 | CLANG_ENABLE_OBJC_ARC = YES; 385 | CLANG_WARN_BOOL_CONVERSION = YES; 386 | CLANG_WARN_CONSTANT_CONVERSION = YES; 387 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 388 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 389 | CLANG_WARN_EMPTY_BODY = YES; 390 | CLANG_WARN_ENUM_CONVERSION = YES; 391 | CLANG_WARN_INFINITE_RECURSION = YES; 392 | CLANG_WARN_INT_CONVERSION = YES; 393 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 394 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 395 | CLANG_WARN_UNREACHABLE_CODE = YES; 396 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 397 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 398 | COPY_PHASE_STRIP = NO; 399 | DEBUG_INFORMATION_FORMAT = dwarf; 400 | ENABLE_STRICT_OBJC_MSGSEND = YES; 401 | ENABLE_TESTABILITY = YES; 402 | GCC_C_LANGUAGE_STANDARD = gnu99; 403 | GCC_DYNAMIC_NO_PIC = NO; 404 | GCC_NO_COMMON_BLOCKS = YES; 405 | GCC_OPTIMIZATION_LEVEL = 0; 406 | GCC_PREPROCESSOR_DEFINITIONS = ( 407 | "DEBUG=1", 408 | "$(inherited)", 409 | ); 410 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 411 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 412 | GCC_WARN_UNDECLARED_SELECTOR = YES; 413 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 414 | GCC_WARN_UNUSED_FUNCTION = YES; 415 | GCC_WARN_UNUSED_VARIABLE = YES; 416 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 417 | MTL_ENABLE_DEBUG_INFO = YES; 418 | ONLY_ACTIVE_ARCH = YES; 419 | SDKROOT = iphoneos; 420 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 421 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 422 | }; 423 | name = Debug; 424 | }; 425 | 9382069C1DB9128B0004D948 /* Release */ = { 426 | isa = XCBuildConfiguration; 427 | buildSettings = { 428 | ALWAYS_SEARCH_USER_PATHS = NO; 429 | CLANG_ANALYZER_NONNULL = YES; 430 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 431 | CLANG_CXX_LIBRARY = "libc++"; 432 | CLANG_ENABLE_MODULES = YES; 433 | CLANG_ENABLE_OBJC_ARC = YES; 434 | CLANG_WARN_BOOL_CONVERSION = YES; 435 | CLANG_WARN_CONSTANT_CONVERSION = YES; 436 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 437 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 438 | CLANG_WARN_EMPTY_BODY = YES; 439 | CLANG_WARN_ENUM_CONVERSION = YES; 440 | CLANG_WARN_INFINITE_RECURSION = YES; 441 | CLANG_WARN_INT_CONVERSION = YES; 442 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 443 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 444 | CLANG_WARN_UNREACHABLE_CODE = YES; 445 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 446 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 447 | COPY_PHASE_STRIP = NO; 448 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 449 | ENABLE_NS_ASSERTIONS = NO; 450 | ENABLE_STRICT_OBJC_MSGSEND = YES; 451 | GCC_C_LANGUAGE_STANDARD = gnu99; 452 | GCC_NO_COMMON_BLOCKS = YES; 453 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 454 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 455 | GCC_WARN_UNDECLARED_SELECTOR = YES; 456 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 457 | GCC_WARN_UNUSED_FUNCTION = YES; 458 | GCC_WARN_UNUSED_VARIABLE = YES; 459 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 460 | MTL_ENABLE_DEBUG_INFO = NO; 461 | SDKROOT = iphoneos; 462 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 463 | VALIDATE_PRODUCT = YES; 464 | }; 465 | name = Release; 466 | }; 467 | 9382069E1DB9128B0004D948 /* Debug */ = { 468 | isa = XCBuildConfiguration; 469 | baseConfigurationReference = 52E67D6403C71C95B83EEE19 /* Pods-VulcanSample.debug.xcconfig */; 470 | buildSettings = { 471 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 472 | INFOPLIST_FILE = VulcanSample/Info.plist; 473 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 474 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 475 | PRODUCT_BUNDLE_IDENTIFIER = jp.sasakky.VulcanSample; 476 | PRODUCT_NAME = "$(TARGET_NAME)"; 477 | SWIFT_VERSION = 3.0; 478 | }; 479 | name = Debug; 480 | }; 481 | 9382069F1DB9128B0004D948 /* Release */ = { 482 | isa = XCBuildConfiguration; 483 | baseConfigurationReference = 4757AAA526B18F55E9A63C6B /* Pods-VulcanSample.release.xcconfig */; 484 | buildSettings = { 485 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 486 | INFOPLIST_FILE = VulcanSample/Info.plist; 487 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 488 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 489 | PRODUCT_BUNDLE_IDENTIFIER = jp.sasakky.VulcanSample; 490 | PRODUCT_NAME = "$(TARGET_NAME)"; 491 | SWIFT_VERSION = 3.0; 492 | }; 493 | name = Release; 494 | }; 495 | 938206A11DB9128B0004D948 /* Debug */ = { 496 | isa = XCBuildConfiguration; 497 | baseConfigurationReference = 7D73FA2B21D4D765374EDFA1 /* Pods-VulcanSampleTests.debug.xcconfig */; 498 | buildSettings = { 499 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 500 | BUNDLE_LOADER = "$(TEST_HOST)"; 501 | INFOPLIST_FILE = VulcanSampleTests/Info.plist; 502 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 503 | PRODUCT_BUNDLE_IDENTIFIER = jp.sasakky.VulcanSampleTests; 504 | PRODUCT_NAME = "$(TARGET_NAME)"; 505 | SWIFT_VERSION = 3.0; 506 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VulcanSample.app/VulcanSample"; 507 | }; 508 | name = Debug; 509 | }; 510 | 938206A21DB9128B0004D948 /* Release */ = { 511 | isa = XCBuildConfiguration; 512 | baseConfigurationReference = 6468223F9327BAD8AFF8FE57 /* Pods-VulcanSampleTests.release.xcconfig */; 513 | buildSettings = { 514 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 515 | BUNDLE_LOADER = "$(TEST_HOST)"; 516 | INFOPLIST_FILE = VulcanSampleTests/Info.plist; 517 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 518 | PRODUCT_BUNDLE_IDENTIFIER = jp.sasakky.VulcanSampleTests; 519 | PRODUCT_NAME = "$(TARGET_NAME)"; 520 | SWIFT_VERSION = 3.0; 521 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VulcanSample.app/VulcanSample"; 522 | }; 523 | name = Release; 524 | }; 525 | /* End XCBuildConfiguration section */ 526 | 527 | /* Begin XCConfigurationList section */ 528 | 9382067B1DB9128B0004D948 /* Build configuration list for PBXProject "VulcanSample" */ = { 529 | isa = XCConfigurationList; 530 | buildConfigurations = ( 531 | 9382069B1DB9128B0004D948 /* Debug */, 532 | 9382069C1DB9128B0004D948 /* Release */, 533 | ); 534 | defaultConfigurationIsVisible = 0; 535 | defaultConfigurationName = Release; 536 | }; 537 | 9382069D1DB9128B0004D948 /* Build configuration list for PBXNativeTarget "VulcanSample" */ = { 538 | isa = XCConfigurationList; 539 | buildConfigurations = ( 540 | 9382069E1DB9128B0004D948 /* Debug */, 541 | 9382069F1DB9128B0004D948 /* Release */, 542 | ); 543 | defaultConfigurationIsVisible = 0; 544 | defaultConfigurationName = Release; 545 | }; 546 | 938206A01DB9128B0004D948 /* Build configuration list for PBXNativeTarget "VulcanSampleTests" */ = { 547 | isa = XCConfigurationList; 548 | buildConfigurations = ( 549 | 938206A11DB9128B0004D948 /* Debug */, 550 | 938206A21DB9128B0004D948 /* Release */, 551 | ); 552 | defaultConfigurationIsVisible = 0; 553 | defaultConfigurationName = Release; 554 | }; 555 | /* End XCConfigurationList section */ 556 | }; 557 | rootObject = 938206781DB9128B0004D948 /* Project object */; 558 | } 559 | -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // VulcanSample 4 | // 5 | // Created by Jin Sasaki on 2016/10/20. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSample/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 | 37 | 46 | 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 | -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // VulcanSample 4 | // 5 | // Created by Jin Sasaki on 2016/10/20. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Vulcan 11 | 12 | class ViewController: UIViewController { 13 | 14 | @IBOutlet weak var imageView: UIImageView! 15 | 16 | @IBAction func didTapButton() { 17 | imageView.image = nil 18 | imageView.vl.setImage(url: URL(string: "https://github.com/jinSasaki/Vulcan/raw/master/assets/sample_1024.jpg")!) 19 | } 20 | 21 | @IBAction func didTapPriorityButton() { 22 | imageView.image = nil 23 | 24 | imageView.vl.setImage(urls: [ 25 | .url(URL(string: "https://github.com/jinSasaki/Vulcan/raw/master/assets/sample_100.jpg")!, priority: 100), 26 | .url(URL(string: "https://github.com/jinSasaki/Vulcan/raw/master/assets/sample_1024.jpg")!, priority: 1000) 27 | ]) 28 | } 29 | 30 | @IBAction func didTapCacheClearButton() { 31 | imageView.image = nil 32 | Vulcan.defaultImageDownloader.cache?.removeAll() 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSampleTests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/VulcanSample/VulcanSampleTests/VulcanSampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VulcanSampleTests.swift 3 | // VulcanSampleTests 4 | // 5 | // Created by Jin Sasaki on 2016/10/20. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import VulcanSample 11 | 12 | class VulcanSampleTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jin Sasaki 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Vulcan 2 | ===== 3 | [![Build Status](https://travis-ci.org/jinSasaki/Vulcan.svg?branch=master)](https://travis-ci.org/jinSasaki/Vulcan) 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 5 | [![Version](https://img.shields.io/cocoapods/v/Vulcan.svg?style=flat)](http://cocoadocs.org/docsets/Vulcan) 6 | [![Platform](https://img.shields.io/cocoapods/p/Vulcan.svg?style=flat)](http://cocoadocs.org/docsets/Vulcan) 7 | 8 | 9 | Multi image downloader with priority in Swift 10 | 11 | ## Features 12 | - Very light 13 | - Multi image download with priority 14 | - Caching images 15 | - Pure Swift 16 | - Composable image 17 | - Support WebP 18 | 19 | Single download | Multi download with priority 20 | --- | --- 21 | ![demo_01](https://github.com/jinSasaki/Vulcan/raw/master/assets/demo_01.gif) | ![demo_02](https://github.com/jinSasaki/Vulcan/raw/master/assets/demo_02.gif) 22 | 23 | ## Installation 24 | 25 | ### CocoaPods 26 | Setup CocoaPods: 27 | 28 | ``` 29 | $ gem install cocoapods 30 | ``` 31 | 32 | > CocoaPods 1.1.0+ is required to build Vulcan 33 | 34 | `Podfile` 35 | ``` 36 | platform :ios, '8.0' 37 | use_frameworks! 38 | 39 | target '' do 40 | pod 'Vulcan' 41 | end 42 | 43 | ``` 44 | 45 | Then, run the following command: 46 | 47 | ``` 48 | $ pod install 49 | ``` 50 | 51 | 52 | ### Carthage 53 | Setup carthage: 54 | 55 | ``` 56 | $ brew update 57 | $ brew install carthage 58 | ``` 59 | 60 | `Cartfile` 61 | ``` 62 | github "jinSasaki/Vulcan" 63 | ``` 64 | 65 | 66 | ## Usage 67 | 68 | ### Image downloading and show 69 | 70 | ```swift 71 | import Vulcan 72 | 73 | // Single downloading 74 | imageView.vl.setImage(url: URL(string: "/path/to/image")!) 75 | 76 | // Multi downloading 77 | // This image will be overridden by the image of higher priority URL. 78 | imageView.vl.setImage(urls: [ 79 | .url(URL(string: "/path/to/image")!, priority: 100), 80 | .url(URL(string: "/path/to/image")!, priority: 1000) 81 | ]) 82 | ``` 83 | 84 | ### WebP image 85 | If you installed via CocoaPods, add `pod 'Vulcan/WebP'`. 86 | If you installed via Carthage, add `SwiftWebP.framework` to project. 87 | 88 | ```swift 89 | import Vulcan 90 | import SwiftWebP // Only installed via Carthage 91 | 92 | extension WebPDecoder: ImageDecoder { 93 | public func decode(data: Data, response: HTTPURLResponse, options: ImageDecodeOptions?) throws -> Image { 94 | let contentTypes = response.allHeaderFields.filter({ ($0.key as? String ?? "").lowercased() == "content-type" }) 95 | guard 96 | let contentType = contentTypes.first, 97 | let value = contentType.value as? String, 98 | value == "image/webp", 99 | let image = WebPDecoder.decode(data) else { 100 | return try DefaultImageDecoder().decode(data: data, response: response, options: options) 101 | } 102 | return image 103 | } 104 | } 105 | 106 | // Set decoder to shared ImageDownloader 107 | Vulcan.defaultImageDownloader.decoder = WebPDecoder() 108 | 109 | // Request image with URL 110 | imageView.vl.setImage(url: URL(string: "/path/to/image")!) 111 | ``` 112 | 113 | ## Requirements 114 | - iOS 9.0+ 115 | - Xcode 8.1+ 116 | - Swift 3.0.1+ 117 | -------------------------------------------------------------------------------- /Sources/Vulcan/ImageCachable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageCachable.swift 3 | // Vulcan 4 | // 5 | // Created by Jin Sasaki on 2016/10/30. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ImageCachable { 12 | var memoryCapacity: Int { get set } 13 | var diskCapacity: Int { get set } 14 | var diskPath: String? { get set } 15 | 16 | func saveImage(image: Image, with id: String) 17 | func image(id: String) -> Image? 18 | func remove(id: String) 19 | func removeAll() 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Vulcan/ImageComposable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageComposable.swift 3 | // Vulcan 4 | // 5 | // Created by Jin Sasaki on 2016/10/30. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ImageComposable { 12 | func compose(image: Image) throws -> Image 13 | } 14 | -------------------------------------------------------------------------------- /Sources/Vulcan/ImageDecorder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageDecorder.swift 3 | // Vulcan 4 | // 5 | // Created by Jin Sasaki on 2016/10/30. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ImageDecoder { 12 | func decode(data: Data, response: HTTPURLResponse, options: ImageDecodeOptions?) throws -> Image 13 | } 14 | 15 | public struct ImageDecodeOptions { 16 | var outputSize: CGSize = CGSize.zero 17 | var opaque: Bool = false 18 | } 19 | 20 | public enum ImageDecodeError: Error { 21 | case failedCreateSource 22 | case failedCreateThumbnail 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Sources/Vulcan/ImageDownloader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageDownloader.swift 3 | // Vulcan 4 | // 5 | // Created by Jin Sasaki on 2016/08/26. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ImageIO 11 | 12 | public typealias Image = UIImage 13 | 14 | public enum ImageDownloadError: Error, CustomDebugStringConvertible { 15 | case downloading 16 | case cancelled 17 | case networkError(Error) 18 | case invalidResponse(URLResponse?) 19 | case failedDecode(Error) 20 | case failedCompose(Error) 21 | 22 | public var debugDescription: String { 23 | switch self { 24 | case .downloading: 25 | return "downloading" 26 | case .cancelled: 27 | return "cancelled" 28 | case .networkError(let error): 29 | return "Network error: \(error)" 30 | case .invalidResponse(let response): 31 | return "Invalid response: \(String(describing: response))" 32 | case .failedDecode(let error): 33 | return "Failed decode: \(error)" 34 | case .failedCompose(let error): 35 | return "Failed compose: \(error)" 36 | } 37 | } 38 | } 39 | 40 | public struct DefaultImageDecoder: ImageDecoder { 41 | public init() {} 42 | public func decode(data: Data, response: HTTPURLResponse, options: ImageDecodeOptions?) throws -> Image { 43 | guard let source = CGImageSourceCreateWithData(data as CFData, nil) else { 44 | throw ImageDecodeError.failedCreateSource 45 | } 46 | 47 | if let options = options , options.outputSize.width > 0 { 48 | // Resize to small image 49 | let maxPixcel = max(options.outputSize.width, options.outputSize.height) 50 | let options = [kCGImageSourceThumbnailMaxPixelSize as AnyHashable: maxPixcel, 51 | kCGImageSourceCreateThumbnailFromImageIfAbsent as AnyHashable: true] as [AnyHashable: Any] 52 | guard let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, options as CFDictionary?) else { 53 | throw ImageDecodeError.failedCreateThumbnail 54 | } 55 | return UIImage(cgImage: cgImage) 56 | } 57 | 58 | let options = [kCGImageSourceCreateThumbnailFromImageIfAbsent as AnyHashable: true] as [AnyHashable: Any] 59 | guard let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, options as CFDictionary?) else { 60 | throw ImageDecodeError.failedCreateThumbnail 61 | } 62 | return UIImage(cgImage: cgImage) 63 | } 64 | } 65 | 66 | open class ImageCache: ImageCachable { 67 | public var memoryCapacity: Int = 1 * 1024 * 1024 68 | public var diskCapacity: Int = 10 * 1024 * 1024 69 | public var diskPath: String? = nil 70 | 71 | let cache = NSCache() 72 | 73 | public init() {} 74 | 75 | open func saveImage(image: Image, with id: String) { 76 | cache.setObject(image, forKey: id as NSString) 77 | } 78 | open func image(id: String) -> Image? { 79 | return cache.object(forKey: id as NSString) 80 | } 81 | open func remove(id: String) { 82 | cache.removeObject(forKey: id as NSString) 83 | } 84 | open func removeAll() { 85 | cache.removeAllObjects() 86 | } 87 | } 88 | 89 | public typealias ImageDownloadHandler = (_ url: URL, _ result: ImageResult) -> Void 90 | 91 | public class ImageDownloader { 92 | public static let `default` = ImageDownloader() 93 | 94 | public var cache: ImageCachable? 95 | public var decoder: ImageDecoder 96 | 97 | fileprivate var downloadingTasks: [String: URLSessionDataTask] = [:] 98 | fileprivate var configuration: URLSessionConfiguration 99 | fileprivate var session: URLSession 100 | 101 | public init(cache: ImageCachable? = ImageCache(), configuration: URLSessionConfiguration = URLSessionConfiguration.default) { 102 | self.cache = cache 103 | self.configuration = configuration 104 | 105 | self.session = URLSession(configuration: configuration) 106 | let memoryCapacity: Int = cache?.memoryCapacity ?? 0 107 | let diskCapacity: Int = cache?.diskCapacity ?? 0 108 | let diskPath: String? = cache?.diskPath 109 | self.configuration.urlCache = URLCache(memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, diskPath: diskPath) 110 | 111 | self.decoder = DefaultImageDecoder() 112 | } 113 | 114 | public func set(cache: ImageCachable?) { 115 | self.cache?.removeAll() 116 | 117 | self.cache = cache 118 | let memoryCapacity: Int = cache?.memoryCapacity ?? 0 119 | let diskCapacity: Int = cache?.diskCapacity ?? 0 120 | let diskPath: String? = cache?.diskPath 121 | self.configuration.urlCache = URLCache(memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, diskPath: diskPath) 122 | } 123 | 124 | public func set(configuration: URLSessionConfiguration) { 125 | self.resetSession() 126 | 127 | self.configuration = configuration 128 | self.session = URLSession(configuration: configuration) 129 | } 130 | 131 | public func resetSession() { 132 | self.session.invalidateAndCancel() 133 | self.downloadingTasks.removeAll() 134 | } 135 | 136 | public func download(url: URL, composer: ImageComposable? = nil, options: ImageDecodeOptions? = nil, handler: ImageDownloadHandler?) -> String { 137 | let request = URLRequest(url: url) 138 | return download(request: request, composer: composer, options: options, handler: handler) 139 | } 140 | 141 | public func download(request: URLRequest, composer: ImageComposable? = nil, options: ImageDecodeOptions? = nil, handler: ImageDownloadHandler?) -> String { 142 | guard let url = request.url else { return "" } 143 | let id = url.absoluteString 144 | if let cachedImage = self.cache?.image(id: id) { 145 | guard let composer = composer else { 146 | handler?(url, .success(cachedImage)) 147 | return id 148 | } 149 | do { 150 | let composedImage = try composer.compose(image: cachedImage) 151 | handler?(url, .success(composedImage)) 152 | } catch let error { 153 | handler?(url, .failure(ImageDownloadError.failedDecode(error))) 154 | } 155 | return id 156 | } 157 | if let task = self.downloadingTasks[id] { 158 | switch task.state { 159 | case .running: 160 | // Suspend 161 | handler?(url, .failure(ImageDownloadError.downloading)) 162 | return id 163 | case .suspended: 164 | task.resume() 165 | return id 166 | default: 167 | // Do nothing 168 | break 169 | } 170 | } 171 | let task = self.session.dataTask(with: url, completionHandler: { [weak self] (data, response, error) in 172 | guard let weakSelf = self else { return } 173 | let task = weakSelf.downloadingTasks[id] 174 | weakSelf.downloadingTasks[id] = nil 175 | if task == nil || task?.state == .canceling { 176 | handler?(url, .failure(ImageDownloadError.cancelled)) 177 | return 178 | } 179 | 180 | if let error = error { 181 | handler?(url, .failure(ImageDownloadError.networkError(error))) 182 | return 183 | } 184 | 185 | guard let httpResponse = response as? HTTPURLResponse, let data = data else { 186 | handler?(url, .failure(ImageDownloadError.invalidResponse(response))) 187 | return 188 | } 189 | if httpResponse.statusCode != 200 { 190 | handler?(url, .failure(ImageDownloadError.invalidResponse(response))) 191 | return 192 | } 193 | let image: Image 194 | do { 195 | image = try weakSelf.decoder.decode(data: data, response: httpResponse, options: options) 196 | } catch let error { 197 | handler?(url, .failure(ImageDownloadError.failedDecode(error))) 198 | return 199 | } 200 | self?.cache?.saveImage(image: image, with: id) 201 | 202 | guard let composer = composer else { 203 | handler?(url, .success(image)) 204 | return 205 | } 206 | do { 207 | let composedImage = try composer.compose(image: image) 208 | handler?(url, .success(composedImage)) 209 | } catch let error { 210 | handler?(url, .failure(ImageDownloadError.failedCompose(error))) 211 | } 212 | }) 213 | self.downloadingTasks[id] = task 214 | task.resume() 215 | return id 216 | } 217 | 218 | public func cancel(id: String) { 219 | if let task = self.downloadingTasks[id] { 220 | task.cancel() 221 | self.downloadingTasks[id] = nil 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /Sources/Vulcan/ImageResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageResult.swift 3 | // Vulcan 4 | // 5 | // Created by Jin Sasaki on 2016/10/30. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ImageResult { 12 | case success(Image) 13 | case failure(Error) 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Vulcan/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.3.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Vulcan/UIImageView+Vulcan.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+Vulcan.swift 3 | // Vulcan 4 | // 5 | // Created by Jin Sasaki on 2017/04/23 6 | // Copyright © 2017年 Sasakky. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private var kVulcanKey: UInt = 0 12 | 13 | public extension UIImageView { 14 | 15 | public var vl: Vulcan { 16 | if let vulcan = objc_getAssociatedObject(self, &kVulcanKey) as? Vulcan { 17 | return vulcan 18 | } 19 | let vulcan = Vulcan() 20 | if vulcan.imageView == nil { 21 | vulcan.imageView = self 22 | } 23 | objc_setAssociatedObject(self, &kVulcanKey, vulcan, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 24 | return vulcan 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Vulcan/Vulcan.h: -------------------------------------------------------------------------------- 1 | // 2 | // Vulcan.h 3 | // Vulcan 4 | // 5 | // Created by Jin Sasaki on 2016/10/28. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Vulcan. 12 | FOUNDATION_EXPORT double VulcanVersionNumber; 13 | 14 | //! Project version string for Vulcan. 15 | FOUNDATION_EXPORT const unsigned char VulcanVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sources/Vulcan/Vulcan.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Vulcan.swift 3 | // Vulcan 4 | // 5 | // Created by Jin Sasaki on 2017/04/23. 6 | // Copyright © 2017年 Sasakky. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final public class Vulcan { 12 | public static var defaultImageDownloader: ImageDownloader = ImageDownloader.default 13 | public private(set) var priority: Int = Int.min 14 | public var imageDownloader: ImageDownloader { 15 | get { 16 | return _imageDownloader ?? Vulcan.defaultImageDownloader 17 | } 18 | set { 19 | _imageDownloader = newValue 20 | } 21 | } 22 | public static var cacheMemoryCapacity: Int? { 23 | get { 24 | return defaultImageDownloader.cache?.memoryCapacity 25 | } 26 | set { 27 | guard let newValue = newValue else { return } 28 | defaultImageDownloader.cache?.memoryCapacity = newValue 29 | } 30 | } 31 | public static var diskCapacity: Int? { 32 | get { 33 | return defaultImageDownloader.cache?.diskCapacity 34 | } 35 | set { 36 | guard let newValue = newValue else { return } 37 | defaultImageDownloader.cache?.diskCapacity = newValue 38 | } 39 | } 40 | public static var diskPath: String? { 41 | get { 42 | return defaultImageDownloader.cache?.diskPath 43 | } 44 | set { 45 | guard let newValue = newValue else { return } 46 | defaultImageDownloader.cache?.diskPath = newValue 47 | } 48 | } 49 | internal weak var imageView: UIImageView? 50 | private var _imageDownloader: ImageDownloader? 51 | private var downloadTaskId: String? 52 | private var dummyView: UIImageView? 53 | 54 | public static func setDefault(imageDownloader: ImageDownloader) { 55 | self.defaultImageDownloader = imageDownloader 56 | } 57 | 58 | public func setImage(url: URL, placeholderImage: UIImage? = nil, composer: ImageComposable? = nil, options: ImageDecodeOptions? = nil, completion: ImageDownloadHandler? = nil) { 59 | let downloader = imageDownloader 60 | cancelLoading() 61 | 62 | imageView?.image = placeholderImage 63 | let id = downloader.download(url: url, composer: composer, options: options) { (url, result) in 64 | switch result { 65 | case .success(let image): 66 | DispatchQueue.main.async { 67 | self.showImage(image: image) 68 | } 69 | default: 70 | break 71 | } 72 | completion?(url, result) 73 | } 74 | self.downloadTaskId = id 75 | } 76 | 77 | public enum PriorityURL { 78 | case url(URL, priority: Int) 79 | case request(URLRequest, priority: Int) 80 | 81 | public var priority: Int { 82 | switch self { 83 | case .url(_, let priority): 84 | return priority 85 | case .request(_, let priority): 86 | return priority 87 | } 88 | } 89 | 90 | public var url: URL { 91 | switch self { 92 | case .url(let url, _): 93 | return url 94 | case .request(let request, _): 95 | return request.url! 96 | } 97 | } 98 | } 99 | 100 | /// Download images with priority 101 | public func setImage(urls: [PriorityURL], placeholderImage: UIImage? = nil, composer: ImageComposable? = nil, options: ImageDecodeOptions? = nil) { 102 | let downloader = imageDownloader 103 | cancelLoading() 104 | 105 | imageView?.image = placeholderImage 106 | let ids = urls.sorted(by: { $0.priority < $1.priority }) 107 | .map({ (priorityURL) -> String in 108 | return downloader.download(url: priorityURL.url, composer: composer, options: options) { (url, result) in 109 | switch result { 110 | case .success(let image): 111 | DispatchQueue.main.async { 112 | if self.priority <= priorityURL.priority { 113 | self.priority = priorityURL.priority 114 | self.showImage(image: image) 115 | } 116 | } 117 | default: 118 | break 119 | } 120 | } 121 | }) 122 | self.downloadTaskId = ids.joined(separator: ",") 123 | } 124 | 125 | public func cancelLoading() { 126 | let downloader = imageDownloader 127 | guard let splited = downloadTaskId?.components(separatedBy: ",") else { 128 | return 129 | } 130 | splited.forEach({ downloader.cancel(id: $0) }) 131 | self.priority = Int.min 132 | } 133 | 134 | private func showImage(image newImage: UIImage) { 135 | guard let baseImageView = self.imageView else { return } 136 | let imageView = dummyView ?? UIImageView() 137 | imageView.frame = baseImageView.bounds 138 | imageView.alpha = 1 139 | imageView.image = baseImageView.image 140 | imageView.contentMode = baseImageView.contentMode 141 | baseImageView.addSubview(imageView) 142 | baseImageView.image = newImage 143 | dummyView = imageView 144 | 145 | UIView.animate(withDuration: 0.3, animations: { 146 | self.dummyView?.alpha = 0 147 | }) { (finished) in 148 | self.dummyView?.removeFromSuperview() 149 | self.dummyView = nil 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Sources/WebP/UIImage+WebP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+WebP.swift 3 | // SwiftWebP 4 | // 5 | // Created by Jin Sasaki on 2016/11/04. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UIImage { 12 | public class func image(fromWebPData webpData: Data) -> UIImage? { 13 | guard let image = WebPDecoder.decode(webpData) else { 14 | return nil 15 | } 16 | return image 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/WebP/WebPDecoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // WebPDecoder.h 3 | // 4 | // Created by Jin Sasaki on 2016/11/04. 5 | // Copyright © 2016年 Sasakky. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface WebPDecoder : NSObject 11 | + (nullable UIImage *)decode:(nullable NSData *)data; 12 | @end 13 | -------------------------------------------------------------------------------- /Sources/WebP/WebPDecoder.m: -------------------------------------------------------------------------------- 1 | // 2 | // WebPDecoder.m 3 | // 4 | // Created by Jin Sasaki on 2016/11/04. 5 | // Copyright © 2016年 Sasakky. All rights reserved. 6 | // 7 | 8 | #import "WebPDecoder.h" 9 | 10 | #import "webp/decode.h" 11 | #import "webp/mux_types.h" 12 | #import "webp/demux.h" 13 | 14 | static void FreeImageData(void *info, const void *data, size_t size) { 15 | free((void *)data); 16 | } 17 | 18 | @implementation WebPDecoder 19 | + (nullable UIImage *)decode:(nullable NSData *)data 20 | { 21 | if (!data) { 22 | return nil; 23 | } 24 | 25 | WebPData webpData; 26 | WebPDataInit(&webpData); 27 | webpData.bytes = data.bytes; 28 | webpData.size = data.length; 29 | WebPDemuxer *demuxer = WebPDemux(&webpData); 30 | if (!demuxer) { 31 | return nil; 32 | } 33 | 34 | uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS); 35 | if (!(flags & ANIMATION_FLAG)) { 36 | // for static single webp image 37 | UIImage *staticImage = [self rawWepImageWithData:webpData]; 38 | WebPDemuxDelete(demuxer); 39 | return staticImage; 40 | } 41 | 42 | WebPIterator iter; 43 | if (!WebPDemuxGetFrame(demuxer, 1, &iter)) { 44 | WebPDemuxReleaseIterator(&iter); 45 | WebPDemuxDelete(demuxer); 46 | return nil; 47 | } 48 | 49 | NSMutableArray *images = [NSMutableArray array]; 50 | NSTimeInterval duration = 0; 51 | 52 | do { 53 | UIImage *image; 54 | if (iter.blend_method == WEBP_MUX_BLEND) { 55 | image = [self blendWebpImageWithOriginImage:[images lastObject] iterator:iter]; 56 | } else { 57 | image = [self rawWepImageWithData:iter.fragment]; 58 | } 59 | 60 | if (!image) { 61 | continue; 62 | } 63 | 64 | [images addObject:image]; 65 | duration += iter.duration / 1000.0f; 66 | 67 | } while (WebPDemuxNextFrame(&iter)); 68 | 69 | WebPDemuxReleaseIterator(&iter); 70 | WebPDemuxDelete(demuxer); 71 | 72 | UIImage *finalImage = nil; 73 | finalImage = [UIImage animatedImageWithImages:images duration:duration]; 74 | return finalImage; 75 | } 76 | 77 | 78 | + (nullable UIImage *)blendWebpImageWithOriginImage:(nullable UIImage *)originImage iterator:(WebPIterator)iter { 79 | if (!originImage) { 80 | return nil; 81 | } 82 | 83 | CGSize size = originImage.size; 84 | CGFloat tmpX = iter.x_offset; 85 | CGFloat tmpY = size.height - iter.height - iter.y_offset; 86 | CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height); 87 | 88 | UIImage *image = [self rawWepImageWithData:iter.fragment]; 89 | if (!image) { 90 | return nil; 91 | } 92 | 93 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); 94 | uint32_t bitmapInfo = iter.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0; 95 | CGContextRef blendCanvas = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, colorSpaceRef, bitmapInfo); 96 | CGContextDrawImage(blendCanvas, CGRectMake(0, 0, size.width, size.height), originImage.CGImage); 97 | CGContextDrawImage(blendCanvas, imageRect, image.CGImage); 98 | CGImageRef newImageRef = CGBitmapContextCreateImage(blendCanvas); 99 | 100 | image = [UIImage imageWithCGImage:newImageRef]; 101 | 102 | CGImageRelease(newImageRef); 103 | CGContextRelease(blendCanvas); 104 | CGColorSpaceRelease(colorSpaceRef); 105 | 106 | return image; 107 | } 108 | 109 | + (nullable UIImage *)rawWepImageWithData:(WebPData)webpData { 110 | WebPDecoderConfig config; 111 | if (!WebPInitDecoderConfig(&config)) { 112 | return nil; 113 | } 114 | 115 | if (WebPGetFeatures(webpData.bytes, webpData.size, &config.input) != VP8_STATUS_OK) { 116 | return nil; 117 | } 118 | 119 | config.output.colorspace = config.input.has_alpha ? MODE_rgbA : MODE_RGB; 120 | config.options.use_threads = 1; 121 | 122 | // Decode the WebP image data into a RGBA value array. 123 | if (WebPDecode(webpData.bytes, webpData.size, &config) != VP8_STATUS_OK) { 124 | return nil; 125 | } 126 | 127 | int width = config.input.width; 128 | int height = config.input.height; 129 | if (config.options.use_scaling) { 130 | width = config.options.scaled_width; 131 | height = config.options.scaled_height; 132 | } 133 | 134 | // Construct a UIImage from the decoded RGBA value array. 135 | CGDataProviderRef provider = 136 | CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData); 137 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); 138 | CGBitmapInfo bitmapInfo = config.input.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0; 139 | size_t components = config.input.has_alpha ? 4 : 3; 140 | CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; 141 | CGImageRef imageRef = CGImageCreate(width, height, 8, components * 8, components * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); 142 | 143 | CGColorSpaceRelease(colorSpaceRef); 144 | CGDataProviderRelease(provider); 145 | 146 | UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; 147 | CGImageRelease(imageRef); 148 | 149 | return image; 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /Tests/VulcanTests/ImageDownloaderTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageDownloaderTests.swift 3 | // Vulcan 4 | // 5 | // Created by Jin Sasaki on 2016/10/30. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Vulcan 11 | 12 | class ImageDownloaderTests: XCTestCase { 13 | 14 | static var downloader: ImageDownloader! 15 | 16 | override class func setUp() { 17 | super.setUp() 18 | 19 | let configuration = URLSessionConfiguration.ephemeral 20 | self.downloader = ImageDownloader(cache: nil, configuration: configuration) 21 | } 22 | 23 | 24 | override func setUp() { 25 | super.setUp() 26 | // Put setup code here. This method is called before the invocation of each test method in the class. 27 | } 28 | 29 | override func tearDown() { 30 | // Put teardown code here. This method is called after the invocation of each test method in the class. 31 | super.tearDown() 32 | } 33 | 34 | 35 | func testSetCache() { 36 | struct Cache: ImageCachable { 37 | var memoryCapacity:Int = 100 38 | var diskCapacity: Int = 100 39 | var diskPath: String? = "path" 40 | func saveImage(image: Image, with id: String) {} 41 | func image(id: String) -> Image? { return nil } 42 | func remove(id: String) {} 43 | func removeAll() {} 44 | } 45 | XCTAssertNil(ImageDownloaderTests.downloader.cache) 46 | 47 | ImageDownloaderTests.downloader.set(cache: Cache()) 48 | XCTAssertNotNil(ImageDownloaderTests.downloader.cache) 49 | let _cache = ImageDownloaderTests.downloader.cache! 50 | XCTAssertEqual(_cache.memoryCapacity, 100) 51 | XCTAssertEqual(_cache.diskCapacity, 100) 52 | XCTAssertEqual(_cache.diskPath, "path") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/VulcanTests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/VulcanTests/VulcanTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VulcanTests.swift 3 | // VulcanTests 4 | // 5 | // Created by Jin Sasaki on 2016/10/28. 6 | // Copyright © 2016年 Sasakky. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Vulcan 11 | 12 | class VulcanTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Vulcan.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Vulcan' 3 | s.version = '0.3.3' 4 | s.license = 'MIT' 5 | s.summary = 'Multi image downloader with priority in Swift' 6 | s.homepage = 'https://github.com/jinSasaki/Vulcan' 7 | s.social_media_url = 'http://twitter.com/sasakky_j' 8 | s.author = { "Jin Sasaki" => "sasakky.j@gmail.com" } 9 | s.source = { :git => 'https://github.com/jinSasaki/Vulcan.git', :tag => s.version } 10 | 11 | s.ios.deployment_target = '8.0' 12 | s.tvos.deployment_target = '9.0' 13 | 14 | s.source_files = 'Sources/**/*.swift' 15 | 16 | s.subspec 'WebP' do |webp| 17 | webp.source_files = 'Sources/WebP/*.{h,m,swift}' 18 | webp.xcconfig = { 19 | 'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/libwebp/src' 20 | } 21 | webp.dependency 'libwebp' 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /Vulcan.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4925B9471DC6044800D9408A /* ImageCachable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4925B93F1DC6044800D9408A /* ImageCachable.swift */; }; 11 | 4925B9481DC6044800D9408A /* ImageComposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4925B9401DC6044800D9408A /* ImageComposable.swift */; }; 12 | 4925B9491DC6044800D9408A /* ImageDecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4925B9411DC6044800D9408A /* ImageDecorder.swift */; }; 13 | 4925B94A1DC6044800D9408A /* ImageDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4925B9421DC6044800D9408A /* ImageDownloader.swift */; }; 14 | 4925B94B1DC6044800D9408A /* ImageResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4925B9431DC6044800D9408A /* ImageResult.swift */; }; 15 | 4925B94D1DC6044800D9408A /* UIImageView+Vulcan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4925B9451DC6044800D9408A /* UIImageView+Vulcan.swift */; }; 16 | 4925B97C1DC6066000D9408A /* Vulcan.h in Headers */ = {isa = PBXBuildFile; fileRef = 4925B9561DC6060B00D9408A /* Vulcan.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 4925B9841DC6071600D9408A /* VulcanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4925B9811DC6071600D9408A /* VulcanTests.swift */; }; 18 | 4925B9861DC609A900D9408A /* ImageDownloaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4925B9851DC609A900D9408A /* ImageDownloaderTests.swift */; }; 19 | 933865C71DC33F5800B7D6F3 /* Vulcan.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 933865BD1DC33F5800B7D6F3 /* Vulcan.framework */; }; 20 | 939CC0A01EACC28900FA795E /* Vulcan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939CC09F1EACC28900FA795E /* Vulcan.swift */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 933865C81DC33F5800B7D6F3 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 933865B41DC33F5800B7D6F3 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 933865BC1DC33F5800B7D6F3; 29 | remoteInfo = Vulcan; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 4925B93F1DC6044800D9408A /* ImageCachable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCachable.swift; sourceTree = ""; }; 35 | 4925B9401DC6044800D9408A /* ImageComposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageComposable.swift; sourceTree = ""; }; 36 | 4925B9411DC6044800D9408A /* ImageDecorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageDecorder.swift; sourceTree = ""; }; 37 | 4925B9421DC6044800D9408A /* ImageDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageDownloader.swift; sourceTree = ""; }; 38 | 4925B9431DC6044800D9408A /* ImageResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageResult.swift; sourceTree = ""; }; 39 | 4925B9441DC6044800D9408A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 4925B9451DC6044800D9408A /* UIImageView+Vulcan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Vulcan.swift"; sourceTree = ""; }; 41 | 4925B9561DC6060B00D9408A /* Vulcan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Vulcan.h; sourceTree = ""; }; 42 | 4925B9801DC6071600D9408A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 4925B9811DC6071600D9408A /* VulcanTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VulcanTests.swift; sourceTree = ""; }; 44 | 4925B9851DC609A900D9408A /* ImageDownloaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageDownloaderTests.swift; sourceTree = ""; }; 45 | 933865BD1DC33F5800B7D6F3 /* Vulcan.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Vulcan.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 933865C61DC33F5800B7D6F3 /* VulcanTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VulcanTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 939CC09F1EACC28900FA795E /* Vulcan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vulcan.swift; sourceTree = ""; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | 933865B91DC33F5800B7D6F3 /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | 933865C31DC33F5800B7D6F3 /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | 933865C71DC33F5800B7D6F3 /* Vulcan.framework in Frameworks */, 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | 4925B93E1DC6044800D9408A /* Vulcan */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 4925B9561DC6060B00D9408A /* Vulcan.h */, 73 | 4925B93F1DC6044800D9408A /* ImageCachable.swift */, 74 | 939CC09F1EACC28900FA795E /* Vulcan.swift */, 75 | 4925B9401DC6044800D9408A /* ImageComposable.swift */, 76 | 4925B9411DC6044800D9408A /* ImageDecorder.swift */, 77 | 4925B9421DC6044800D9408A /* ImageDownloader.swift */, 78 | 4925B9431DC6044800D9408A /* ImageResult.swift */, 79 | 4925B9441DC6044800D9408A /* Info.plist */, 80 | 4925B9451DC6044800D9408A /* UIImageView+Vulcan.swift */, 81 | ); 82 | name = Vulcan; 83 | path = Sources/Vulcan; 84 | sourceTree = ""; 85 | }; 86 | 4925B97E1DC6071600D9408A /* VulcanTests */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 4925B9801DC6071600D9408A /* Info.plist */, 90 | 4925B9811DC6071600D9408A /* VulcanTests.swift */, 91 | 4925B9851DC609A900D9408A /* ImageDownloaderTests.swift */, 92 | ); 93 | name = VulcanTests; 94 | path = Tests/VulcanTests; 95 | sourceTree = SOURCE_ROOT; 96 | }; 97 | 933865B31DC33F5800B7D6F3 = { 98 | isa = PBXGroup; 99 | children = ( 100 | 4925B93E1DC6044800D9408A /* Vulcan */, 101 | 4925B97E1DC6071600D9408A /* VulcanTests */, 102 | 933865BE1DC33F5800B7D6F3 /* Products */, 103 | ); 104 | sourceTree = ""; 105 | }; 106 | 933865BE1DC33F5800B7D6F3 /* Products */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 933865BD1DC33F5800B7D6F3 /* Vulcan.framework */, 110 | 933865C61DC33F5800B7D6F3 /* VulcanTests.xctest */, 111 | ); 112 | name = Products; 113 | sourceTree = ""; 114 | }; 115 | /* End PBXGroup section */ 116 | 117 | /* Begin PBXHeadersBuildPhase section */ 118 | 933865BA1DC33F5800B7D6F3 /* Headers */ = { 119 | isa = PBXHeadersBuildPhase; 120 | buildActionMask = 2147483647; 121 | files = ( 122 | 4925B97C1DC6066000D9408A /* Vulcan.h in Headers */, 123 | ); 124 | runOnlyForDeploymentPostprocessing = 0; 125 | }; 126 | /* End PBXHeadersBuildPhase section */ 127 | 128 | /* Begin PBXNativeTarget section */ 129 | 933865BC1DC33F5800B7D6F3 /* Vulcan */ = { 130 | isa = PBXNativeTarget; 131 | buildConfigurationList = 933865D11DC33F5800B7D6F3 /* Build configuration list for PBXNativeTarget "Vulcan" */; 132 | buildPhases = ( 133 | 933865B81DC33F5800B7D6F3 /* Sources */, 134 | 933865B91DC33F5800B7D6F3 /* Frameworks */, 135 | 933865BA1DC33F5800B7D6F3 /* Headers */, 136 | 933865BB1DC33F5800B7D6F3 /* Resources */, 137 | ); 138 | buildRules = ( 139 | ); 140 | dependencies = ( 141 | ); 142 | name = Vulcan; 143 | productName = Vulcan; 144 | productReference = 933865BD1DC33F5800B7D6F3 /* Vulcan.framework */; 145 | productType = "com.apple.product-type.framework"; 146 | }; 147 | 933865C51DC33F5800B7D6F3 /* VulcanTests */ = { 148 | isa = PBXNativeTarget; 149 | buildConfigurationList = 933865D41DC33F5800B7D6F3 /* Build configuration list for PBXNativeTarget "VulcanTests" */; 150 | buildPhases = ( 151 | 933865C21DC33F5800B7D6F3 /* Sources */, 152 | 933865C31DC33F5800B7D6F3 /* Frameworks */, 153 | 933865C41DC33F5800B7D6F3 /* Resources */, 154 | ); 155 | buildRules = ( 156 | ); 157 | dependencies = ( 158 | 933865C91DC33F5800B7D6F3 /* PBXTargetDependency */, 159 | ); 160 | name = VulcanTests; 161 | productName = VulcanTests; 162 | productReference = 933865C61DC33F5800B7D6F3 /* VulcanTests.xctest */; 163 | productType = "com.apple.product-type.bundle.unit-test"; 164 | }; 165 | /* End PBXNativeTarget section */ 166 | 167 | /* Begin PBXProject section */ 168 | 933865B41DC33F5800B7D6F3 /* Project object */ = { 169 | isa = PBXProject; 170 | attributes = { 171 | LastSwiftUpdateCheck = 0800; 172 | LastUpgradeCheck = 0830; 173 | ORGANIZATIONNAME = Sasakky; 174 | TargetAttributes = { 175 | 933865BC1DC33F5800B7D6F3 = { 176 | CreatedOnToolsVersion = 8.0; 177 | ProvisioningStyle = Automatic; 178 | }; 179 | 933865C51DC33F5800B7D6F3 = { 180 | CreatedOnToolsVersion = 8.0; 181 | ProvisioningStyle = Automatic; 182 | }; 183 | }; 184 | }; 185 | buildConfigurationList = 933865B71DC33F5800B7D6F3 /* Build configuration list for PBXProject "Vulcan" */; 186 | compatibilityVersion = "Xcode 3.2"; 187 | developmentRegion = English; 188 | hasScannedForEncodings = 0; 189 | knownRegions = ( 190 | en, 191 | ); 192 | mainGroup = 933865B31DC33F5800B7D6F3; 193 | productRefGroup = 933865BE1DC33F5800B7D6F3 /* Products */; 194 | projectDirPath = ""; 195 | projectRoot = ""; 196 | targets = ( 197 | 933865BC1DC33F5800B7D6F3 /* Vulcan */, 198 | 933865C51DC33F5800B7D6F3 /* VulcanTests */, 199 | ); 200 | }; 201 | /* End PBXProject section */ 202 | 203 | /* Begin PBXResourcesBuildPhase section */ 204 | 933865BB1DC33F5800B7D6F3 /* Resources */ = { 205 | isa = PBXResourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | 933865C41DC33F5800B7D6F3 /* Resources */ = { 212 | isa = PBXResourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | /* End PBXResourcesBuildPhase section */ 219 | 220 | /* Begin PBXSourcesBuildPhase section */ 221 | 933865B81DC33F5800B7D6F3 /* Sources */ = { 222 | isa = PBXSourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | 939CC0A01EACC28900FA795E /* Vulcan.swift in Sources */, 226 | 4925B94B1DC6044800D9408A /* ImageResult.swift in Sources */, 227 | 4925B9491DC6044800D9408A /* ImageDecorder.swift in Sources */, 228 | 4925B9481DC6044800D9408A /* ImageComposable.swift in Sources */, 229 | 4925B94A1DC6044800D9408A /* ImageDownloader.swift in Sources */, 230 | 4925B9471DC6044800D9408A /* ImageCachable.swift in Sources */, 231 | 4925B94D1DC6044800D9408A /* UIImageView+Vulcan.swift in Sources */, 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | 933865C21DC33F5800B7D6F3 /* Sources */ = { 236 | isa = PBXSourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | 4925B9861DC609A900D9408A /* ImageDownloaderTests.swift in Sources */, 240 | 4925B9841DC6071600D9408A /* VulcanTests.swift in Sources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXSourcesBuildPhase section */ 245 | 246 | /* Begin PBXTargetDependency section */ 247 | 933865C91DC33F5800B7D6F3 /* PBXTargetDependency */ = { 248 | isa = PBXTargetDependency; 249 | target = 933865BC1DC33F5800B7D6F3 /* Vulcan */; 250 | targetProxy = 933865C81DC33F5800B7D6F3 /* PBXContainerItemProxy */; 251 | }; 252 | /* End PBXTargetDependency section */ 253 | 254 | /* Begin XCBuildConfiguration section */ 255 | 933865CF1DC33F5800B7D6F3 /* Debug */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_ANALYZER_NONNULL = YES; 260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 261 | CLANG_CXX_LIBRARY = "libc++"; 262 | CLANG_ENABLE_MODULES = YES; 263 | CLANG_ENABLE_OBJC_ARC = YES; 264 | CLANG_WARN_BOOL_CONVERSION = YES; 265 | CLANG_WARN_CONSTANT_CONVERSION = YES; 266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 267 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 268 | CLANG_WARN_EMPTY_BODY = YES; 269 | CLANG_WARN_ENUM_CONVERSION = YES; 270 | CLANG_WARN_INFINITE_RECURSION = YES; 271 | CLANG_WARN_INT_CONVERSION = YES; 272 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 273 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 274 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 275 | CLANG_WARN_UNREACHABLE_CODE = YES; 276 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 277 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 278 | COPY_PHASE_STRIP = NO; 279 | CURRENT_PROJECT_VERSION = 1; 280 | DEBUG_INFORMATION_FORMAT = dwarf; 281 | ENABLE_STRICT_OBJC_MSGSEND = YES; 282 | ENABLE_TESTABILITY = YES; 283 | GCC_C_LANGUAGE_STANDARD = gnu99; 284 | GCC_DYNAMIC_NO_PIC = NO; 285 | GCC_NO_COMMON_BLOCKS = YES; 286 | GCC_OPTIMIZATION_LEVEL = 0; 287 | GCC_PREPROCESSOR_DEFINITIONS = ( 288 | "DEBUG=1", 289 | "$(inherited)", 290 | ); 291 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 292 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 293 | GCC_WARN_UNDECLARED_SELECTOR = YES; 294 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 295 | GCC_WARN_UNUSED_FUNCTION = YES; 296 | GCC_WARN_UNUSED_VARIABLE = YES; 297 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 298 | MTL_ENABLE_DEBUG_INFO = YES; 299 | ONLY_ACTIVE_ARCH = YES; 300 | SDKROOT = iphoneos; 301 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 302 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 303 | TARGETED_DEVICE_FAMILY = "1,2"; 304 | VERSIONING_SYSTEM = "apple-generic"; 305 | VERSION_INFO_PREFIX = ""; 306 | }; 307 | name = Debug; 308 | }; 309 | 933865D01DC33F5800B7D6F3 /* Release */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_ANALYZER_NONNULL = YES; 314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 315 | CLANG_CXX_LIBRARY = "libc++"; 316 | CLANG_ENABLE_MODULES = YES; 317 | CLANG_ENABLE_OBJC_ARC = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_CONSTANT_CONVERSION = YES; 320 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 321 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 322 | CLANG_WARN_EMPTY_BODY = YES; 323 | CLANG_WARN_ENUM_CONVERSION = YES; 324 | CLANG_WARN_INFINITE_RECURSION = YES; 325 | CLANG_WARN_INT_CONVERSION = YES; 326 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 329 | CLANG_WARN_UNREACHABLE_CODE = YES; 330 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 331 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 332 | COPY_PHASE_STRIP = NO; 333 | CURRENT_PROJECT_VERSION = 1; 334 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 335 | ENABLE_NS_ASSERTIONS = NO; 336 | ENABLE_STRICT_OBJC_MSGSEND = YES; 337 | GCC_C_LANGUAGE_STANDARD = gnu99; 338 | GCC_NO_COMMON_BLOCKS = YES; 339 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 340 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 341 | GCC_WARN_UNDECLARED_SELECTOR = YES; 342 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 343 | GCC_WARN_UNUSED_FUNCTION = YES; 344 | GCC_WARN_UNUSED_VARIABLE = YES; 345 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 346 | MTL_ENABLE_DEBUG_INFO = NO; 347 | SDKROOT = iphoneos; 348 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 349 | TARGETED_DEVICE_FAMILY = "1,2"; 350 | VALIDATE_PRODUCT = YES; 351 | VERSIONING_SYSTEM = "apple-generic"; 352 | VERSION_INFO_PREFIX = ""; 353 | }; 354 | name = Release; 355 | }; 356 | 933865D21DC33F5800B7D6F3 /* Debug */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | CODE_SIGN_IDENTITY = ""; 360 | DEFINES_MODULE = YES; 361 | DYLIB_COMPATIBILITY_VERSION = 1; 362 | DYLIB_CURRENT_VERSION = 1; 363 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 364 | INFOPLIST_FILE = Sources/Vulcan/Info.plist; 365 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 366 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 367 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 368 | PRODUCT_BUNDLE_IDENTIFIER = jp.sasakky.Vulcan; 369 | PRODUCT_NAME = "$(TARGET_NAME)"; 370 | SKIP_INSTALL = YES; 371 | SWIFT_VERSION = 4.0; 372 | }; 373 | name = Debug; 374 | }; 375 | 933865D31DC33F5800B7D6F3 /* Release */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | CODE_SIGN_IDENTITY = ""; 379 | DEFINES_MODULE = YES; 380 | DYLIB_COMPATIBILITY_VERSION = 1; 381 | DYLIB_CURRENT_VERSION = 1; 382 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 383 | INFOPLIST_FILE = Sources/Vulcan/Info.plist; 384 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 385 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 386 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 387 | PRODUCT_BUNDLE_IDENTIFIER = jp.sasakky.Vulcan; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | SKIP_INSTALL = YES; 390 | SWIFT_VERSION = 4.0; 391 | }; 392 | name = Release; 393 | }; 394 | 933865D51DC33F5800B7D6F3 /* Debug */ = { 395 | isa = XCBuildConfiguration; 396 | buildSettings = { 397 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 398 | INFOPLIST_FILE = Tests/VulcanTests/Info.plist; 399 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 400 | PRODUCT_BUNDLE_IDENTIFIER = jp.sasakky.VulcanTests; 401 | PRODUCT_NAME = "$(TARGET_NAME)"; 402 | SWIFT_VERSION = 4.0; 403 | }; 404 | name = Debug; 405 | }; 406 | 933865D61DC33F5800B7D6F3 /* Release */ = { 407 | isa = XCBuildConfiguration; 408 | buildSettings = { 409 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 410 | INFOPLIST_FILE = Tests/VulcanTests/Info.plist; 411 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 412 | PRODUCT_BUNDLE_IDENTIFIER = jp.sasakky.VulcanTests; 413 | PRODUCT_NAME = "$(TARGET_NAME)"; 414 | SWIFT_VERSION = 4.0; 415 | }; 416 | name = Release; 417 | }; 418 | /* End XCBuildConfiguration section */ 419 | 420 | /* Begin XCConfigurationList section */ 421 | 933865B71DC33F5800B7D6F3 /* Build configuration list for PBXProject "Vulcan" */ = { 422 | isa = XCConfigurationList; 423 | buildConfigurations = ( 424 | 933865CF1DC33F5800B7D6F3 /* Debug */, 425 | 933865D01DC33F5800B7D6F3 /* Release */, 426 | ); 427 | defaultConfigurationIsVisible = 0; 428 | defaultConfigurationName = Release; 429 | }; 430 | 933865D11DC33F5800B7D6F3 /* Build configuration list for PBXNativeTarget "Vulcan" */ = { 431 | isa = XCConfigurationList; 432 | buildConfigurations = ( 433 | 933865D21DC33F5800B7D6F3 /* Debug */, 434 | 933865D31DC33F5800B7D6F3 /* Release */, 435 | ); 436 | defaultConfigurationIsVisible = 0; 437 | defaultConfigurationName = Release; 438 | }; 439 | 933865D41DC33F5800B7D6F3 /* Build configuration list for PBXNativeTarget "VulcanTests" */ = { 440 | isa = XCConfigurationList; 441 | buildConfigurations = ( 442 | 933865D51DC33F5800B7D6F3 /* Debug */, 443 | 933865D61DC33F5800B7D6F3 /* Release */, 444 | ); 445 | defaultConfigurationIsVisible = 0; 446 | defaultConfigurationName = Release; 447 | }; 448 | /* End XCConfigurationList section */ 449 | }; 450 | rootObject = 933865B41DC33F5800B7D6F3 /* Project object */; 451 | } 452 | -------------------------------------------------------------------------------- /Vulcan.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Vulcan.xcodeproj/xcshareddata/xcschemes/Vulcan.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Vulcan.xcodeproj/xcshareddata/xcschemes/VulcanTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /assets/demo_01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinSasaki/Vulcan/d6dc1b3c242e8ffe308a0200a2dbe1f3f02c4832/assets/demo_01.gif -------------------------------------------------------------------------------- /assets/demo_02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinSasaki/Vulcan/d6dc1b3c242e8ffe308a0200a2dbe1f3f02c4832/assets/demo_02.gif -------------------------------------------------------------------------------- /assets/sample_100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinSasaki/Vulcan/d6dc1b3c242e8ffe308a0200a2dbe1f3f02c4832/assets/sample_100.jpg -------------------------------------------------------------------------------- /assets/sample_1024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinSasaki/Vulcan/d6dc1b3c242e8ffe308a0200a2dbe1f3f02c4832/assets/sample_1024.jpg --------------------------------------------------------------------------------