├── .gitignore ├── Demo ├── IMYWebLoader.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── IMYWebLoader.xcworkspace │ └── contents.xcworkspacedata ├── IMYWebLoader │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── IMYIndexVC.h │ ├── IMYIndexVC.m │ ├── Info.plist │ ├── WKURLProtocolVC.h │ ├── WKURLProtocolVC.m │ └── main.m ├── IMYWebLoaderTests │ ├── IMYWebLoaderTests.m │ └── Info.plist ├── Podfile ├── Podfile.lock └── Pods │ ├── Headers │ ├── Private │ │ ├── IMYWebLoader │ │ │ ├── IMYWebAjaxHandler.h │ │ │ ├── IMYWebAjaxHandlerDefaultImpl.h │ │ │ ├── IMYWebCacheHandler.h │ │ │ ├── IMYWebCacheHandlerDefaultImpl.h │ │ │ ├── IMYWebData.h │ │ │ ├── IMYWebDefines.h │ │ │ ├── IMYWebLoader+Impl.h │ │ │ ├── IMYWebLoader.h │ │ │ ├── IMYWebNetworkHandler.h │ │ │ ├── IMYWebNetworkHandlerDefaultImpl.h │ │ │ ├── IMYWebOperation.h │ │ │ ├── IMYWebPrefetchHandler.h │ │ │ ├── IMYWebPrefetchHandlerDefaultImpl.h │ │ │ ├── IMYWebRequestHandleManager.h │ │ │ ├── IMYWebRequestHandleManagerDefaultImpl.h │ │ │ ├── IMYWebRequestHandlerDefaultImpl.h │ │ │ ├── IMYWebURLProtocol.h │ │ │ ├── IMYWebUtils.h │ │ │ ├── NSObject+WKCustomProtocolLoader.h │ │ │ └── WKUserContentController+IMYHookAjax.h │ │ └── XMLDictionary │ │ │ └── XMLDictionary.h │ └── Public │ │ ├── IMYWebLoader │ │ ├── IMYWebAjaxHandler.h │ │ ├── IMYWebAjaxHandlerDefaultImpl.h │ │ ├── IMYWebCacheHandler.h │ │ ├── IMYWebCacheHandlerDefaultImpl.h │ │ ├── IMYWebData.h │ │ ├── IMYWebDefines.h │ │ ├── IMYWebLoader+Impl.h │ │ ├── IMYWebLoader.h │ │ ├── IMYWebNetworkHandler.h │ │ ├── IMYWebNetworkHandlerDefaultImpl.h │ │ ├── IMYWebOperation.h │ │ ├── IMYWebPrefetchHandler.h │ │ ├── IMYWebPrefetchHandlerDefaultImpl.h │ │ ├── IMYWebRequestHandleManager.h │ │ ├── IMYWebRequestHandleManagerDefaultImpl.h │ │ ├── IMYWebRequestHandlerDefaultImpl.h │ │ ├── IMYWebURLProtocol.h │ │ ├── IMYWebUtils.h │ │ ├── NSObject+WKCustomProtocolLoader.h │ │ └── WKUserContentController+IMYHookAjax.h │ │ └── XMLDictionary │ │ └── XMLDictionary.h │ ├── Local Podspecs │ └── IMYWebLoader.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ └── project.pbxproj │ ├── Target Support Files │ ├── IMYWebLoader │ │ ├── IMYWebLoader-dummy.m │ │ ├── IMYWebLoader-prefix.pch │ │ └── IMYWebLoader.xcconfig │ ├── Pods-IMYWebLoader │ │ ├── Pods-IMYWebLoader-acknowledgements.markdown │ │ ├── Pods-IMYWebLoader-acknowledgements.plist │ │ ├── Pods-IMYWebLoader-dummy.m │ │ ├── Pods-IMYWebLoader-frameworks.sh │ │ ├── Pods-IMYWebLoader-resources.sh │ │ ├── Pods-IMYWebLoader.debug.xcconfig │ │ └── Pods-IMYWebLoader.release.xcconfig │ └── XMLDictionary │ │ ├── XMLDictionary-dummy.m │ │ ├── XMLDictionary-prefix.pch │ │ └── XMLDictionary.xcconfig │ └── XMLDictionary │ ├── LICENCE.md │ ├── README.md │ └── XMLDictionary │ ├── XMLDictionary.h │ └── XMLDictionary.m ├── IMYWebLoader.podspec ├── LICENSE ├── README.md └── Sources ├── Private ├── Handler │ ├── IMYWebCacheHandlerDefaultImpl.h │ ├── IMYWebCacheHandlerDefaultImpl.m │ ├── IMYWebNetworkHandlerDefaultImpl.h │ ├── IMYWebNetworkHandlerDefaultImpl.m │ ├── IMYWebPrefetchHandlerDefaultImpl.h │ └── IMYWebPrefetchHandlerDefaultImpl.m ├── HookAjax │ ├── IMYWebAjaxHandlerDefaultImpl.h │ ├── IMYWebAjaxHandlerDefaultImpl.m │ └── WKUserContentController+IMYHookAjax.m ├── Loader │ ├── IMYWebLoader+Impl.h │ └── IMYWebLoader+Impl.m ├── URLProtocol │ ├── IMYWebRequestHandleManagerDefaultImpl.h │ ├── IMYWebRequestHandleManagerDefaultImpl.m │ ├── IMYWebRequestHandlerDefaultImpl.h │ ├── IMYWebRequestHandlerDefaultImpl.m │ ├── IMYWebURLProtocol.h │ ├── IMYWebURLProtocol.m │ ├── NSObject+WKCustomProtocolLoader.h │ └── NSObject+WKCustomProtocolLoader.m └── Utils │ ├── IMYWebUtils.h │ └── IMYWebUtils.m ├── Public ├── Data │ ├── IMYWebData.h │ └── IMYWebData.m ├── Handler │ ├── IMYWebAjaxHandler.h │ ├── IMYWebCacheHandler.h │ ├── IMYWebNetworkHandler.h │ ├── IMYWebPrefetchHandler.h │ └── IMYWebRequestHandleManager.h ├── Header │ ├── IMYWebDefines.h │ ├── IMYWebOperation.h │ └── WKUserContentController+IMYHookAjax.h └── IMYWebLoader.h └── Resources └── imywk_hookajax.js /.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 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 362980621E64270A0091D742 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 362980611E64270A0091D742 /* main.m */; }; 11 | 362980651E64270A0091D742 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 362980641E64270A0091D742 /* AppDelegate.m */; }; 12 | 3629806B1E64270A0091D742 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 362980691E64270A0091D742 /* Main.storyboard */; }; 13 | 3629806D1E64270A0091D742 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3629806C1E64270A0091D742 /* Assets.xcassets */; }; 14 | 362980701E64270A0091D742 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3629806E1E64270A0091D742 /* LaunchScreen.storyboard */; }; 15 | 3629807B1E64270A0091D742 /* IMYWebLoaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3629807A1E64270A0091D742 /* IMYWebLoaderTests.m */; }; 16 | 36AF08DB1E9BB6F800BAC046 /* WKURLProtocolVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 36AF08DA1E9BB6F800BAC046 /* WKURLProtocolVC.m */; }; 17 | 36AF08E21E9BB91700BAC046 /* IMYIndexVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 36AF08E11E9BB91700BAC046 /* IMYIndexVC.m */; }; 18 | FCCBBD89D91DC203319EA56F /* libPods-IMYWebLoader.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 03710CF9824C45880EDAA428 /* libPods-IMYWebLoader.a */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 362980771E64270A0091D742 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 362980551E64270A0091D742 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 3629805C1E64270A0091D742; 27 | remoteInfo = IMYWebLoader; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 03710CF9824C45880EDAA428 /* libPods-IMYWebLoader.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-IMYWebLoader.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 1E1013721227A4D6FC824714 /* Pods-IMYWebLoader.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IMYWebLoader.debug.xcconfig"; path = "Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader.debug.xcconfig"; sourceTree = ""; }; 34 | 3629805D1E64270A0091D742 /* IMYWebLoader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IMYWebLoader.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 362980611E64270A0091D742 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 36 | 362980631E64270A0091D742 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 37 | 362980641E64270A0091D742 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 38 | 3629806A1E64270A0091D742 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 39 | 3629806C1E64270A0091D742 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 40 | 3629806F1E64270A0091D742 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 41 | 362980711E64270A0091D742 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | 362980761E64270A0091D742 /* IMYWebLoaderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IMYWebLoaderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 3629807A1E64270A0091D742 /* IMYWebLoaderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IMYWebLoaderTests.m; sourceTree = ""; }; 44 | 3629807C1E64270A0091D742 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | 36AF08D91E9BB6F800BAC046 /* WKURLProtocolVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKURLProtocolVC.h; sourceTree = ""; }; 46 | 36AF08DA1E9BB6F800BAC046 /* WKURLProtocolVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKURLProtocolVC.m; sourceTree = ""; }; 47 | 36AF08E01E9BB91700BAC046 /* IMYIndexVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IMYIndexVC.h; sourceTree = ""; }; 48 | 36AF08E11E9BB91700BAC046 /* IMYIndexVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IMYIndexVC.m; sourceTree = ""; }; 49 | 433795A53BC2B8F0327E78AD /* Pods-IMYWebLoader.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IMYWebLoader.release.xcconfig"; path = "Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader.release.xcconfig"; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 3629805A1E64270A0091D742 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | FCCBBD89D91DC203319EA56F /* libPods-IMYWebLoader.a in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | 362980731E64270A0091D742 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 25E668C9C0FB2F4B8A3E476A /* Frameworks */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 03710CF9824C45880EDAA428 /* libPods-IMYWebLoader.a */, 75 | ); 76 | name = Frameworks; 77 | sourceTree = ""; 78 | }; 79 | 362980541E64270A0091D742 = { 80 | isa = PBXGroup; 81 | children = ( 82 | 3629805F1E64270A0091D742 /* IMYWebLoader */, 83 | 362980791E64270A0091D742 /* IMYWebLoaderTests */, 84 | 3629805E1E64270A0091D742 /* Products */, 85 | 5023865AF897388D81F3E50F /* Pods */, 86 | 25E668C9C0FB2F4B8A3E476A /* Frameworks */, 87 | ); 88 | sourceTree = ""; 89 | }; 90 | 3629805E1E64270A0091D742 /* Products */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 3629805D1E64270A0091D742 /* IMYWebLoader.app */, 94 | 362980761E64270A0091D742 /* IMYWebLoaderTests.xctest */, 95 | ); 96 | name = Products; 97 | sourceTree = ""; 98 | }; 99 | 3629805F1E64270A0091D742 /* IMYWebLoader */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 362980631E64270A0091D742 /* AppDelegate.h */, 103 | 362980641E64270A0091D742 /* AppDelegate.m */, 104 | 362980691E64270A0091D742 /* Main.storyboard */, 105 | 3629806C1E64270A0091D742 /* Assets.xcassets */, 106 | 3629806E1E64270A0091D742 /* LaunchScreen.storyboard */, 107 | 362980711E64270A0091D742 /* Info.plist */, 108 | 362980601E64270A0091D742 /* Supporting Files */, 109 | 36AF08D91E9BB6F800BAC046 /* WKURLProtocolVC.h */, 110 | 36AF08DA1E9BB6F800BAC046 /* WKURLProtocolVC.m */, 111 | 36AF08E01E9BB91700BAC046 /* IMYIndexVC.h */, 112 | 36AF08E11E9BB91700BAC046 /* IMYIndexVC.m */, 113 | ); 114 | path = IMYWebLoader; 115 | sourceTree = ""; 116 | }; 117 | 362980601E64270A0091D742 /* Supporting Files */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 362980611E64270A0091D742 /* main.m */, 121 | ); 122 | name = "Supporting Files"; 123 | sourceTree = ""; 124 | }; 125 | 362980791E64270A0091D742 /* IMYWebLoaderTests */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 3629807A1E64270A0091D742 /* IMYWebLoaderTests.m */, 129 | 3629807C1E64270A0091D742 /* Info.plist */, 130 | ); 131 | path = IMYWebLoaderTests; 132 | sourceTree = ""; 133 | }; 134 | 5023865AF897388D81F3E50F /* Pods */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 1E1013721227A4D6FC824714 /* Pods-IMYWebLoader.debug.xcconfig */, 138 | 433795A53BC2B8F0327E78AD /* Pods-IMYWebLoader.release.xcconfig */, 139 | ); 140 | name = Pods; 141 | sourceTree = ""; 142 | }; 143 | /* End PBXGroup section */ 144 | 145 | /* Begin PBXNativeTarget section */ 146 | 3629805C1E64270A0091D742 /* IMYWebLoader */ = { 147 | isa = PBXNativeTarget; 148 | buildConfigurationList = 3629807F1E64270A0091D742 /* Build configuration list for PBXNativeTarget "IMYWebLoader" */; 149 | buildPhases = ( 150 | EABFC25E6BE876D8287524E4 /* [CP] Check Pods Manifest.lock */, 151 | 362980591E64270A0091D742 /* Sources */, 152 | 3629805A1E64270A0091D742 /* Frameworks */, 153 | 3629805B1E64270A0091D742 /* Resources */, 154 | AE1F4A421A66A1E5D15F5120 /* [CP] Embed Pods Frameworks */, 155 | B33618F5C5C3A08ADC28888E /* [CP] Copy Pods Resources */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | ); 161 | name = IMYWebLoader; 162 | productName = IMYWebLoader; 163 | productReference = 3629805D1E64270A0091D742 /* IMYWebLoader.app */; 164 | productType = "com.apple.product-type.application"; 165 | }; 166 | 362980751E64270A0091D742 /* IMYWebLoaderTests */ = { 167 | isa = PBXNativeTarget; 168 | buildConfigurationList = 362980821E64270A0091D742 /* Build configuration list for PBXNativeTarget "IMYWebLoaderTests" */; 169 | buildPhases = ( 170 | 362980721E64270A0091D742 /* Sources */, 171 | 362980731E64270A0091D742 /* Frameworks */, 172 | 362980741E64270A0091D742 /* Resources */, 173 | ); 174 | buildRules = ( 175 | ); 176 | dependencies = ( 177 | 362980781E64270A0091D742 /* PBXTargetDependency */, 178 | ); 179 | name = IMYWebLoaderTests; 180 | productName = IMYWebLoaderTests; 181 | productReference = 362980761E64270A0091D742 /* IMYWebLoaderTests.xctest */; 182 | productType = "com.apple.product-type.bundle.unit-test"; 183 | }; 184 | /* End PBXNativeTarget section */ 185 | 186 | /* Begin PBXProject section */ 187 | 362980551E64270A0091D742 /* Project object */ = { 188 | isa = PBXProject; 189 | attributes = { 190 | LastUpgradeCheck = 0820; 191 | ORGANIZATIONNAME = meetyou; 192 | TargetAttributes = { 193 | 3629805C1E64270A0091D742 = { 194 | CreatedOnToolsVersion = 8.2.1; 195 | DevelopmentTeam = AX5C7YM44M; 196 | ProvisioningStyle = Automatic; 197 | }; 198 | 362980751E64270A0091D742 = { 199 | CreatedOnToolsVersion = 8.2.1; 200 | DevelopmentTeam = 588AWE66S7; 201 | ProvisioningStyle = Automatic; 202 | TestTargetID = 3629805C1E64270A0091D742; 203 | }; 204 | }; 205 | }; 206 | buildConfigurationList = 362980581E64270A0091D742 /* Build configuration list for PBXProject "IMYWebLoader" */; 207 | compatibilityVersion = "Xcode 3.2"; 208 | developmentRegion = English; 209 | hasScannedForEncodings = 0; 210 | knownRegions = ( 211 | en, 212 | Base, 213 | ); 214 | mainGroup = 362980541E64270A0091D742; 215 | productRefGroup = 3629805E1E64270A0091D742 /* Products */; 216 | projectDirPath = ""; 217 | projectRoot = ""; 218 | targets = ( 219 | 3629805C1E64270A0091D742 /* IMYWebLoader */, 220 | 362980751E64270A0091D742 /* IMYWebLoaderTests */, 221 | ); 222 | }; 223 | /* End PBXProject section */ 224 | 225 | /* Begin PBXResourcesBuildPhase section */ 226 | 3629805B1E64270A0091D742 /* Resources */ = { 227 | isa = PBXResourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | 362980701E64270A0091D742 /* LaunchScreen.storyboard in Resources */, 231 | 3629806D1E64270A0091D742 /* Assets.xcassets in Resources */, 232 | 3629806B1E64270A0091D742 /* Main.storyboard in Resources */, 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | 362980741E64270A0091D742 /* Resources */ = { 237 | isa = PBXResourcesBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | }; 243 | /* End PBXResourcesBuildPhase section */ 244 | 245 | /* Begin PBXShellScriptBuildPhase section */ 246 | AE1F4A421A66A1E5D15F5120 /* [CP] Embed Pods Frameworks */ = { 247 | isa = PBXShellScriptBuildPhase; 248 | buildActionMask = 2147483647; 249 | files = ( 250 | ); 251 | inputPaths = ( 252 | ); 253 | name = "[CP] Embed Pods Frameworks"; 254 | outputPaths = ( 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | shellPath = /bin/sh; 258 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader-frameworks.sh\"\n"; 259 | showEnvVarsInLog = 0; 260 | }; 261 | B33618F5C5C3A08ADC28888E /* [CP] Copy Pods Resources */ = { 262 | isa = PBXShellScriptBuildPhase; 263 | buildActionMask = 2147483647; 264 | files = ( 265 | ); 266 | inputPaths = ( 267 | ); 268 | name = "[CP] Copy Pods Resources"; 269 | outputPaths = ( 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | shellPath = /bin/sh; 273 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader-resources.sh\"\n"; 274 | showEnvVarsInLog = 0; 275 | }; 276 | EABFC25E6BE876D8287524E4 /* [CP] Check Pods Manifest.lock */ = { 277 | isa = PBXShellScriptBuildPhase; 278 | buildActionMask = 2147483647; 279 | files = ( 280 | ); 281 | inputPaths = ( 282 | ); 283 | name = "[CP] Check Pods Manifest.lock"; 284 | outputPaths = ( 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | shellPath = /bin/sh; 288 | shellScript = "diff \"${PODS_ROOT}/../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"; 289 | showEnvVarsInLog = 0; 290 | }; 291 | /* End PBXShellScriptBuildPhase section */ 292 | 293 | /* Begin PBXSourcesBuildPhase section */ 294 | 362980591E64270A0091D742 /* Sources */ = { 295 | isa = PBXSourcesBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | 362980651E64270A0091D742 /* AppDelegate.m in Sources */, 299 | 36AF08E21E9BB91700BAC046 /* IMYIndexVC.m in Sources */, 300 | 36AF08DB1E9BB6F800BAC046 /* WKURLProtocolVC.m in Sources */, 301 | 362980621E64270A0091D742 /* main.m in Sources */, 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | 362980721E64270A0091D742 /* Sources */ = { 306 | isa = PBXSourcesBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | 3629807B1E64270A0091D742 /* IMYWebLoaderTests.m in Sources */, 310 | ); 311 | runOnlyForDeploymentPostprocessing = 0; 312 | }; 313 | /* End PBXSourcesBuildPhase section */ 314 | 315 | /* Begin PBXTargetDependency section */ 316 | 362980781E64270A0091D742 /* PBXTargetDependency */ = { 317 | isa = PBXTargetDependency; 318 | target = 3629805C1E64270A0091D742 /* IMYWebLoader */; 319 | targetProxy = 362980771E64270A0091D742 /* PBXContainerItemProxy */; 320 | }; 321 | /* End PBXTargetDependency section */ 322 | 323 | /* Begin PBXVariantGroup section */ 324 | 362980691E64270A0091D742 /* Main.storyboard */ = { 325 | isa = PBXVariantGroup; 326 | children = ( 327 | 3629806A1E64270A0091D742 /* Base */, 328 | ); 329 | name = Main.storyboard; 330 | sourceTree = ""; 331 | }; 332 | 3629806E1E64270A0091D742 /* LaunchScreen.storyboard */ = { 333 | isa = PBXVariantGroup; 334 | children = ( 335 | 3629806F1E64270A0091D742 /* Base */, 336 | ); 337 | name = LaunchScreen.storyboard; 338 | sourceTree = ""; 339 | }; 340 | /* End PBXVariantGroup section */ 341 | 342 | /* Begin XCBuildConfiguration section */ 343 | 3629807D1E64270A0091D742 /* Debug */ = { 344 | isa = XCBuildConfiguration; 345 | buildSettings = { 346 | ALWAYS_SEARCH_USER_PATHS = NO; 347 | CLANG_ANALYZER_NONNULL = YES; 348 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 349 | CLANG_CXX_LIBRARY = "libc++"; 350 | CLANG_ENABLE_MODULES = YES; 351 | CLANG_ENABLE_OBJC_ARC = YES; 352 | CLANG_WARN_BOOL_CONVERSION = YES; 353 | CLANG_WARN_CONSTANT_CONVERSION = YES; 354 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 355 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 356 | CLANG_WARN_EMPTY_BODY = YES; 357 | CLANG_WARN_ENUM_CONVERSION = YES; 358 | CLANG_WARN_INFINITE_RECURSION = YES; 359 | CLANG_WARN_INT_CONVERSION = YES; 360 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 361 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 362 | CLANG_WARN_UNREACHABLE_CODE = YES; 363 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 364 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 365 | COPY_PHASE_STRIP = NO; 366 | DEBUG_INFORMATION_FORMAT = dwarf; 367 | ENABLE_STRICT_OBJC_MSGSEND = YES; 368 | ENABLE_TESTABILITY = YES; 369 | GCC_C_LANGUAGE_STANDARD = gnu99; 370 | GCC_DYNAMIC_NO_PIC = NO; 371 | GCC_NO_COMMON_BLOCKS = YES; 372 | GCC_OPTIMIZATION_LEVEL = 0; 373 | GCC_PREPROCESSOR_DEFINITIONS = ( 374 | "DEBUG=1", 375 | "$(inherited)", 376 | ); 377 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 378 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 379 | GCC_WARN_UNDECLARED_SELECTOR = YES; 380 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 381 | GCC_WARN_UNUSED_FUNCTION = YES; 382 | GCC_WARN_UNUSED_VARIABLE = YES; 383 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 384 | MTL_ENABLE_DEBUG_INFO = YES; 385 | ONLY_ACTIVE_ARCH = YES; 386 | SDKROOT = iphoneos; 387 | }; 388 | name = Debug; 389 | }; 390 | 3629807E1E64270A0091D742 /* Release */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ALWAYS_SEARCH_USER_PATHS = NO; 394 | CLANG_ANALYZER_NONNULL = YES; 395 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 396 | CLANG_CXX_LIBRARY = "libc++"; 397 | CLANG_ENABLE_MODULES = YES; 398 | CLANG_ENABLE_OBJC_ARC = YES; 399 | CLANG_WARN_BOOL_CONVERSION = YES; 400 | CLANG_WARN_CONSTANT_CONVERSION = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 403 | CLANG_WARN_EMPTY_BODY = YES; 404 | CLANG_WARN_ENUM_CONVERSION = YES; 405 | CLANG_WARN_INFINITE_RECURSION = YES; 406 | CLANG_WARN_INT_CONVERSION = YES; 407 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 408 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 409 | CLANG_WARN_UNREACHABLE_CODE = YES; 410 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 411 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 412 | COPY_PHASE_STRIP = NO; 413 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 414 | ENABLE_NS_ASSERTIONS = NO; 415 | ENABLE_STRICT_OBJC_MSGSEND = YES; 416 | GCC_C_LANGUAGE_STANDARD = gnu99; 417 | GCC_NO_COMMON_BLOCKS = YES; 418 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 419 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 420 | GCC_WARN_UNDECLARED_SELECTOR = YES; 421 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 422 | GCC_WARN_UNUSED_FUNCTION = YES; 423 | GCC_WARN_UNUSED_VARIABLE = YES; 424 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 425 | MTL_ENABLE_DEBUG_INFO = NO; 426 | SDKROOT = iphoneos; 427 | VALIDATE_PRODUCT = YES; 428 | }; 429 | name = Release; 430 | }; 431 | 362980801E64270A0091D742 /* Debug */ = { 432 | isa = XCBuildConfiguration; 433 | baseConfigurationReference = 1E1013721227A4D6FC824714 /* Pods-IMYWebLoader.debug.xcconfig */; 434 | buildSettings = { 435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 436 | DEVELOPMENT_TEAM = AX5C7YM44M; 437 | INFOPLIST_FILE = IMYWebLoader/Info.plist; 438 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 439 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 440 | PRODUCT_BUNDLE_IDENTIFIER = meetyou.IMYWebLoader; 441 | PRODUCT_NAME = "$(TARGET_NAME)"; 442 | }; 443 | name = Debug; 444 | }; 445 | 362980811E64270A0091D742 /* Release */ = { 446 | isa = XCBuildConfiguration; 447 | baseConfigurationReference = 433795A53BC2B8F0327E78AD /* Pods-IMYWebLoader.release.xcconfig */; 448 | buildSettings = { 449 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 450 | DEVELOPMENT_TEAM = AX5C7YM44M; 451 | INFOPLIST_FILE = IMYWebLoader/Info.plist; 452 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 453 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 454 | PRODUCT_BUNDLE_IDENTIFIER = meetyou.IMYWebLoader; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | }; 457 | name = Release; 458 | }; 459 | 362980831E64270A0091D742 /* Debug */ = { 460 | isa = XCBuildConfiguration; 461 | buildSettings = { 462 | BUNDLE_LOADER = "$(TEST_HOST)"; 463 | DEVELOPMENT_TEAM = 588AWE66S7; 464 | INFOPLIST_FILE = IMYWebLoaderTests/Info.plist; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 466 | PRODUCT_BUNDLE_IDENTIFIER = meetyou.IMYWebLoaderTests; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/IMYWebLoader.app/IMYWebLoader"; 469 | }; 470 | name = Debug; 471 | }; 472 | 362980841E64270A0091D742 /* Release */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | BUNDLE_LOADER = "$(TEST_HOST)"; 476 | DEVELOPMENT_TEAM = 588AWE66S7; 477 | INFOPLIST_FILE = IMYWebLoaderTests/Info.plist; 478 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 479 | PRODUCT_BUNDLE_IDENTIFIER = meetyou.IMYWebLoaderTests; 480 | PRODUCT_NAME = "$(TARGET_NAME)"; 481 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/IMYWebLoader.app/IMYWebLoader"; 482 | }; 483 | name = Release; 484 | }; 485 | /* End XCBuildConfiguration section */ 486 | 487 | /* Begin XCConfigurationList section */ 488 | 362980581E64270A0091D742 /* Build configuration list for PBXProject "IMYWebLoader" */ = { 489 | isa = XCConfigurationList; 490 | buildConfigurations = ( 491 | 3629807D1E64270A0091D742 /* Debug */, 492 | 3629807E1E64270A0091D742 /* Release */, 493 | ); 494 | defaultConfigurationIsVisible = 0; 495 | defaultConfigurationName = Release; 496 | }; 497 | 3629807F1E64270A0091D742 /* Build configuration list for PBXNativeTarget "IMYWebLoader" */ = { 498 | isa = XCConfigurationList; 499 | buildConfigurations = ( 500 | 362980801E64270A0091D742 /* Debug */, 501 | 362980811E64270A0091D742 /* Release */, 502 | ); 503 | defaultConfigurationIsVisible = 0; 504 | defaultConfigurationName = Release; 505 | }; 506 | 362980821E64270A0091D742 /* Build configuration list for PBXNativeTarget "IMYWebLoaderTests" */ = { 507 | isa = XCConfigurationList; 508 | buildConfigurations = ( 509 | 362980831E64270A0091D742 /* Debug */, 510 | 362980841E64270A0091D742 /* Release */, 511 | ); 512 | defaultConfigurationIsVisible = 0; 513 | defaultConfigurationName = Release; 514 | }; 515 | /* End XCConfigurationList section */ 516 | }; 517 | rootObject = 362980551E64270A0091D742 /* Project object */; 518 | } 519 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // IMYWebLoader 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // Copyright © 2017年 meetyou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // IMYWebLoader 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // Copyright © 2017年 meetyou. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader/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 | } -------------------------------------------------------------------------------- /Demo/IMYWebLoader/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 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader/IMYIndexVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYIndexVC.h 3 | // IMYWebLoader 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // Copyright © 2017年 meetyou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface IMYIndexVC : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader/IMYIndexVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYIndexVC.m 3 | // IMYWebLoader 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // Copyright © 2017年 meetyou. All rights reserved. 7 | // 8 | 9 | #import "IMYIndexVC.h" 10 | #import "WKURLProtocolVC.h" 11 | #import 12 | 13 | @interface IMYCacheNewsRequestHandler : NSObject 14 | 15 | @end 16 | 17 | @implementation IMYIndexVC { 18 | NSArray *array; 19 | } 20 | 21 | + (void)initialize { 22 | [[IMYWebLoader defaultRequestManager] addRequestHandlerClass:[IMYCacheNewsRequestHandler class]]; 23 | } 24 | 25 | - (void)viewDidLoad { 26 | 27 | [super viewDidLoad]; 28 | 29 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 30 | 31 | array = @[@{@"id":@0, @"title":@"正常WKWebView"}, 32 | @{@"id":@1, @"title":@"Hook Ajax WKWebView"}, 33 | @{@"id":@2, @"title":@"正常加载-有缓存"}, 34 | @{@"id":@3, @"title":@"预加载"}]; 35 | } 36 | 37 | - (void)didReceiveMemoryWarning { 38 | [super didReceiveMemoryWarning]; 39 | // Dispose of any resources that can be recreated. 40 | } 41 | 42 | #pragma mark - Table view data source 43 | 44 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 45 | return array.count; 46 | } 47 | 48 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 49 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; 50 | 51 | NSDictionary *dict = array[indexPath.row]; 52 | cell.textLabel.text = dict[@"title"]; 53 | return cell; 54 | } 55 | 56 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 57 | { 58 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 59 | 60 | NSDictionary *dict = array[indexPath.row]; 61 | switch ([dict[@"id"] integerValue]) { 62 | case 0: { 63 | WKURLProtocolVC *vc = [WKURLProtocolVC new]; 64 | vc.urlString = @"http://www.w3school.com.cn/tiy/loadtext.asp?f=ajax_post2"; 65 | [self.navigationController pushViewController:vc animated:YES]; 66 | break; 67 | } 68 | case 1: { 69 | WKURLProtocolVC *vc = [WKURLProtocolVC new]; 70 | vc.urlString = @"http://www.w3school.com.cn/tiy/loadtext.asp?f=ajax_post2"; 71 | vc.testAjax = YES; 72 | [self.navigationController pushViewController:vc animated:YES]; 73 | break; 74 | } 75 | case 2: { 76 | NSString *urlString = @"https://news-node.seeyouyima.com/article?news_id=842947"; 77 | WKURLProtocolVC *vc = [WKURLProtocolVC new]; 78 | vc.urlString = urlString; 79 | [self.navigationController pushViewController:vc animated:YES]; 80 | break; 81 | } 82 | case 3: { 83 | [self prefetchWebVC]; 84 | break; 85 | } 86 | } 87 | } 88 | 89 | - (void)prefetchWebVC 90 | { 91 | NSString *urlString = @"https://news-node.seeyouyima.com/article?news_id=842947"; 92 | id data = [[IMYWebLoader defaultCacheHandler] dataForKey:urlString]; 93 | if (data) { 94 | WKURLProtocolVC *vc = [WKURLProtocolVC new]; 95 | vc.urlString = urlString; 96 | [self.navigationController pushViewController:vc animated:YES]; 97 | } else { 98 | id prefetcher = [[IMYWebLoader defaultPrefetchHandler] prefetchWebUrl:urlString]; 99 | /// kvo complated ... 100 | if (![(id)prefetcher observationInfo]) { 101 | [(id)prefetcher addObserver:self forKeyPath:@"complated" options:NSKeyValueObservingOptionNew context:nil]; 102 | } 103 | } 104 | } 105 | 106 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 107 | { 108 | if ([change[NSKeyValueChangeNewKey] boolValue]) { 109 | NSLog(@"预加载完成!"); 110 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 111 | [self prefetchWebVC]; 112 | }); 113 | } 114 | [object removeObserver:self forKeyPath:@"complated"]; 115 | } 116 | 117 | @end 118 | 119 | 120 | 121 | @implementation IMYCacheNewsRequestHandler 122 | 123 | /// 是否要拦截该 request 124 | + (BOOL)shouldHookWithRequest:(NSURLRequest *)request 125 | { 126 | if ([request.URL.absoluteString containsString:@"news-node.seeyouyima.com"]) { 127 | return YES; 128 | } 129 | return NO; 130 | } 131 | 132 | /// 取消该 Request 的拦截,只要有一个 class 返回 YES,则不拦截该请求 133 | + (BOOL)cancelHookWithRequest:(NSURLRequest *)request 134 | { 135 | return NO; 136 | } 137 | 138 | /// 根据 Request 返回具体的请求实例, 会使用第一个返回的请求对象 139 | + (nullable id)requestHandlerWithRequest:(NSURLRequest *)request 140 | { 141 | return nil; 142 | } 143 | 144 | /// 开始加载 145 | - (void)startLoadingWithDelegate:(id)delegate 146 | { 147 | 148 | } 149 | 150 | /// 停止加载 151 | - (void)stopLoading 152 | { 153 | 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader/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 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader/WKURLProtocolVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKURLProtocolVC.h 3 | // IMYWebLoader 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // Copyright © 2017年 meetyou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WKURLProtocolVC : UIViewController 12 | 13 | @property (nonatomic, assign) BOOL testAjax; 14 | @property (nonatomic, copy) NSString *urlString; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader/WKURLProtocolVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKURLProtocolVC.m 3 | // IMYWebLoader 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // Copyright © 2017年 meetyou. All rights reserved. 7 | // 8 | 9 | #import "WKURLProtocolVC.h" 10 | #import 11 | #import 12 | 13 | @interface WKURLProtocolVC () 14 | @property (nonatomic, weak) WKWebView *webView; 15 | @end 16 | 17 | @implementation WKURLProtocolVC 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | 22 | WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; 23 | configuration.userContentController = [[WKUserContentController alloc] init]; 24 | 25 | if (self.testAjax) { 26 | [configuration.userContentController imy_installHookAjax]; 27 | } 28 | 29 | WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration]; 30 | webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 31 | [self.view addSubview:webView]; 32 | self.webView = webView; 33 | 34 | [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.urlString]]]; 35 | } 36 | 37 | - (void)dealloc 38 | { 39 | [self.webView.configuration.userContentController imy_uninstallHookAjax]; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Demo/IMYWebLoader/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // IMYWebLoader 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // Copyright © 2017年 meetyou. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Demo/IMYWebLoaderTests/IMYWebLoaderTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebLoaderTests.m 3 | // IMYWebLoaderTests 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // Copyright © 2017年 meetyou. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface IMYWebLoaderTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation IMYWebLoaderTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Demo/IMYWebLoaderTests/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 | -------------------------------------------------------------------------------- /Demo/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios,'7.0' 2 | 3 | target 'IMYWebLoader' do 4 | 5 | pod 'IMYWebLoader', :path => './..' 6 | 7 | end 8 | -------------------------------------------------------------------------------- /Demo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - IMYWebLoader (0.1): 3 | - XMLDictionary 4 | - XMLDictionary (1.4.1) 5 | 6 | DEPENDENCIES: 7 | - IMYWebLoader (from `./..`) 8 | 9 | EXTERNAL SOURCES: 10 | IMYWebLoader: 11 | :path: ./.. 12 | 13 | SPEC CHECKSUMS: 14 | IMYWebLoader: 35fc8b310a92b520a5ce98248472dec1dc069553 15 | XMLDictionary: fa07b6ff422b3a91d47a5de9bc82e3fc04fbd167 16 | 17 | PODFILE CHECKSUM: 4860998863bdd0c88d4ec89165693f448c2f0d46 18 | 19 | COCOAPODS: 1.1.1 20 | -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebAjaxHandler.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Handler/IMYWebAjaxHandler.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebAjaxHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/HookAjax/IMYWebAjaxHandlerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebCacheHandler.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Handler/IMYWebCacheHandler.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebCacheHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/Handler/IMYWebCacheHandlerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebData.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Data/IMYWebData.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebDefines.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Header/IMYWebDefines.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebLoader+Impl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/Loader/IMYWebLoader+Impl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebLoader.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/IMYWebLoader.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebNetworkHandler.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Handler/IMYWebNetworkHandler.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebNetworkHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/Handler/IMYWebNetworkHandlerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebOperation.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Header/IMYWebOperation.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebPrefetchHandler.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Handler/IMYWebPrefetchHandler.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebPrefetchHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/Handler/IMYWebPrefetchHandlerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebRequestHandleManager.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Handler/IMYWebRequestHandleManager.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebRequestHandleManagerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/URLProtocol/IMYWebRequestHandleManagerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebRequestHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/URLProtocol/IMYWebRequestHandlerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebURLProtocol.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/URLProtocol/IMYWebURLProtocol.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/IMYWebUtils.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/Utils/IMYWebUtils.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/NSObject+WKCustomProtocolLoader.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/URLProtocol/NSObject+WKCustomProtocolLoader.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/IMYWebLoader/WKUserContentController+IMYHookAjax.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Header/WKUserContentController+IMYHookAjax.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Private/XMLDictionary/XMLDictionary.h: -------------------------------------------------------------------------------- 1 | ../../../XMLDictionary/XMLDictionary/XMLDictionary.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebAjaxHandler.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Handler/IMYWebAjaxHandler.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebAjaxHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/HookAjax/IMYWebAjaxHandlerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebCacheHandler.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Handler/IMYWebCacheHandler.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebCacheHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/Handler/IMYWebCacheHandlerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebData.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Data/IMYWebData.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebDefines.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Header/IMYWebDefines.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebLoader+Impl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/Loader/IMYWebLoader+Impl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebLoader.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/IMYWebLoader.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebNetworkHandler.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Handler/IMYWebNetworkHandler.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebNetworkHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/Handler/IMYWebNetworkHandlerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebOperation.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Header/IMYWebOperation.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebPrefetchHandler.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Handler/IMYWebPrefetchHandler.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebPrefetchHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/Handler/IMYWebPrefetchHandlerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebRequestHandleManager.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Handler/IMYWebRequestHandleManager.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebRequestHandleManagerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/URLProtocol/IMYWebRequestHandleManagerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebRequestHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/URLProtocol/IMYWebRequestHandlerDefaultImpl.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebURLProtocol.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/URLProtocol/IMYWebURLProtocol.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/IMYWebUtils.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/Utils/IMYWebUtils.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/NSObject+WKCustomProtocolLoader.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Private/URLProtocol/NSObject+WKCustomProtocolLoader.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/IMYWebLoader/WKUserContentController+IMYHookAjax.h: -------------------------------------------------------------------------------- 1 | ../../../../../Sources/Public/Header/WKUserContentController+IMYHookAjax.h -------------------------------------------------------------------------------- /Demo/Pods/Headers/Public/XMLDictionary/XMLDictionary.h: -------------------------------------------------------------------------------- 1 | ../../../XMLDictionary/XMLDictionary/XMLDictionary.h -------------------------------------------------------------------------------- /Demo/Pods/Local Podspecs/IMYWebLoader.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IMYWebLoader", 3 | "version": "0.1", 4 | "description": "Web resources loading by Prefetch/Cache/Hook, Support UIWebView/WKWebView ... Orz", 5 | "license": "MIT", 6 | "summary": "支持对 UIWebView/WKWebView 的资源, 进行 预加载、缓存、拦截 等操作 ... Orz", 7 | "homepage": "https://github.com/li6185377/IMYWebLoader", 8 | "authors": { 9 | "ljh": "137249466@qq.com" 10 | }, 11 | "source": { 12 | "git": "https://github.com/li6185377/IMYWebLoader.git", 13 | "tag": "0.1" 14 | }, 15 | "requires_arc": true, 16 | "platforms": { 17 | "ios": "7.0" 18 | }, 19 | "source_files": [ 20 | "Sources/Private/**/*.{h,m}", 21 | "Sources/Public/**/*.{h,m}" 22 | ], 23 | "resources": "Sources/Resources/**/*.{js}", 24 | "weak_frameworks": [ 25 | "JavaScriptCore", 26 | "WebKit" 27 | ], 28 | "dependencies": { 29 | "XMLDictionary": [ 30 | 31 | ] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Demo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - IMYWebLoader (0.1): 3 | - XMLDictionary 4 | - XMLDictionary (1.4.1) 5 | 6 | DEPENDENCIES: 7 | - IMYWebLoader (from `./..`) 8 | 9 | EXTERNAL SOURCES: 10 | IMYWebLoader: 11 | :path: ./.. 12 | 13 | SPEC CHECKSUMS: 14 | IMYWebLoader: 35fc8b310a92b520a5ce98248472dec1dc069553 15 | XMLDictionary: fa07b6ff422b3a91d47a5de9bc82e3fc04fbd167 16 | 17 | PODFILE CHECKSUM: 4860998863bdd0c88d4ec89165693f448c2f0d46 18 | 19 | COCOAPODS: 1.1.1 20 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/IMYWebLoader/IMYWebLoader-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_IMYWebLoader : NSObject 3 | @end 4 | @implementation PodsDummy_IMYWebLoader 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/IMYWebLoader/IMYWebLoader-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/IMYWebLoader/IMYWebLoader.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/IMYWebLoader 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/IMYWebLoader" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/IMYWebLoader" "${PODS_ROOT}/Headers/Public/XMLDictionary" 4 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/XMLDictionary" 5 | OTHER_LDFLAGS = -weak_framework "JavaScriptCore" -weak_framework "WebKit" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## IMYWebLoader 5 | 6 | MIT License 7 | 8 | Copyright (c) 2017 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | ## XMLDictionary 30 | 31 | XMLDictionary 32 | 33 | Copyright (C) 2011 Charcoal Design 34 | 35 | This software is provided 'as-is', without any express or implied 36 | warranty. In no event will the authors be held liable for any damages 37 | arising from the use of this software. 38 | 39 | Permission is granted to anyone to use this software for any purpose, 40 | including commercial applications, and to alter it and redistribute it 41 | freely, subject to the following restrictions: 42 | 43 | 1. The origin of this software must not be misrepresented; you must not 44 | claim that you wrote the original software. If you use this software 45 | in a product, an acknowledgment in the product documentation would be 46 | appreciated but is not required. 47 | 2. Altered source versions must be plainly marked as such, and must not be 48 | misrepresented as being the original software. 49 | 3. This notice may not be removed or altered from any source distribution. 50 | Generated by CocoaPods - https://cocoapods.org 51 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | MIT License 18 | 19 | Copyright (c) 2017 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | IMYWebLoader 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | XMLDictionary 49 | 50 | Copyright (C) 2011 Charcoal Design 51 | 52 | This software is provided 'as-is', without any express or implied 53 | warranty. In no event will the authors be held liable for any damages 54 | arising from the use of this software. 55 | 56 | Permission is granted to anyone to use this software for any purpose, 57 | including commercial applications, and to alter it and redistribute it 58 | freely, subject to the following restrictions: 59 | 60 | 1. The origin of this software must not be misrepresented; you must not 61 | claim that you wrote the original software. If you use this software 62 | in a product, an acknowledgment in the product documentation would be 63 | appreciated but is not required. 64 | 2. Altered source versions must be plainly marked as such, and must not be 65 | misrepresented as being the original software. 66 | 3. This notice may not be removed or altered from any source distribution. 67 | License 68 | zlib 69 | Title 70 | XMLDictionary 71 | Type 72 | PSGroupSpecifier 73 | 74 | 75 | FooterText 76 | Generated by CocoaPods - https://cocoapods.org 77 | Title 78 | 79 | Type 80 | PSGroupSpecifier 81 | 82 | 83 | StringsTable 84 | Acknowledgements 85 | Title 86 | Acknowledgements 87 | 88 | 89 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_IMYWebLoader : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_IMYWebLoader 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | install_resource() 27 | { 28 | if [[ "$1" = /* ]] ; then 29 | RESOURCE_PATH="$1" 30 | else 31 | RESOURCE_PATH="${PODS_ROOT}/$1" 32 | fi 33 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 34 | cat << EOM 35 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 36 | EOM 37 | exit 1 38 | fi 39 | case $RESOURCE_PATH in 40 | *.storyboard) 41 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 42 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 43 | ;; 44 | *.xib) 45 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 46 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 47 | ;; 48 | *.framework) 49 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 50 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 51 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 52 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 53 | ;; 54 | *.xcdatamodel) 55 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 56 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 57 | ;; 58 | *.xcdatamodeld) 59 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 60 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 61 | ;; 62 | *.xcmappingmodel) 63 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 64 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 65 | ;; 66 | *.xcassets) 67 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 68 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 69 | ;; 70 | *) 71 | echo "$RESOURCE_PATH" 72 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 73 | ;; 74 | esac 75 | } 76 | if [[ "$CONFIGURATION" == "Debug" ]]; then 77 | install_resource "../../Sources/Resources/imywk_hookajax.js" 78 | fi 79 | if [[ "$CONFIGURATION" == "Release" ]]; then 80 | install_resource "../../Sources/Resources/imywk_hookajax.js" 81 | fi 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "${PODS_ROOT}*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/IMYWebLoader" "${PODS_ROOT}/Headers/Public/XMLDictionary" 4 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/IMYWebLoader" "$PODS_CONFIGURATION_BUILD_DIR/XMLDictionary" 5 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/IMYWebLoader" -isystem "${PODS_ROOT}/Headers/Public/XMLDictionary" 6 | OTHER_LDFLAGS = $(inherited) -ObjC -l"IMYWebLoader" -l"XMLDictionary" -weak_framework "JavaScriptCore" -weak_framework "WebKit" 7 | PODS_BUILD_DIR = $BUILD_DIR 8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-IMYWebLoader/Pods-IMYWebLoader.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/IMYWebLoader" "${PODS_ROOT}/Headers/Public/XMLDictionary" 4 | LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/IMYWebLoader" "$PODS_CONFIGURATION_BUILD_DIR/XMLDictionary" 5 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/IMYWebLoader" -isystem "${PODS_ROOT}/Headers/Public/XMLDictionary" 6 | OTHER_LDFLAGS = $(inherited) -ObjC -l"IMYWebLoader" -l"XMLDictionary" -weak_framework "JavaScriptCore" -weak_framework "WebKit" 7 | PODS_BUILD_DIR = $BUILD_DIR 8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/XMLDictionary/XMLDictionary-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_XMLDictionary : NSObject 3 | @end 4 | @implementation PodsDummy_XMLDictionary 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/XMLDictionary/XMLDictionary-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/XMLDictionary/XMLDictionary.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/XMLDictionary 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/XMLDictionary" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/IMYWebLoader" "${PODS_ROOT}/Headers/Public/XMLDictionary" 4 | PODS_BUILD_DIR = $BUILD_DIR 5 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 8 | SKIP_INSTALL = YES 9 | -------------------------------------------------------------------------------- /Demo/Pods/XMLDictionary/LICENCE.md: -------------------------------------------------------------------------------- 1 | XMLDictionary 2 | 3 | Copyright (C) 2011 Charcoal Design 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /Demo/Pods/XMLDictionary/README.md: -------------------------------------------------------------------------------- 1 | Purpose 2 | -------------- 3 | 4 | XMLDictionary is a class designed to simplify parsing and generating of XML on iOS and Mac OS. XMLDictionary is built on top of the NSXMLParser classes, but behaves more like a DOM-style parser rather than SAX parser, in that it creates a tree of objects rather than generating events at the start and end of each node. 5 | 6 | Unlike other DOM parsers, XMLDictionary does not attempt to replicate all of the nuances of the XML standard such as the ability to nest tags within text. If you need to represent something like an HTML document then XMLDictionary won't work for you. If you want to use XML as a data interchange format for passing nested data structures then XMLDictionary may well provide a simpler solution than other DOM-based parsers. 7 | 8 | 9 | Supported OS & SDK Versions 10 | ----------------------------- 11 | 12 | * Supported build target - iOS 10.2 / Mac OS 10.12 (Xcode 8.2, Apple LLVM compiler 8.0) 13 | * Earliest supported deployment target - iOS 8.0 / Mac OS 10.10 14 | * Earliest compatible deployment target - iOS 4.3 / Mac OS 10.6 15 | 16 | NOTE: 'Supported' means that the library has been tested with this version. 'Compatible' means that the library should work on this OS version (i.e. it doesn't rely on any unavailable SDK features) but is no longer being tested for compatibility and may require tweaking or bug fixes to run correctly. 17 | 18 | 19 | ARC Compatibility 20 | ------------------ 21 | 22 | As of version 1.1, XMLDictionary requires ARC. If you wish to use XMLDictionary in a non-ARC project, just add the -fobjc-arc compiler flag to the XMLDictionary.m class. To do this, go to the Build Phases tab in your target settings, open the Compile Sources group, double-click XMLDictionary.m in the list and type -fobjc-arc into the popover. 23 | 24 | If you wish to convert your whole project to ARC, comment out the #error line in XMLDictionary.m, then run the Edit > Refactor > Convert to Objective-C ARC... tool in Xcode and make sure all files that you wish to use ARC for (including XMLDictionary.m) are checked. 25 | 26 | 27 | Thread Safety 28 | -------------- 29 | 30 | XMLDictionary's methods should all be thread safe. It is safe to use multiple XMLDictionaryParsers concurrently on different threads, but it is not safe to call the same parser concurrently on multiple threads. 31 | 32 | 33 | Installation 34 | -------------- 35 | 36 | To use the XMLDictionary in an app, just drag the class files into your project. 37 | 38 | 39 | XMLDictionaryParser 40 | --------------------- 41 | 42 | The XMLDictionaryParser class is responsible for parsing an XML file into a dictionary. You don't normally need to use this class explicitly as you can just use the utility methods added to NSDictionary, however it can be useful if you want to modify the default parser settings. 43 | 44 | You can create new instances of XMLDictionaryParser if you need to use multiple different settings for different dictionaries. Once you have created an XMLDictionaryParser you can use the following methods to parse XML files using that specific parser instance: 45 | 46 | - (NSDictionary *)dictionaryWithData:(NSData *)data; 47 | - (NSDictionary *)dictionaryWithString:(NSString *)string; 48 | - (NSDictionary *)dictionaryWithFile:(NSString *)path; 49 | - (NSDictionary *)dictionaryWithParser:(NSXMLParser *)parser; 50 | 51 | Alternatively, you can simply modify the settings of `[XMLDictionaryParser sharedInstance]` to affect the settings for all dictionaries parsed subsequently using the NSDictionary category extension methods. 52 | 53 | Use the following properties to tweak the parsing behaviour: 54 | 55 | @property (nonatomic, assign) BOOL collapseTextNodes; 56 | 57 | If YES (the default value), tags that contain only text and have no children, attributes or comments will be collapsed into a single string object, simplifying traversal of the object tree. 58 | 59 | @property (nonatomic, assign) BOOL stripEmptyNodes; 60 | 61 | If YES (the default value), tags that are empty (have no children, attributes, text or comments) will be stripped. 62 | 63 | @property (nonatomic, assign) BOOL trimWhiteSpace; 64 | 65 | If YES (the default value), leading and trailing white space will be trimmed from text nodes, and text nodes containing only white space will be omitted from the dictionary. 66 | 67 | @property (nonatomic, assign) BOOL alwaysUseArrays; 68 | 69 | If `YES`, the every child node will be represented as an array, even if there is only one of them. This simplifies the logic needed to cope with properties that may be duplicated because you don't need to use `[value isKindOfClass:[NSArray class]]` to check the for the singular case. Defaults to `NO`. 70 | 71 | @property (nonatomic, assign) BOOL preserveComments; 72 | 73 | If `YES`, XML comments will be grouped into an array under the key `__comments` and can be accessed via the `comments` method. Defaults to `NO`. 74 | 75 | @property (nonatomic, assign) XMLDictionaryAttributesMode attributesMode; 76 | 77 | This property controls how XML attributes are handled. The default is `XMLDictionaryAttributesModePrefixed` meaning that attributes will be included in the dictionary, with an _ (underscore) prefix to avoid namespace collisions. Alternative values are `XMLDictionaryAttributesModeDictionary`, which will place all the attributes in a separate dictionary, `XMLDictionaryAttributesModeUnprefixed`, which includes the attributes without prefix (which may cause collisions with nodes) and `XMLDictionaryAttributesModeDiscard`, which will strip the attributes. 78 | 79 | @property (nonatomic, assign) XMLDictionaryNodeNameMode nodeNameMode; 80 | 81 | This property controls how the node name is handled. The default value is `XMLDictionaryNodeNameModeRootOnly`, meaning that the node name will only be included in the root dictionary (the names for the children can be inferred from the dictionary keys, but the `nodeName` method won't work for anything except the root node). Alternative values are `XMLDictionaryNodeNameModeAlways`, meaning that the node name will be included in the dictionary with the key `__name` (and can be accessed using the `nodeName`) method, or `XMLDictionaryNodeNameModeNever` which will never include the `__name' key. 82 | 83 | 84 | Category Methods 85 | ----------------- 86 | 87 | XMLDictionary extends NSDictionary with the following methods: 88 | 89 | + (NSDictionary *)dictionaryWithXMLParser:(NSParser *)parser; 90 | 91 | Create a new NSDictionary object from an existing NSXMLParser. Useful if fetching data through AFNetworking. 92 | 93 | + (NSDictionary *)dictionaryWithXMLData:(NSData *)data; 94 | 95 | Create a new NSDictionary object from XML-encoded data. 96 | 97 | + (NSDictionary *)dictionaryWithXMLString:(NSString *)string; 98 | 99 | Create a new NSDictionary object from XML-encoded string. 100 | 101 | + (NSDictionary *)dictionaryWithXMLFile:(NSString *)path; 102 | 103 | Create a new NSDictionary object from and XML-encoded file. 104 | 105 | - (NSString *)attributeForKey:(NSString *)key; 106 | 107 | Get the XML attribute for a given key (key name should not include prefix). 108 | 109 | - (NSDictionary *)attributes; 110 | 111 | Get a dictionary of all XML attributes for a given node's dictionary. If the node has no attributes then this will return nil. 112 | 113 | - (NSDictionary *)childNodes; 114 | 115 | Get a dictionary of all child nodes for a given node's dictionary. If multiple nodes have the same name they will be grouped into an array. If the node has no children then this will return nil. 116 | 117 | - (NSArray *)comments; 118 | 119 | Get an array of all comments for a given node. Note that the nesting relative to other nodes is not preserved. If the node has no comments then this will return nil. 120 | 121 | - (NSString *)nodeName; 122 | 123 | Get the name of the node. If the name is not known this will return nil. 124 | 125 | - (NSString *)innerText; 126 | 127 | Get the text content of the node. If the node has no text content, this will return nil; 128 | 129 | - (NSString *)innerXML; 130 | 131 | Get the contents of the node as an XML-encoded string. This XML string will not include the container node itself. 132 | 133 | - (NSString *)XMLString; 134 | 135 | Get the node and its content as an XML-encoded string. If the node name is not known, the top level tag will be called ``. 136 | 137 | - (NSArray *)arrayValueForKeyPath:(NSString *)keyPath; 138 | 139 | Works just like `valueForKeyPath:` except that the value returned will always be an array. So if there is only a single value, it will be returned as `@[value]`. 140 | 141 | - (NSString *)stringValueForKeyPath:(NSString *)keyPath; 142 | 143 | Works just like `valueForKeyPath:` except that the value returned will always be a string. So if the value is a dictionary, the text value of `innerText` will be returned, and if the value is an array, the first item will be returned. 144 | 145 | - (NSDictionary *)dictionaryValueForKeyPath:(NSString *)keyPath; 146 | 147 | Works just like `valueForKeyPath:` except that the value returned will always be a dictionary. So if the collapseTextNodes option is enabled and the value is a string, this will convert it back to a dictionary before returning, and if the value is an array, the first item will be returned. 148 | 149 | 150 | Usage 151 | -------- 152 | 153 | The simplest way to load an XML file is as follows: 154 | 155 | NSString *filePath = [[NSBundle mainBundle] pathForResource:@"someFile" ofType:@"xml"]; 156 | NSDictionary *xmlDoc = [NSDictionary dictionaryWithXMLFile:filePath]; 157 | 158 | You can then iterate over the dictionary as you would with any other object tree, e.g. one loaded from a Plist. 159 | 160 | To access nested nodes and attributes, you can use the valueForKeyPath syntax. For example to get the string value of `` from the following XML: 161 | 162 | 163 | 164 | Hello World 165 | 166 | Was his name-oh 167 | 168 | 169 | You would write: 170 | 171 | NSString *foo = [xmlDoc valueForKeyPath:@"bar.foo"]; 172 | 173 | The above examples assumes that you are using the default setting for `collapseTextNodes` and `alwaysUseArrays`. If `collapseTextNodes` is disabled then you would instead access ``'s value by writing: 174 | 175 | NSString *foo = [[xmlDoc valueForKeyPath:@"bar.foo"] innerText]; 176 | 177 | If the `alwaysUseArrays` option is enabled then would use one of the following, depending on the `collapseTextNodes` property: 178 | 179 | NSString *foo = [[xmlDoc valueForKeyPath:@"bar.foo"] firstObject]; 180 | NSString *foo = [[[xmlDoc valueForKeyPath:@"bar.foo"] firstObject] innerText]; 181 | 182 | To get the cliche attribute of `bar`, you could write: 183 | 184 | NSString *barCliche = [xmlDoc[@"bar] attributes][@"cliche"]; 185 | 186 | If the `attributesMode` is set to the default value of `XMLDictionaryAttributesModePrefixed` then you can also do this: 187 | 188 | NSString *barCliche = [xmlDoc valueForKeyPath:@"bar._cliche"]; 189 | 190 | Or if it is set to `XMLDictionaryAttributesModeUnprefixed` you would simply do this: 191 | 192 | NSString *barCliche = [xmlDoc valueForKeyPath:@"bar.cliche"]; 193 | 194 | 195 | Release Notes 196 | ---------------- 197 | 198 | Version 1.4.1 199 | 200 | - Upgraded for Xcode 8.2 201 | - Added tvOS and watchOS support to podspec 202 | 203 | Version 1.4 204 | 205 | - Added dictionaryWithXMLParser: constructor method 206 | - Added wrapRootNode option as a nicer way to preserve root node name 207 | - No longer crashes if non-string values are used as keys or attributes 208 | - Now complies with the -Weverything warning level 209 | 210 | Version 1.3 211 | 212 | - added stripEmptyNodes property (defaults to YES) 213 | - added arrayValueForKeyPath, stringValueForKeyPath and dictionaryValueForKeyPath methods to simplify working with data 214 | 215 | Version 1.2.2 216 | 217 | - sharedInstance method no longer returns a new instance each time 218 | 219 | Version 1.2.1 220 | 221 | - Removed isa reference, deprecated in iOS 7 222 | 223 | Version 1.2 224 | 225 | - Exposed XMLDictionaryParser object, which can be used to configure the parser 226 | - Parsing options can now be changed without modifying the library 227 | - Added option to always encode properties as arrays 228 | - `__name` and `__coment` keys are no longer included by default 229 | - Apostrophe is now encoded as `'` 230 | - removed `attributeForKey:` method 231 | 232 | Version 1.1 233 | 234 | - Updated to use ARC 235 | - Added podspec 236 | 237 | Version 1.0 238 | 239 | - Initial release 240 | -------------------------------------------------------------------------------- /Demo/Pods/XMLDictionary/XMLDictionary/XMLDictionary.h: -------------------------------------------------------------------------------- 1 | // 2 | // XMLDictionary.h 3 | // 4 | // Version 1.4.1 5 | // 6 | // Created by Nick Lockwood on 15/11/2010. 7 | // Copyright 2010 Charcoal Design. All rights reserved. 8 | // 9 | // Get the latest version of XMLDictionary from here: 10 | // 11 | // https://github.com/nicklockwood/XMLDictionary 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #import 33 | #pragma GCC diagnostic push 34 | #pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis" 35 | 36 | 37 | NS_ASSUME_NONNULL_BEGIN 38 | 39 | 40 | typedef NS_ENUM(NSInteger, XMLDictionaryAttributesMode) 41 | { 42 | XMLDictionaryAttributesModePrefixed = 0, //default 43 | XMLDictionaryAttributesModeDictionary, 44 | XMLDictionaryAttributesModeUnprefixed, 45 | XMLDictionaryAttributesModeDiscard 46 | }; 47 | 48 | 49 | typedef NS_ENUM(NSInteger, XMLDictionaryNodeNameMode) 50 | { 51 | XMLDictionaryNodeNameModeRootOnly = 0, //default 52 | XMLDictionaryNodeNameModeAlways, 53 | XMLDictionaryNodeNameModeNever 54 | }; 55 | 56 | 57 | static NSString *const XMLDictionaryAttributesKey = @"__attributes"; 58 | static NSString *const XMLDictionaryCommentsKey = @"__comments"; 59 | static NSString *const XMLDictionaryTextKey = @"__text"; 60 | static NSString *const XMLDictionaryNodeNameKey = @"__name"; 61 | static NSString *const XMLDictionaryAttributePrefix = @"_"; 62 | 63 | 64 | @interface XMLDictionaryParser : NSObject 65 | 66 | + (XMLDictionaryParser *)sharedInstance; 67 | 68 | @property (nonatomic, assign) BOOL collapseTextNodes; // defaults to YES 69 | @property (nonatomic, assign) BOOL stripEmptyNodes; // defaults to YES 70 | @property (nonatomic, assign) BOOL trimWhiteSpace; // defaults to YES 71 | @property (nonatomic, assign) BOOL alwaysUseArrays; // defaults to NO 72 | @property (nonatomic, assign) BOOL preserveComments; // defaults to NO 73 | @property (nonatomic, assign) BOOL wrapRootNode; // defaults to NO 74 | 75 | @property (nonatomic, assign) XMLDictionaryAttributesMode attributesMode; 76 | @property (nonatomic, assign) XMLDictionaryNodeNameMode nodeNameMode; 77 | 78 | - (nullable NSDictionary *)dictionaryWithParser:(NSXMLParser *)parser; 79 | - (nullable NSDictionary *)dictionaryWithData:(NSData *)data; 80 | - (nullable NSDictionary *)dictionaryWithString:(NSString *)string; 81 | - (nullable NSDictionary *)dictionaryWithFile:(NSString *)path; 82 | 83 | @end 84 | 85 | 86 | @interface NSDictionary (XMLDictionary) 87 | 88 | + (nullable NSDictionary *)dictionaryWithXMLParser:(NSXMLParser *)parser; 89 | + (nullable NSDictionary *)dictionaryWithXMLData:(NSData *)data; 90 | + (nullable NSDictionary *)dictionaryWithXMLString:(NSString *)string; 91 | + (nullable NSDictionary *)dictionaryWithXMLFile:(NSString *)path; 92 | 93 | @property (nonatomic, readonly, copy, nullable) NSDictionary *attributes; 94 | @property (nonatomic, readonly, copy, nullable) NSDictionary *childNodes; 95 | @property (nonatomic, readonly, copy, nullable) NSArray *comments; 96 | @property (nonatomic, readonly, copy, nullable) NSString *nodeName; 97 | @property (nonatomic, readonly, copy, nullable) NSString *innerText; 98 | @property (nonatomic, readonly, copy) NSString *innerXML; 99 | @property (nonatomic, readonly, copy) NSString *XMLString; 100 | 101 | - (nullable NSArray *)arrayValueForKeyPath:(NSString *)keyPath; 102 | - (nullable NSString *)stringValueForKeyPath:(NSString *)keyPath; 103 | - (nullable NSDictionary *)dictionaryValueForKeyPath:(NSString *)keyPath; 104 | 105 | @end 106 | 107 | 108 | @interface NSString (XMLDictionary) 109 | 110 | @property (nonatomic, readonly, copy) NSString *XMLEncodedString; 111 | 112 | @end 113 | 114 | 115 | NS_ASSUME_NONNULL_END 116 | 117 | 118 | #pragma GCC diagnostic pop 119 | -------------------------------------------------------------------------------- /Demo/Pods/XMLDictionary/XMLDictionary/XMLDictionary.m: -------------------------------------------------------------------------------- 1 | // 2 | // XMLDictionary.m 3 | // 4 | // Version 1.4.1 5 | // 6 | // Created by Nick Lockwood on 15/11/2010. 7 | // Copyright 2010 Charcoal Design. All rights reserved. 8 | // 9 | // Get the latest version of XMLDictionary from here: 10 | // 11 | // https://github.com/nicklockwood/XMLDictionary 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #import "XMLDictionary.h" 33 | 34 | 35 | #pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis" 36 | #pragma GCC diagnostic ignored "-Wpartial-availability" 37 | #pragma GCC diagnostic ignored "-Wdirect-ivar-access" 38 | #pragma GCC diagnostic ignored "-Wformat-non-iso" 39 | #pragma GCC diagnostic ignored "-Wgnu" 40 | 41 | 42 | #import 43 | #if !__has_feature(objc_arc) 44 | #error This class requires automatic reference counting 45 | #endif 46 | 47 | 48 | @interface XMLDictionaryParser () 49 | 50 | @property (nonatomic, strong) NSMutableDictionary *root; 51 | @property (nonatomic, strong) NSMutableArray *stack; 52 | @property (nonatomic, strong) NSMutableString *text; 53 | 54 | @end 55 | 56 | 57 | @implementation XMLDictionaryParser 58 | 59 | + (XMLDictionaryParser *)sharedInstance 60 | { 61 | static dispatch_once_t once; 62 | static XMLDictionaryParser *sharedInstance; 63 | dispatch_once(&once, ^{ 64 | 65 | sharedInstance = [[XMLDictionaryParser alloc] init]; 66 | }); 67 | return sharedInstance; 68 | } 69 | 70 | - (instancetype)init 71 | { 72 | if ((self = [super init])) 73 | { 74 | _collapseTextNodes = YES; 75 | _stripEmptyNodes = YES; 76 | _trimWhiteSpace = YES; 77 | _alwaysUseArrays = NO; 78 | _preserveComments = NO; 79 | _wrapRootNode = NO; 80 | } 81 | return self; 82 | } 83 | 84 | - (id)copyWithZone:(NSZone *)zone 85 | { 86 | XMLDictionaryParser *copy = [[[self class] allocWithZone:zone] init]; 87 | copy.collapseTextNodes = _collapseTextNodes; 88 | copy.stripEmptyNodes = _stripEmptyNodes; 89 | copy.trimWhiteSpace = _trimWhiteSpace; 90 | copy.alwaysUseArrays = _alwaysUseArrays; 91 | copy.preserveComments = _preserveComments; 92 | copy.attributesMode = _attributesMode; 93 | copy.nodeNameMode = _nodeNameMode; 94 | copy.wrapRootNode = _wrapRootNode; 95 | return copy; 96 | } 97 | 98 | - (NSDictionary *)dictionaryWithParser:(NSXMLParser *)parser 99 | { 100 | parser.delegate = self; 101 | [parser parse]; 102 | id result = _root; 103 | _root = nil; 104 | _stack = nil; 105 | _text = nil; 106 | return result; 107 | } 108 | 109 | - (NSDictionary *)dictionaryWithData:(NSData *)data 110 | { 111 | NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; 112 | return [self dictionaryWithParser:parser]; 113 | } 114 | 115 | - (NSDictionary *)dictionaryWithString:(NSString *)string 116 | { 117 | NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; 118 | return [self dictionaryWithData:data]; 119 | } 120 | 121 | - (NSDictionary *)dictionaryWithFile:(NSString *)path 122 | { 123 | NSData *data = [NSData dataWithContentsOfFile:path]; 124 | return [self dictionaryWithData:data]; 125 | } 126 | 127 | + (NSString *)XMLStringForNode:(id)node withNodeName:(NSString *)nodeName 128 | { 129 | if ([node isKindOfClass:[NSArray class]]) 130 | { 131 | NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:[node count]]; 132 | for (id individualNode in node) 133 | { 134 | [nodes addObject:[self XMLStringForNode:individualNode withNodeName:nodeName]]; 135 | } 136 | return [nodes componentsJoinedByString:@"\n"]; 137 | } 138 | else if ([node isKindOfClass:[NSDictionary class]]) 139 | { 140 | NSDictionary *attributes = [(NSDictionary *)node attributes]; 141 | NSMutableString *attributeString = [NSMutableString string]; 142 | [attributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, __unused BOOL *stop) { 143 | [attributeString appendFormat:@" %@=\"%@\"", key.description.XMLEncodedString, value.description.XMLEncodedString]; 144 | }]; 145 | 146 | NSString *innerXML = [node innerXML]; 147 | if (innerXML.length) 148 | { 149 | return [NSString stringWithFormat:@"<%1$@%2$@>%3$@", nodeName, attributeString, innerXML]; 150 | } 151 | else 152 | { 153 | return [NSString stringWithFormat:@"<%@%@/>", nodeName, attributeString]; 154 | } 155 | } 156 | else 157 | { 158 | return [NSString stringWithFormat:@"<%1$@>%2$@", nodeName, [node description].XMLEncodedString]; 159 | } 160 | } 161 | 162 | - (void)endText 163 | { 164 | if (_trimWhiteSpace) 165 | { 166 | _text = [[_text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy]; 167 | } 168 | if (_text.length) 169 | { 170 | NSMutableDictionary *top = _stack.lastObject; 171 | id existing = top[XMLDictionaryTextKey]; 172 | if ([existing isKindOfClass:[NSArray class]]) 173 | { 174 | [existing addObject:_text]; 175 | } 176 | else if (existing) 177 | { 178 | top[XMLDictionaryTextKey] = [@[existing, _text] mutableCopy]; 179 | } 180 | else 181 | { 182 | top[XMLDictionaryTextKey] = _text; 183 | } 184 | } 185 | _text = nil; 186 | } 187 | 188 | - (void)addText:(NSString *)text 189 | { 190 | if (!_text) 191 | { 192 | _text = [NSMutableString stringWithString:text]; 193 | } 194 | else 195 | { 196 | [_text appendString:text]; 197 | } 198 | } 199 | 200 | - (void)parser:(__unused NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName attributes:(NSDictionary *)attributeDict 201 | { 202 | [self endText]; 203 | 204 | NSMutableDictionary *node = [NSMutableDictionary dictionary]; 205 | switch (_nodeNameMode) 206 | { 207 | case XMLDictionaryNodeNameModeRootOnly: 208 | { 209 | if (!_root) 210 | { 211 | node[XMLDictionaryNodeNameKey] = elementName; 212 | } 213 | break; 214 | } 215 | case XMLDictionaryNodeNameModeAlways: 216 | { 217 | node[XMLDictionaryNodeNameKey] = elementName; 218 | break; 219 | } 220 | case XMLDictionaryNodeNameModeNever: 221 | { 222 | break; 223 | } 224 | } 225 | 226 | if (attributeDict.count) 227 | { 228 | switch (_attributesMode) 229 | { 230 | case XMLDictionaryAttributesModePrefixed: 231 | { 232 | for (NSString *key in attributeDict) 233 | { 234 | node[[XMLDictionaryAttributePrefix stringByAppendingString:key]] = attributeDict[key]; 235 | } 236 | break; 237 | } 238 | case XMLDictionaryAttributesModeDictionary: 239 | { 240 | node[XMLDictionaryAttributesKey] = attributeDict; 241 | break; 242 | } 243 | case XMLDictionaryAttributesModeUnprefixed: 244 | { 245 | [node addEntriesFromDictionary:attributeDict]; 246 | break; 247 | } 248 | case XMLDictionaryAttributesModeDiscard: 249 | { 250 | break; 251 | } 252 | } 253 | } 254 | 255 | if (!_root) 256 | { 257 | _root = node; 258 | _stack = [NSMutableArray arrayWithObject:node]; 259 | if (_wrapRootNode) 260 | { 261 | _root = [NSMutableDictionary dictionaryWithObject:_root forKey:elementName]; 262 | [_stack insertObject:_root atIndex:0]; 263 | } 264 | } 265 | else 266 | { 267 | NSMutableDictionary *top = _stack.lastObject; 268 | id existing = top[elementName]; 269 | if ([existing isKindOfClass:[NSArray class]]) 270 | { 271 | [(NSMutableArray *)existing addObject:node]; 272 | } 273 | else if (existing) 274 | { 275 | top[elementName] = [@[existing, node] mutableCopy]; 276 | } 277 | else if (_alwaysUseArrays) 278 | { 279 | top[elementName] = [NSMutableArray arrayWithObject:node]; 280 | } 281 | else 282 | { 283 | top[elementName] = node; 284 | } 285 | [_stack addObject:node]; 286 | } 287 | } 288 | 289 | - (NSString *)nameForNode:(NSDictionary *)node inDictionary:(NSDictionary *)dict 290 | { 291 | if (node.nodeName) 292 | { 293 | return node.nodeName; 294 | } 295 | else 296 | { 297 | for (NSString *name in dict) 298 | { 299 | id object = dict[name]; 300 | if (object == node) 301 | { 302 | return name; 303 | } 304 | else if ([object isKindOfClass:[NSArray class]] && [(NSArray *)object containsObject:node]) 305 | { 306 | return name; 307 | } 308 | } 309 | } 310 | return nil; 311 | } 312 | 313 | - (void)parser:(__unused NSXMLParser *)parser didEndElement:(__unused NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName 314 | { 315 | [self endText]; 316 | 317 | NSMutableDictionary *top = _stack.lastObject; 318 | [_stack removeLastObject]; 319 | 320 | if (!top.attributes && !top.childNodes && !top.comments) 321 | { 322 | NSMutableDictionary *newTop = _stack.lastObject; 323 | NSString *nodeName = [self nameForNode:top inDictionary:newTop]; 324 | if (nodeName) 325 | { 326 | id parentNode = newTop[nodeName]; 327 | NSString *innerText = top.innerText; 328 | if (innerText && _collapseTextNodes) 329 | { 330 | if ([parentNode isKindOfClass:[NSArray class]]) 331 | { 332 | parentNode[[parentNode count] - 1] = innerText; 333 | } 334 | else 335 | { 336 | newTop[nodeName] = innerText; 337 | } 338 | } 339 | else if (!innerText) 340 | { 341 | if (_stripEmptyNodes) 342 | { 343 | if ([parentNode isKindOfClass:[NSArray class]]) 344 | { 345 | [(NSMutableArray *)parentNode removeLastObject]; 346 | } 347 | else 348 | { 349 | [newTop removeObjectForKey:nodeName]; 350 | } 351 | } 352 | else if (!_collapseTextNodes) 353 | { 354 | top[XMLDictionaryTextKey] = @""; 355 | } 356 | } 357 | } 358 | } 359 | } 360 | 361 | - (void)parser:(__unused NSXMLParser *)parser foundCharacters:(NSString *)string 362 | { 363 | [self addText:string]; 364 | } 365 | 366 | - (void)parser:(__unused NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock 367 | { 368 | [self addText:[[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding]]; 369 | } 370 | 371 | - (void)parser:(__unused NSXMLParser *)parser foundComment:(NSString *)comment 372 | { 373 | if (_preserveComments) 374 | { 375 | NSMutableDictionary *top = _stack.lastObject; 376 | NSMutableArray *comments = top[XMLDictionaryCommentsKey]; 377 | if (!comments) 378 | { 379 | comments = [@[comment] mutableCopy]; 380 | top[XMLDictionaryCommentsKey] = comments; 381 | } 382 | else 383 | { 384 | [comments addObject:comment]; 385 | } 386 | } 387 | } 388 | 389 | @end 390 | 391 | 392 | @implementation NSDictionary(XMLDictionary) 393 | 394 | + (NSDictionary *)dictionaryWithXMLParser:(NSXMLParser *)parser 395 | { 396 | return [[[XMLDictionaryParser sharedInstance] copy] dictionaryWithParser:parser]; 397 | } 398 | 399 | + (NSDictionary *)dictionaryWithXMLData:(NSData *)data 400 | { 401 | return [[[XMLDictionaryParser sharedInstance] copy] dictionaryWithData:data]; 402 | } 403 | 404 | + (NSDictionary *)dictionaryWithXMLString:(NSString *)string 405 | { 406 | return [[[XMLDictionaryParser sharedInstance] copy] dictionaryWithString:string]; 407 | } 408 | 409 | + (NSDictionary *)dictionaryWithXMLFile:(NSString *)path 410 | { 411 | return [[[XMLDictionaryParser sharedInstance] copy] dictionaryWithFile:path]; 412 | } 413 | 414 | - (nullable NSDictionary *)attributes 415 | { 416 | NSDictionary *attributes = self[XMLDictionaryAttributesKey]; 417 | if (attributes) 418 | { 419 | return attributes.count? attributes: nil; 420 | } 421 | else 422 | { 423 | NSMutableDictionary *filteredDict = [NSMutableDictionary dictionaryWithDictionary:self]; 424 | [filteredDict removeObjectsForKeys:@[XMLDictionaryCommentsKey, XMLDictionaryTextKey, XMLDictionaryNodeNameKey]]; 425 | for (NSString *key in filteredDict.allKeys) 426 | { 427 | [filteredDict removeObjectForKey:key]; 428 | if ([key hasPrefix:XMLDictionaryAttributePrefix]) 429 | { 430 | filteredDict[[key substringFromIndex:XMLDictionaryAttributePrefix.length]] = self[key]; 431 | } 432 | } 433 | return filteredDict.count? filteredDict: nil; 434 | } 435 | return nil; 436 | } 437 | 438 | - (nullable NSDictionary *)childNodes 439 | { 440 | NSMutableDictionary *filteredDict = [self mutableCopy]; 441 | [filteredDict removeObjectsForKeys:@[XMLDictionaryAttributesKey, XMLDictionaryCommentsKey, XMLDictionaryTextKey, XMLDictionaryNodeNameKey]]; 442 | for (NSString *key in filteredDict.allKeys) 443 | { 444 | if ([key hasPrefix:XMLDictionaryAttributePrefix]) 445 | { 446 | [filteredDict removeObjectForKey:key]; 447 | } 448 | } 449 | return filteredDict.count? filteredDict: nil; 450 | } 451 | 452 | - (nullable NSArray *)comments 453 | { 454 | return self[XMLDictionaryCommentsKey]; 455 | } 456 | 457 | - (nullable NSString *)nodeName 458 | { 459 | return self[XMLDictionaryNodeNameKey]; 460 | } 461 | 462 | - (id)innerText 463 | { 464 | id text = self[XMLDictionaryTextKey]; 465 | if ([text isKindOfClass:[NSArray class]]) 466 | { 467 | return [text componentsJoinedByString:@"\n"]; 468 | } 469 | else 470 | { 471 | return text; 472 | } 473 | } 474 | 475 | - (NSString *)innerXML 476 | { 477 | NSMutableArray *nodes = [NSMutableArray array]; 478 | 479 | for (NSString *comment in [self comments]) 480 | { 481 | [nodes addObject:[NSString stringWithFormat:@"", [comment XMLEncodedString]]]; 482 | } 483 | 484 | NSDictionary *childNodes = [self childNodes]; 485 | for (NSString *key in childNodes) 486 | { 487 | [nodes addObject:[XMLDictionaryParser XMLStringForNode:childNodes[key] withNodeName:key]]; 488 | } 489 | 490 | NSString *text = [self innerText]; 491 | if (text) 492 | { 493 | [nodes addObject:[text XMLEncodedString]]; 494 | } 495 | 496 | return [nodes componentsJoinedByString:@"\n"]; 497 | } 498 | 499 | - (NSString *)XMLString 500 | { 501 | if (self.count == 1 && ![self nodeName]) 502 | { 503 | //ignore outermost dictionary 504 | return [self innerXML]; 505 | } 506 | else 507 | { 508 | return [XMLDictionaryParser XMLStringForNode:self withNodeName:[self nodeName] ?: @"root"]; 509 | } 510 | } 511 | 512 | - (nullable NSArray *)arrayValueForKeyPath:(NSString *)keyPath 513 | { 514 | id value = [self valueForKeyPath:keyPath]; 515 | if (value && ![value isKindOfClass:[NSArray class]]) 516 | { 517 | return @[value]; 518 | } 519 | return value; 520 | } 521 | 522 | - (nullable NSString *)stringValueForKeyPath:(NSString *)keyPath 523 | { 524 | id value = [self valueForKeyPath:keyPath]; 525 | if ([value isKindOfClass:[NSArray class]]) 526 | { 527 | value = ((NSArray *)value).firstObject; 528 | } 529 | if ([value isKindOfClass:[NSDictionary class]]) 530 | { 531 | return [(NSDictionary *)value innerText]; 532 | } 533 | return value; 534 | } 535 | 536 | - (nullable NSDictionary *)dictionaryValueForKeyPath:(NSString *)keyPath 537 | { 538 | id value = [self valueForKeyPath:keyPath]; 539 | if ([value isKindOfClass:[NSArray class]]) 540 | { 541 | value = [value count]? value[0]: nil; 542 | } 543 | if ([value isKindOfClass:[NSString class]]) 544 | { 545 | return @{XMLDictionaryTextKey: value}; 546 | } 547 | return value; 548 | } 549 | 550 | @end 551 | 552 | 553 | @implementation NSString (XMLDictionary) 554 | 555 | - (NSString *)XMLEncodedString 556 | { 557 | return [[[[[self stringByReplacingOccurrencesOfString:@"&" withString:@"&"] 558 | stringByReplacingOccurrencesOfString:@"<" withString:@"<"] 559 | stringByReplacingOccurrencesOfString:@">" withString:@">"] 560 | stringByReplacingOccurrencesOfString:@"\"" withString:@"""] 561 | stringByReplacingOccurrencesOfString:@"\'" withString:@"'"]; 562 | } 563 | 564 | @end 565 | -------------------------------------------------------------------------------- /IMYWebLoader.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'IMYWebLoader' 3 | s.version = '0.1' 4 | s.description = 'Web resources loading by Prefetch/Cache/Hook, Support UIWebView/WKWebView ... Orz' 5 | s.license = 'MIT' 6 | s.summary = '支持对 UIWebView/WKWebView 的资源, 进行 预加载、缓存、拦截 等操作 ... Orz' 7 | s.homepage = 'https://github.com/li6185377/IMYWebLoader' 8 | s.authors = { 'ljh' => '137249466@qq.com' } 9 | s.source = { :git => 'https://github.com/li6185377/IMYWebLoader.git', :tag => '0.1' } 10 | s.requires_arc = true 11 | s.ios.deployment_target = '7.0' 12 | 13 | s.source_files = 'Sources/Private/**/*.{h,m}', 'Sources/Public/**/*.{h,m}' 14 | s.resources = 'Sources/Resources/**/*.{js}' 15 | 16 | s.weak_frameworks = 'JavaScriptCore', 'WebKit' 17 | 18 | s.dependency 'XMLDictionary' 19 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 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 | # IMYWebLoader 2 | 3 | 支持对 UIWebView/WKWebView 的资源, 进行 预加载、缓存、拦截 等操作 ... Orz 4 | 5 | ### 屁话 6 | UIWebView 缓存控制这块本身很弱,几乎无法用系统API 来完成我们的扩展,于是大家找到了 NSURLProtcol 来进行缓存或者拦截的操作, 总体来说还是满足了大家的需求 7 | 8 | iOS8 除了WKWebView 之后更是连 NSURLProtocol 都不支持了,还好 WebKit 是开源的,大家通过搜索发现了 通过注册 CustomScheme,来拦截相应scheme的请求,于是大家进行了 http、https 的拦截 9 | 10 | 但是由于 WebKit 源码的限制,request body 永远都没法互相传递, 11 | 12 | 通过大量尝试,使用 fishhook,hook c++虚函数,均已失败告终 最终放弃了在 Native 层的拦截 13 | 14 | 突然有一天... 15 | 16 | 突然想到 前端的 post body 请求,大都都是通过 XMLHttpRequest 来请求的,为何我不能在 JS 层去 hook 呢? (还在使用 from 表单的,就让它去屎吧) 17 | 18 | hook-ajax 可能支持的没那么全,遇到问题可以帮忙改改,然后提个 pull request (一般的AJAX请求 肯定都是没问题的啦) 19 | 20 | 懒得写 readme ... 了 21 | 22 | ### 功能 23 | 24 | - 支持对 UIWebView/WKWebView 的数据缓存 25 | - 支持 WKWebView 带 request body 的 AJAX 请求 26 | - 支持断网后阅读 27 | - 支持预加载方法,直接解析 html,提前下载其中的静态资源 28 | - 支持功能实现替换,没写死,都使用协议编程 29 | - ... 30 | 31 | ### 参考 32 | 33 | - [WKWebView 那些坑](https://mp.weixin.qq.com/s/rhYKLIbXOsUJC_n6dt9UfA)
34 | - [WebKit::WKCustomProtocolLoader](https://github.com/WebKit/webkit/blob/11c7bf06fa29f362a5ebd620bca4b703dc7f733a/Source/WebKit2/UIProcess/Cocoa/LegacyCustomProtocolManagerClient.mm#L51) 35 | - [让 WKWebView 支持 NSURLProtocol](https://blog.yeatse.com/2016/10/26/support-nsurlprotocol-in-wkwebview) 36 | - [Hook-Ajax](https://github.com/wendux/Ajax-hook) 37 | -------------------------------------------------------------------------------- /Sources/Private/Handler/IMYWebCacheHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebCacheHandlerDefaultImpl.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import 10 | #import "IMYWebCacheHandler.h" 11 | 12 | @interface IMYWebCacheHandlerDefaultImpl : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Sources/Private/Handler/IMYWebCacheHandlerDefaultImpl.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebCacheHandlerDefaultImpl.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import "IMYWebCacheHandlerDefaultImpl.h" 10 | #import 11 | 12 | @interface IMYWebCacheHandlerDefaultImpl () 13 | 14 | @property (nonatomic, strong) NSCache *memCache; 15 | @property (nonatomic, strong) NSString *dirPath; 16 | 17 | @end 18 | 19 | @implementation IMYWebCacheHandlerDefaultImpl 20 | 21 | - (instancetype)init 22 | { 23 | self = [super init]; 24 | if (self) { 25 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 26 | 27 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 28 | self.dirPath = [paths.firstObject stringByAppendingPathComponent:@"imy.web.default.cache"]; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)applicationDidReceiveMemoryWarning 34 | { 35 | [self.memCache removeAllObjects]; 36 | } 37 | 38 | - (void)dealloc 39 | { 40 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 41 | } 42 | 43 | - (NSString *)cacheKeyForRequest:(NSURLRequest *)request 44 | { 45 | return request.URL.absoluteString; 46 | } 47 | 48 | - (NSString *)fileNameForKey:(NSString *)key 49 | { 50 | if (!key) { 51 | return @""; 52 | } 53 | 54 | NSData *data = [key dataUsingEncoding:NSUTF8StringEncoding]; 55 | 56 | // CC_SHA1_DIGEST_LENGTH : 20 57 | uint8_t digest[20]; 58 | 59 | CC_SHA1(data.bytes, (unsigned int)data.length, digest); 60 | 61 | NSString *sha1 = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 62 | digest[0], digest[1], digest[2], digest[3], digest[4], 63 | digest[5], digest[6], digest[7], digest[8], digest[9], 64 | digest[10], digest[11], digest[12], digest[13], digest[14], 65 | digest[15], digest[16], digest[17], digest[18], digest[19]]; 66 | 67 | return sha1; 68 | } 69 | 70 | - (NSString *)filePathForKey:(NSString *)key 71 | { 72 | NSString *fileName = [self fileNameForKey:key]; 73 | NSFileManager *fileManager = [NSFileManager defaultManager]; 74 | if (![fileManager fileExistsAtPath:_dirPath]) { 75 | [fileManager createDirectoryAtPath:_dirPath withIntermediateDirectories:YES attributes:nil error:NULL]; 76 | } 77 | NSString *filePath = [_dirPath stringByAppendingPathComponent:fileName]; 78 | return filePath; 79 | } 80 | 81 | - (void)setData:(IMYWebData *)data forKey:(NSString *)key 82 | { 83 | [self.memCache setObject:data forKey:key]; 84 | NSString *filePath = [self filePathForKey:key]; 85 | NSData *fileData = nil; 86 | if (data) { 87 | fileData = [NSKeyedArchiver archivedDataWithRootObject:data]; 88 | } 89 | if (fileData) { 90 | [fileData writeToFile:filePath atomically:YES]; 91 | } else { 92 | [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; 93 | } 94 | } 95 | 96 | - (IMYWebData *)dataForKey:(NSString *)key 97 | { 98 | IMYWebData *data = [self.memCache objectForKey:key]; 99 | if (!data) { 100 | NSString *filePath = [self filePathForKey:key]; 101 | NSData *fileData = [NSData dataWithContentsOfFile:filePath]; 102 | if (fileData) { 103 | data = [NSKeyedUnarchiver unarchiveObjectWithData:fileData]; 104 | } 105 | } 106 | return data; 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /Sources/Private/Handler/IMYWebNetworkHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebNetworkHandlerDefaultImpl.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import 10 | #import "IMYWebNetworkHandler.h" 11 | 12 | @interface IMYWebNetworkHandlerDefaultImpl : NSObject 13 | 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Sources/Private/Handler/IMYWebNetworkHandlerDefaultImpl.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebNetworkHandlerDefaultImpl.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import "IMYWebNetworkHandlerDefaultImpl.h" 10 | 11 | @implementation IMYWebNetworkHandlerDefaultImpl 12 | 13 | - (NSURLRequest *)requestWithString:(NSString *)urlString 14 | { 15 | NSURL *url = [NSURL URLWithString:urlString]; 16 | if (!url) { 17 | return nil; 18 | } 19 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30]; 20 | request.HTTPMethod = @"GET"; 21 | return request; 22 | } 23 | 24 | - (id)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * _Nullable, NSURLResponse * _Nullable, NSError * _Nullable))completionHandler 25 | { 26 | NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:completionHandler]; 27 | [task resume]; 28 | return (id)task; 29 | } 30 | 31 | + (void)networkRequestThreadEntryPoint 32 | { 33 | @autoreleasepool { 34 | [[NSThread currentThread] setName:@"IMYWebNetworkThread"]; 35 | 36 | NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; 37 | [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; 38 | [runLoop run]; 39 | } 40 | } 41 | 42 | - (NSThread *)networkRequestThread 43 | { 44 | static NSThread *_networkRequestThread = nil; 45 | static dispatch_once_t oncePredicate; 46 | dispatch_once(&oncePredicate, ^{ 47 | _networkRequestThread = [[NSThread alloc] initWithTarget:[IMYWebNetworkHandlerDefaultImpl class] selector:@selector(networkRequestThreadEntryPoint) object:nil]; 48 | [_networkRequestThread start]; 49 | }); 50 | 51 | return _networkRequestThread; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /Sources/Private/Handler/IMYWebPrefetchHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebPrefetchHandlerDefaultImpl.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import 10 | #import "IMYWebPrefetchHandler.h" 11 | 12 | @interface IMYWebPrefetchHandlerDefaultImpl : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Sources/Private/Handler/IMYWebPrefetchHandlerDefaultImpl.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebPrefetchHandlerDefaultImpl.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import "IMYWebPrefetchHandlerDefaultImpl.h" 10 | #import "XMLDictionary.h" 11 | #import "IMYWebLoader.h" 12 | #import "IMYWebUtils.h" 13 | 14 | @interface IMYWebPrefetchOperater : NSObject 15 | 16 | @property (nonatomic, copy) NSString *webUrl; 17 | @property (nonatomic, assign, getter=isComplated) BOOL complated; 18 | 19 | @property (nonatomic, weak) id operation; 20 | @property (nonatomic, strong) NSMutableDictionary *detailMaps; 21 | 22 | - (void)startLoading:(NSURLRequest *)request cacheKey:(NSString *)cacheKey; 23 | 24 | @end 25 | 26 | @interface IMYWebPrefetchHandlerDefaultImpl () 27 | @property (nonatomic, strong) NSMutableDictionary *webUrls; 28 | @end 29 | 30 | @implementation IMYWebPrefetchHandlerDefaultImpl 31 | 32 | - (instancetype)init 33 | { 34 | self = [super init]; 35 | if (self) { 36 | self.webUrls = [NSMutableDictionary dictionary]; 37 | } 38 | return self; 39 | } 40 | 41 | - (id)prefetchWebUrl:(NSString *)webUrl 42 | { 43 | if (!webUrl.length) { 44 | return nil; 45 | } 46 | id prefetcher = nil; 47 | @synchronized (self) { 48 | prefetcher = self.webUrls[webUrl]; 49 | //已存在,一次运行周期内 只预加载一次 50 | if (prefetcher) { 51 | return prefetcher; 52 | } 53 | prefetcher = [self lockPrefetchHTMLDetail:webUrl]; 54 | } 55 | return prefetcher; 56 | } 57 | 58 | - (id)lockPrefetchHTMLDetail:(NSString *)urlString 59 | { 60 | NSURLRequest *request = [[IMYWebLoader defaultNetworkHandler] requestWithString:urlString]; 61 | if (!request) { 62 | return nil; 63 | } 64 | 65 | IMYWebPrefetchOperater *operater = [IMYWebPrefetchOperater new]; 66 | operater.webUrl = urlString; 67 | 68 | NSString *cacheKey = [[IMYWebLoader defaultCacheHandler] cacheKeyForRequest:request]; 69 | IMYWebData *data = [[IMYWebLoader defaultCacheHandler] dataForKey:cacheKey]; 70 | ///已有缓存 71 | if (data) { 72 | operater.complated = YES; 73 | return operater; 74 | } 75 | 76 | [operater startLoading:request cacheKey:cacheKey]; 77 | 78 | return operater; 79 | } 80 | 81 | - (void)cancelAllPrefetcherLoading 82 | { 83 | @synchronized (self) { 84 | [self.webUrls enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, IMYWebPrefetchOperater * _Nonnull obj, BOOL * _Nonnull stop) { 85 | [obj cancel]; 86 | }]; 87 | } 88 | } 89 | 90 | - (void)removePrefetcherForWebUrl:(NSString *)webUrl 91 | { 92 | @synchronized (self) { 93 | [self.webUrls removeObjectForKey:webUrl]; 94 | } 95 | } 96 | 97 | @end 98 | 99 | @implementation IMYWebPrefetchOperater 100 | 101 | - (instancetype)init 102 | { 103 | self = [super init]; 104 | if (self) { 105 | self.detailMaps = [NSMutableDictionary dictionary]; 106 | } 107 | return self; 108 | } 109 | 110 | - (void)startLoading:(NSURLRequest *)request cacheKey:(NSString *)cacheKey 111 | { 112 | self.operation = [[IMYWebLoader defaultNetworkHandler] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 113 | if (!data.length || error) { 114 | return; 115 | } 116 | NSInteger statusCode = 0; 117 | if ([response isKindOfClass:[NSHTTPURLResponse class]]) { 118 | statusCode = [(NSHTTPURLResponse *)response statusCode]; 119 | } 120 | if(statusCode < 200 || statusCode >= 300) { 121 | return; 122 | } 123 | 124 | NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 125 | 126 | ///过滤可能的错误,html 文本长度过短,或者 内容包含 404错误字眼等 127 | if (!html.length || [html containsString:@"404错误"] || [html containsString:@" 404 "]) { 128 | return; 129 | } 130 | 131 | IMYWebData *webData = [IMYWebData new]; 132 | webData.data = data; 133 | webData.response = response; 134 | webData.request = request; 135 | 136 | //数据缓存 137 | [[IMYWebLoader defaultCacheHandler] setData:webData forKey:cacheKey]; 138 | 139 | ///标志已完成 140 | self.complated = YES; 141 | 142 | ///加载静态资源 143 | [self prefetchResourcesWithHTML:html baseURL:response.URL]; 144 | }]; 145 | } 146 | 147 | 148 | - (void)prefetchResourcesWithHTML:(NSString *)html baseURL:(NSURL *)baseURL 149 | { 150 | ///替换 换行符,正则没匹配 \n 151 | html = [html stringByReplacingOccurrencesOfString:@"\n" withString:@" "]; 152 | 153 | [self prefetchCSSWithHTML:html baseURL:baseURL]; 154 | [self prefetchScriptWithHTML:html baseURL:baseURL]; 155 | 156 | [self prefetchImagesWithHTML:html baseURL:baseURL]; 157 | } 158 | 159 | /// 没有导太多的 pods,只能手动写了 160 | - (NSArray *)filter:(NSArray *)array block:(BOOL(^)(id element))block { 161 | if (!array || !block) { 162 | return [NSArray array]; 163 | } 164 | NSMutableArray *result = [NSMutableArray array]; 165 | [array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 166 | if (block(obj)) { 167 | [result addObject:obj]; 168 | } 169 | }]; 170 | return result; 171 | } 172 | 173 | - (void)prefetchCSSWithHTML:(NSString *)html baseURL:(NSURL *)baseURL 174 | { 175 | NSArray *urls = [self urlsWithHTML:html regular:[self cssHrefExpression] forKey:@"href" baseURL:baseURL]; 176 | urls = [self filter:urls block:^BOOL(id element) { 177 | return [element containsString:@".css"] || [element containsString:@".ico"]; 178 | }]; 179 | if (urls.count) { 180 | [self prefetchFileWithUrls:urls]; 181 | } 182 | } 183 | 184 | - (void)prefetchScriptWithHTML:(NSString *)html baseURL:(NSURL *)baseURL 185 | { 186 | NSArray *urls = [self urlsWithHTML:html regular:[self scriptSrcExpression] forKey:@"src" baseURL:baseURL]; 187 | urls = [self filter:urls block:^BOOL(id element) { 188 | return [element containsString:@".js"]; 189 | }]; 190 | if (urls.count) { 191 | [self prefetchFileWithUrls:urls]; 192 | } 193 | } 194 | 195 | - (void)prefetchFileWithUrls:(NSArray *)urls 196 | { 197 | @synchronized (self) { 198 | [self lockPrefetchFileWithUrls:urls]; 199 | } 200 | } 201 | 202 | - (void)lockPrefetchFileWithUrls:(NSArray *)urls 203 | { 204 | [urls enumerateObjectsUsingBlock:^(NSString *fileURL, NSUInteger idx, BOOL * _Nonnull stop) { 205 | 206 | IMYWebPrefetchOperater *operater = self.detailMaps[fileURL]; 207 | if (operater) { 208 | return ; 209 | } 210 | 211 | NSURLRequest *request = [[IMYWebLoader defaultNetworkHandler] requestWithString:fileURL]; 212 | if (!request) { 213 | return; 214 | } 215 | 216 | operater = [IMYWebPrefetchOperater new]; 217 | operater.webUrl = fileURL; 218 | self.detailMaps[fileURL] = operater; 219 | 220 | NSString *cacheKey = [[IMYWebLoader defaultCacheHandler] cacheKeyForRequest:request]; 221 | IMYWebData *data = [[IMYWebLoader defaultCacheHandler] dataForKey:cacheKey]; 222 | ///已有缓存 223 | if (data) { 224 | operater.complated = YES; 225 | return; 226 | } 227 | 228 | operater.operation = [[IMYWebLoader defaultNetworkHandler] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 229 | if (!data.length || error) { 230 | return; 231 | } 232 | NSInteger statusCode = 0; 233 | if ([response isKindOfClass:[NSHTTPURLResponse class]]) { 234 | statusCode = [(NSHTTPURLResponse *)response statusCode]; 235 | } 236 | if(statusCode < 200 || statusCode >= 300) { 237 | return; 238 | } 239 | NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 240 | 241 | ///过滤可能的错误,html 文本长度过短,或者 内容包含 404错误字眼等 242 | if ([html containsString:@"404错误"] || [html containsString:@" 404 "]) { 243 | return; 244 | } 245 | 246 | operater.complated = YES; 247 | 248 | IMYWebData *cacheData = [IMYWebData new]; 249 | cacheData.data = data; 250 | cacheData.response = response; 251 | cacheData.request = request; 252 | 253 | [[IMYWebLoader defaultCacheHandler] setData:cacheData forKey:cacheKey]; 254 | }]; 255 | }]; 256 | } 257 | 258 | - (void)prefetchImagesWithHTML:(NSString *)html baseURL:(NSURL *)baseURL 259 | { 260 | NSMutableArray *prefetchImages = [NSMutableArray array]; 261 | 262 | NSArray *imgArray = [self urlsWithHTML:html regular:[self imgSrcExpression] forKey:@"src" baseURL:baseURL]; 263 | if (imgArray.count > 3) { 264 | imgArray = [imgArray subarrayWithRange:NSMakeRange(0, 3)]; 265 | } 266 | if (imgArray.count) { 267 | [prefetchImages addObjectsFromArray:imgArray]; 268 | } 269 | 270 | NSArray *posterArray = [self urlsWithHTML:html regular:[self videoPosterExpression] forKey:@"poster" baseURL:baseURL]; 271 | if (posterArray.count) { 272 | [prefetchImages addObjectsFromArray:posterArray]; 273 | } 274 | [self prefetchFileWithUrls:prefetchImages]; 275 | } 276 | 277 | - (NSArray *)urlsWithHTML:(NSString *)html regular:(NSRegularExpression *)regular forKey:(NSString *)key baseURL:(NSURL *)baseURL 278 | { 279 | if (!html || !regular || !key) { 280 | return nil; 281 | } 282 | NSArray *results = [regular matchesInString:html options:0 range:NSMakeRange(0, html.length)]; 283 | NSMutableArray *urls = [NSMutableArray array]; 284 | [results enumerateObjectsUsingBlock:^(NSTextCheckingResult * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 285 | NSString *substring = [html substringWithRange:obj.range]; 286 | NSDictionary *xmlNode = [NSDictionary dictionaryWithXMLString:substring]; 287 | if (!xmlNode) { 288 | NSCharacterSet *whitespaces = [NSCharacterSet whitespaceCharacterSet]; 289 | NSArray *parts = [substring componentsSeparatedByCharactersInSet:whitespaces]; 290 | NSArray *filteredArray = [self filter:parts block:^BOOL(id element) { 291 | return [element hasPrefix:@"<"] || [element containsString:@"="] || [element hasSuffix:@">"]; 292 | }]; 293 | NSString *nodeString = [filteredArray componentsJoinedByString:@" "]; 294 | xmlNode = [NSDictionary dictionaryWithXMLString:nodeString]; 295 | } 296 | 297 | ///获取 src url 298 | __block NSString *srcURL = nil; 299 | [xmlNode enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull nodeName, NSString * _Nonnull nodeValue, BOOL * _Nonnull stop) { 300 | if ([nodeName hasSuffix:key] && [nodeValue isKindOfClass:[NSString class]] && nodeValue.length > 0) { 301 | srcURL = nodeValue; 302 | *stop = YES; 303 | } 304 | }]; 305 | 306 | srcURL = [IMYWebUtils URLWithString:srcURL baseURL:baseURL].absoluteString; 307 | 308 | if (srcURL) { 309 | [urls addObject:srcURL]; 310 | } 311 | }]; 312 | return urls; 313 | } 314 | 315 | - (NSRegularExpression *)cssHrefExpression 316 | { 317 | return [NSRegularExpression regularExpressionWithPattern:@"" options:0 error:nil]; 318 | } 319 | 320 | - (NSRegularExpression *)scriptSrcExpression 321 | { 322 | return [NSRegularExpression regularExpressionWithPattern:@"" options:0 error:nil]; 323 | } 324 | 325 | - (NSRegularExpression *)imgSrcExpression 326 | { 327 | return [NSRegularExpression regularExpressionWithPattern:@"" options:0 error:nil]; 328 | } 329 | 330 | - (NSRegularExpression *)videoPosterExpression 331 | { 332 | return [NSRegularExpression regularExpressionWithPattern:@"" options:0 error:nil]; 333 | } 334 | 335 | - (void)cancel 336 | { 337 | @synchronized (self) { 338 | if (self.operation) { 339 | [self.operation cancel]; 340 | self.operation = nil; 341 | } 342 | [self.detailMaps enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, IMYWebPrefetchOperater * _Nonnull obj, BOOL * _Nonnull stop) { 343 | [obj cancel]; 344 | }]; 345 | } 346 | } 347 | @end 348 | -------------------------------------------------------------------------------- /Sources/Private/HookAjax/IMYWebAjaxHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebAjaxHandlerDefaultImpl.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // 7 | // 8 | 9 | #import 10 | #import "IMYWebAjaxHandler.h" 11 | 12 | @interface IMYWebAjaxHandlerDefaultImpl : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Sources/Private/HookAjax/IMYWebAjaxHandlerDefaultImpl.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebAjaxHandlerDefaultImpl.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // 7 | // 8 | 9 | #import "IMYWebAjaxHandlerDefaultImpl.h" 10 | #import "IMYWebUtils.h" 11 | #import "IMYWebLoader.h" 12 | 13 | @implementation IMYWebAjaxHandlerDefaultImpl 14 | 15 | - (void)startWithMethod:(NSString *)method 16 | url:(NSString *)urlString 17 | baseURL:(NSURL *)baseURL 18 | headers:(NSDictionary *)headers 19 | body:(id)body 20 | completedBlock:(void (^)(NSInteger, NSDictionary * _Nullable, NSString * _Nullable))completedBlock 21 | { 22 | NSURL *URL = [IMYWebUtils URLWithString:urlString baseURL:baseURL]; 23 | 24 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30]; 25 | request.HTTPMethod = method.uppercaseString; 26 | if ([body isKindOfClass:[NSString class]]) { 27 | request.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding]; 28 | } else if ([body isKindOfClass:[NSData class]]) { 29 | request.HTTPBody = body; 30 | } else if ([NSJSONSerialization isValidJSONObject:body]) { 31 | NSError *err = nil; 32 | request.HTTPBody = [NSJSONSerialization dataWithJSONObject:body options:0 error:&err]; 33 | } 34 | [request setAllHTTPHeaderFields:headers]; 35 | 36 | [[IMYWebLoader defaultNetworkHandler] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 37 | NSHTTPURLResponse *httpResponse = nil; 38 | if ([response isKindOfClass:[NSHTTPURLResponse class]]) { 39 | httpResponse = (id)response; 40 | } 41 | NSDictionary *allHeaderFields = httpResponse.allHeaderFields; 42 | NSString *responseString = nil; 43 | if (data.length > 0) { 44 | responseString = [self responseStringWithData:data charset:allHeaderFields[@"Content-Type"]]; 45 | } 46 | if (completedBlock) { 47 | completedBlock(httpResponse.statusCode, allHeaderFields, responseString); 48 | } 49 | }]; 50 | } 51 | 52 | - (NSString *)responseStringWithData:(NSData *)data charset:(NSString *)charset 53 | { 54 | NSStringEncoding stringEncoding = NSUTF8StringEncoding; 55 | /// 对一些国内常见编码进行支持 56 | charset = charset.lowercaseString; 57 | if ([charset containsString:@"gb2312"]) { 58 | stringEncoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); 59 | } 60 | NSString *responseString = [[NSString alloc] initWithData:data encoding:stringEncoding]; 61 | return responseString; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Sources/Private/HookAjax/WKUserContentController+IMYHookAjax.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKUserContentController+IMYHookAjax.m 3 | // IMYViewKit 4 | // 5 | // Created by ljh on 2017/3/24. 6 | // Copyright © 2017年 IMY. All rights reserved. 7 | // 8 | 9 | #import "WKUserContentController+IMYHookAjax.h" 10 | #import 11 | #import "IMYWebUtils.h" 12 | #import "IMYWebLoader.h" 13 | 14 | @interface _IMYWKHookAjaxHandler : NSObject 15 | @property (nonatomic, weak) WKWebView *webView; 16 | @end 17 | 18 | @implementation _IMYWKHookAjaxHandler 19 | 20 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message 21 | { 22 | self.webView = message.webView; 23 | [self requestWithBody:message.body]; 24 | } 25 | 26 | - (void)requestWithBody:(NSDictionary *)body 27 | { 28 | id requestID = body[@"id"]; 29 | NSString *method = body[@"method"]; 30 | id requestData = body[@"data"]; 31 | NSDictionary *requestHeaders = body[@"headers"]; 32 | NSString *urlString = body[@"url"]; 33 | 34 | [[IMYWebLoader defaultAjaxHandler] startWithMethod:method 35 | url:urlString 36 | baseURL:self.webView.URL 37 | headers:requestHeaders 38 | body:requestData 39 | completedBlock:^(NSInteger httpCode, NSDictionary * _Nullable headers, NSString * _Nullable data) { 40 | [self requestCallback:requestID httpCode:httpCode headers:headers data:data]; 41 | }]; 42 | } 43 | 44 | - (void)requestCallback:(id)requestId httpCode:(NSInteger)httpCode headers:(NSDictionary *)headers data:(NSString *)data 45 | { 46 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 47 | dict[@"status"] = @(httpCode); 48 | dict[@"headers"] = headers; 49 | if (data.length > 0) { 50 | dict[@"data"] = data; 51 | } 52 | NSString *jsonString = nil; 53 | NSError *err = nil; 54 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:&err]; 55 | if (jsonData.length > 0) { 56 | jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 57 | } 58 | NSString *jsScript = [NSString stringWithFormat:@"window.imy_realxhr_callback(%@, %@);", requestId, jsonString?:@"{}"]; 59 | dispatch_async(dispatch_get_main_queue(), ^{ 60 | [self.webView evaluateJavaScript:jsScript completionHandler:^(id result, NSError *error) { 61 | 62 | }]; 63 | }); 64 | 65 | } 66 | 67 | @end 68 | 69 | @implementation WKUserContentController (IMYHookAjax) 70 | 71 | static const void *IMYHookAjaxKey = &IMYHookAjaxKey; 72 | - (void)imy_uninstallHookAjax 73 | { 74 | [self removeScriptMessageHandlerForName:@"IMYXHR"]; 75 | objc_setAssociatedObject(self, IMYHookAjaxKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 76 | } 77 | 78 | - (void)imy_installHookAjax 79 | { 80 | BOOL installed = [objc_getAssociatedObject(self, IMYHookAjaxKey) boolValue]; 81 | if (installed) { 82 | return; 83 | } 84 | objc_setAssociatedObject(self, IMYHookAjaxKey, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 85 | 86 | _IMYWKHookAjaxHandler *handler = [_IMYWKHookAjaxHandler new]; 87 | [self addScriptMessageHandler:handler name:@"IMYXHR"]; 88 | 89 | // add wk hook 90 | { 91 | NSString *path = [[NSBundle mainBundle] pathForResource:@"imywk_hookajax" ofType:@"js"]; 92 | NSString *jsScript = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; 93 | WKUserScript *userScript = [[WKUserScript alloc] initWithSource:jsScript injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; 94 | [self addUserScript:userScript]; 95 | } 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /Sources/Private/Loader/IMYWebLoader+Impl.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebLoader+Impl.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // 7 | // 8 | 9 | #import "IMYWebLoader.h" 10 | 11 | @interface IMYWebLoader () 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Sources/Private/Loader/IMYWebLoader+Impl.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebLoader+Impl.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // 7 | // 8 | 9 | #import "IMYWebLoader+Impl.h" 10 | #import 11 | 12 | #import "IMYWebCacheHandlerDefaultImpl.h" 13 | #import "IMYWebNetworkHandlerDefaultImpl.h" 14 | #import "IMYWebPrefetchHandlerDefaultImpl.h" 15 | #import "IMYWebAjaxHandlerDefaultImpl.h" 16 | #import "IMYWebRequestHandleManagerDefaultImpl.h" 17 | 18 | static pthread_mutex_t _lock; 19 | static NSMutableDictionary *_instanceMap = nil; 20 | static NSMutableDictionary *_classMap = nil; 21 | 22 | @implementation IMYWebLoader 23 | 24 | + (void)setHandlerClass:(Class)handlerClass forProtocol:(Protocol *)protocol 25 | { 26 | if (!handlerClass || !protocol) { 27 | NSAssert(NO, @"handlerClass/protocol can't nil !"); 28 | return; 29 | } 30 | NSString *key = NSStringFromProtocol(protocol); 31 | pthread_mutex_lock(&_lock); 32 | [_instanceMap removeObjectForKey:key]; 33 | [_classMap setObject:handlerClass forKey:key]; 34 | pthread_mutex_unlock(&_lock); 35 | } 36 | 37 | + (id)handlerForProtocol:(Protocol *)protocol 38 | { 39 | if (!protocol) { 40 | NSAssert(NO, @"protocol can't nil !"); 41 | return nil; 42 | } 43 | NSString *key = NSStringFromProtocol(protocol); 44 | pthread_mutex_lock(&_lock); 45 | id handler = [_instanceMap objectForKey:key]; 46 | if (!handler) { 47 | Class clazz = [_classMap objectForKey:key]; 48 | handler = [[clazz alloc] init]; 49 | if (handler) { 50 | [_instanceMap setObject:handler forKey:key]; 51 | } else { 52 | NSAssert(NO, @"not found handler with %@ protocol !", key); 53 | } 54 | } 55 | pthread_mutex_unlock(&_lock); 56 | return handler; 57 | } 58 | 59 | + (void)initialize 60 | { 61 | 62 | static dispatch_once_t onceToken; 63 | dispatch_once(&onceToken, ^{ 64 | pthread_mutex_init(&_lock, NULL); 65 | _instanceMap = [NSMutableDictionary dictionary]; 66 | _classMap = [NSMutableDictionary dictionary]; 67 | 68 | [self setHandlerClass:[IMYWebCacheHandlerDefaultImpl class] forProtocol:@protocol(IMYWebCacheHandler)]; 69 | [self setHandlerClass:[IMYWebPrefetchHandlerDefaultImpl class] forProtocol:@protocol(IMYWebPrefetchHandler)]; 70 | [self setHandlerClass:[IMYWebNetworkHandlerDefaultImpl class] forProtocol:@protocol(IMYWebNetworkHandler)]; 71 | [self setHandlerClass:[IMYWebAjaxHandlerDefaultImpl class] forProtocol:@protocol(IMYWebAjaxHandler)]; 72 | [self setHandlerClass:[IMYWebRequestHandleManagerDefaultImpl class] forProtocol:@protocol(IMYWebRequestHandleManager)]; 73 | }); 74 | } 75 | 76 | @end 77 | 78 | @implementation IMYWebLoader (Guest) 79 | 80 | + (id)defaultRequestManager 81 | { 82 | return [self handlerForProtocol:@protocol(IMYWebRequestHandleManager)]; 83 | } 84 | 85 | + (id)defaultAjaxHandler 86 | { 87 | return [self handlerForProtocol:@protocol(IMYWebAjaxHandler)]; 88 | } 89 | 90 | + (id)defaultCacheHandler 91 | { 92 | return [self handlerForProtocol:@protocol(IMYWebCacheHandler)]; 93 | } 94 | 95 | + (id)defaultNetworkHandler 96 | { 97 | return [self handlerForProtocol:@protocol(IMYWebNetworkHandler)]; 98 | } 99 | 100 | + (id)defaultPrefetchHandler 101 | { 102 | return [self handlerForProtocol:@protocol(IMYWebPrefetchHandler)]; 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /Sources/Private/URLProtocol/IMYWebRequestHandleManagerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebRequestHandleManagerDefaultImpl.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // 7 | // 8 | 9 | #import 10 | #import "IMYWebRequestHandleManager.h" 11 | 12 | @interface IMYWebRequestHandleManagerDefaultImpl : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Sources/Private/URLProtocol/IMYWebRequestHandleManagerDefaultImpl.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebRequestHandleManagerDefaultImpl.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // 7 | // 8 | 9 | #import "IMYWebRequestHandleManagerDefaultImpl.h" 10 | #import "IMYWebURLProtocol.h" 11 | #import "IMYWebRequestHandlerDefaultImpl.h" 12 | 13 | @interface IMYWebRequestHandleManagerDefaultImpl () { 14 | NSArray *_requestHandlerClass; 15 | } 16 | @end 17 | 18 | @implementation IMYWebRequestHandleManagerDefaultImpl 19 | 20 | - (instancetype)init 21 | { 22 | self = [super init]; 23 | if (self) { 24 | self.enableWKCustomProtocol = YES; 25 | [self addRequestHandlerClass:[IMYWebRequestHandlerDefaultImpl class]]; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)addRequestHandlerClass:(Class)handlerClass 31 | { 32 | NSArray *array = _requestHandlerClass ?: [NSArray array]; 33 | const NSInteger index = [array indexOfObject:handlerClass]; 34 | if (index != NSNotFound) { 35 | return; 36 | } 37 | @synchronized (self) { 38 | array = [array arrayByAddingObject:handlerClass]; 39 | array = [array sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { 40 | NSInteger priority1 = [obj1 respondsToSelector:@selector(priority)] ? [obj1 priority] : 0; 41 | NSInteger priority2 = [obj2 respondsToSelector:@selector(priority)] ? [obj2 priority] : 0; 42 | if (priority1 < priority2) { 43 | return NSOrderedDescending; 44 | } else if (priority1 > priority2) { 45 | return NSOrderedAscending; 46 | } else { 47 | return NSOrderedSame; 48 | } 49 | }]; 50 | 51 | id holdArray = _requestHandlerClass; 52 | dispatch_async(dispatch_get_main_queue(), ^{ 53 | [holdArray description]; 54 | }); 55 | 56 | _requestHandlerClass = array; 57 | } 58 | } 59 | 60 | - (void)removeRequestHandlerClass:(Class)handlerClass 61 | { 62 | NSArray *array = _requestHandlerClass ?: [NSArray array]; 63 | const NSInteger index = [array indexOfObject:handlerClass]; 64 | if (index == NSNotFound) { 65 | return; 66 | } 67 | @synchronized (self) { 68 | NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array]; 69 | [mutableArray removeObjectAtIndex:index]; 70 | 71 | id holdArray = _requestHandlerClass; 72 | dispatch_async(dispatch_get_main_queue(), ^{ 73 | [holdArray description]; 74 | }); 75 | _requestHandlerClass = [mutableArray copy]; 76 | } 77 | } 78 | 79 | - (NSArray> *)requestHandlerClass 80 | { 81 | NSArray *array = _requestHandlerClass; 82 | return array; 83 | } 84 | 85 | - (void)setEnableWKCustomProtocol:(BOOL)enableWKCustomProtocol 86 | { 87 | [IMYWebURLProtocol setEnableWKCustomProtocol:enableWKCustomProtocol]; 88 | } 89 | 90 | - (BOOL)enableWKCustomProtocol 91 | { 92 | return [IMYWebURLProtocol enableWKCustomProtocol]; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /Sources/Private/URLProtocol/IMYWebRequestHandlerDefaultImpl.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebRequestHandlerDefaultImpl.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/11. 6 | // 7 | // 8 | 9 | #import 10 | #import "IMYWebRequestHandleManager.h" 11 | 12 | @interface IMYWebRequestHandlerDefaultImpl : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Sources/Private/URLProtocol/IMYWebRequestHandlerDefaultImpl.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebRequestHandlerDefaultImpl.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/11. 6 | // 7 | // 8 | 9 | #import "IMYWebRequestHandlerDefaultImpl.h" 10 | #import "IMYWebLoader.h" 11 | 12 | static NSString * const IMYWebDXP = @"IMYWebDXP"; 13 | 14 | @interface IMYWebRequestHandlerDefaultImpl () 15 | @property (nonatomic, strong) NSURLRequest *request; 16 | @property (nonatomic, weak) id operation; 17 | @end 18 | 19 | @implementation IMYWebRequestHandlerDefaultImpl 20 | 21 | + (BOOL)shouldHookWithRequest:(NSURLRequest *)request 22 | { 23 | ///只缓存get请求 24 | if (request.HTTPMethod && ![request.HTTPMethod.uppercaseString isEqualToString:@"GET"]) { 25 | return NO; 26 | } 27 | 28 | ///通过UA 来判断是否UIWebView发起的请求 29 | NSString *UA = [request valueForHTTPHeaderField:@"User-Agent"]; 30 | if ([UA containsString:@" AppleWebKit/"] == NO) { 31 | return NO; 32 | } 33 | 34 | /// 不缓存 ajax 请求 35 | NSString *hasAjax = [request valueForHTTPHeaderField:@"X-Requested-With"]; 36 | if (hasAjax != nil) { 37 | return NO; 38 | } 39 | 40 | NSString *pathExtension = [request.URL.absoluteString componentsSeparatedByString:@"?"].firstObject.pathExtension.lowercaseString; 41 | NSArray *validExtension = @[ @"jpg", @"jpeg", @"gif", @"png", @"webp", @"bmp", @"tif", @"ico", @"js", @"css", @"html", @"htm", @"ttf", @"svg"]; 42 | if (pathExtension && [validExtension containsObject:pathExtension]) { 43 | return YES; 44 | } 45 | 46 | return NO; 47 | } 48 | 49 | + (BOOL)cancelHookWithRequest:(NSURLRequest *)request 50 | { 51 | ///已被拦截 52 | if ([request valueForHTTPHeaderField:IMYWebDXP]) { 53 | return YES; 54 | } 55 | return NO; 56 | } 57 | 58 | + (id)requestHandlerWithRequest:(NSURLRequest *)request 59 | { 60 | IMYWebRequestHandlerDefaultImpl *handler = [IMYWebRequestHandlerDefaultImpl new]; 61 | handler.request = request; 62 | return handler; 63 | } 64 | 65 | - (void)startLoadingWithDelegate:(id)delegate 66 | { 67 | NSString *cacheKey = [[IMYWebLoader defaultCacheHandler] cacheKeyForRequest:self.request]; 68 | IMYWebData *webData = [[IMYWebLoader defaultCacheHandler] dataForKey:cacheKey]; 69 | if (webData) { 70 | [delegate request:self didReceiveResponse:webData.response]; 71 | [delegate request:self didReceiveData:webData.data]; 72 | [delegate requestDidFinishLoading:self]; 73 | return; 74 | } 75 | 76 | NSThread *thread = [NSThread currentThread]; 77 | 78 | NSMutableURLRequest *request = [self.request mutableCopy]; 79 | [request setValue:@"1" forHTTPHeaderField:IMYWebDXP]; 80 | 81 | __weak id wself = self; 82 | self.operation = [[IMYWebLoader defaultNetworkHandler] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 83 | __strong IMYWebRequestHandlerDefaultImpl *self = wself; 84 | [self performSelector:@selector(performBlock:) onThread:thread withObject:^{ 85 | __strong IMYWebRequestHandlerDefaultImpl *self = wself; 86 | if (response) { 87 | [delegate request:self didReceiveResponse:response]; 88 | } 89 | if (error) { 90 | [delegate request:self didFailWithError:error]; 91 | } else { 92 | [delegate request:self didReceiveData:data]; 93 | [delegate requestDidFinishLoading:self]; 94 | 95 | IMYWebData *webData = [IMYWebData new]; 96 | webData.data = data; 97 | webData.response = response; 98 | webData.request = self.request; 99 | [[IMYWebLoader defaultCacheHandler] setData:webData forKey:cacheKey]; 100 | } 101 | } waitUntilDone:NO]; 102 | }]; 103 | } 104 | 105 | - (void)performBlock:(dispatch_block_t)block 106 | { 107 | if (block) { 108 | block(); 109 | } 110 | } 111 | 112 | - (void)stopLoading 113 | { 114 | [self.operation cancel]; 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /Sources/Private/URLProtocol/IMYWebURLProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebURLProtocol.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface IMYWebURLProtocol : NSURLProtocol 12 | 13 | @end 14 | 15 | 16 | @interface IMYWebURLProtocol (WKCustomProtocol) 17 | @property (class, nonatomic) BOOL enableWKCustomProtocol; 18 | @end 19 | -------------------------------------------------------------------------------- /Sources/Private/URLProtocol/IMYWebURLProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebURLProtocol.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import "IMYWebURLProtocol.h" 10 | #import "IMYWebLoader+Impl.h" 11 | #import "IMYWebRequestHandleManager.h" 12 | #import 13 | 14 | @interface IMYWebURLProtocol () 15 | 16 | @property (nonatomic, assign) BOOL receivedResponse; 17 | @property (nonatomic, assign) BOOL stoppedLoading; 18 | 19 | @property (nonatomic, strong) id requestHandler; 20 | 21 | @end 22 | 23 | @implementation IMYWebURLProtocol 24 | 25 | + (void)load 26 | { 27 | [NSURLProtocol registerClass:self]; 28 | } 29 | 30 | + (BOOL)canInitWithTask:(NSURLSessionTask *)task 31 | { 32 | return [self canInitWithRequest:task.currentRequest]; 33 | } 34 | 35 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request 36 | { 37 | return request; 38 | } 39 | 40 | + (BOOL)canInitWithRequest:(NSURLRequest *)request 41 | { 42 | NSArray *requestHandlerClass = [IMYWebLoader defaultRequestManager].requestHandlerClass; 43 | if (!requestHandlerClass.count) { 44 | return NO; 45 | } 46 | BOOL shouldHook = NO; 47 | BOOL cancelHook = NO; 48 | 49 | for (Class handlerClass in requestHandlerClass) { 50 | shouldHook = [handlerClass shouldHookWithRequest:request]; 51 | if (shouldHook) { 52 | break; 53 | } 54 | } 55 | for (Class handlerClass in requestHandlerClass) { 56 | cancelHook = [handlerClass cancelHookWithRequest:request]; 57 | if (cancelHook) { 58 | break; 59 | } 60 | } 61 | 62 | return shouldHook && !cancelHook; 63 | } 64 | 65 | - (void)startLoading 66 | { 67 | NSURLRequest *request = self.request; 68 | id requestHandler = nil; 69 | NSArray *requestHandlerClass = [IMYWebLoader defaultRequestManager].requestHandlerClass; 70 | for (Class handlerClass in requestHandlerClass) { 71 | requestHandler = [handlerClass requestHandlerWithRequest:request]; 72 | if (requestHandler) { 73 | break; 74 | } 75 | } 76 | if (!requestHandler) { 77 | NSError *error = [NSError errorWithDomain:@"no request handler !" code:-999 userInfo:nil]; 78 | [self callbackRequestDidFailWithError:error]; 79 | } else { 80 | self.requestHandler = requestHandler; 81 | [self.requestHandler startLoadingWithDelegate:self]; 82 | } 83 | } 84 | 85 | - (void)stopLoading 86 | { 87 | self.stoppedLoading = YES; 88 | [self.requestHandler stopLoading]; 89 | } 90 | 91 | /// 保证 Response URL 跟请求时一致 92 | - (NSURLResponse *)callbackResponseWithResponse:(NSURLResponse *)response 93 | { 94 | NSHTTPURLResponse *httpResponse = (id)response; 95 | if ([httpResponse isKindOfClass:[NSHTTPURLResponse class]]) { 96 | if (![httpResponse.URL isEqual:self.request.URL]) { 97 | httpResponse = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL statusCode:httpResponse.statusCode HTTPVersion:@"HTTP/1.1" headerFields:httpResponse.allHeaderFields]; 98 | } 99 | } 100 | return httpResponse; 101 | } 102 | 103 | - (void)callbackRequestWasRedirectedToRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response 104 | { 105 | if (self.receivedResponse) { 106 | NSError *error = [NSError errorWithDomain:@"only return first received response !!" code:-999 userInfo:nil]; 107 | NSAssert(NO, error.domain); 108 | return; 109 | } 110 | if (self.stoppedLoading) { 111 | return; 112 | } 113 | response = [self callbackResponseWithResponse:response]; 114 | if (!response) { 115 | NSError *error = [NSError errorWithDomain:@"wasRedirectedToRequest redirectResponse not found !!" code:-999 userInfo:nil]; 116 | [self callbackRequestDidFailWithError:error]; 117 | } else { 118 | self.receivedResponse = YES; 119 | [self.client URLProtocol:self wasRedirectedToRequest:request redirectResponse:response]; 120 | } 121 | } 122 | 123 | - (void)callbackRequestDidReceiveResponse:(NSURLResponse *)response 124 | { 125 | if (self.receivedResponse) { 126 | NSError *error = [NSError errorWithDomain:@"only return first received response !!" code:-999 userInfo:nil]; 127 | NSAssert(NO, error.domain); 128 | return; 129 | } 130 | if (self.stoppedLoading) { 131 | return; 132 | } 133 | response = [self callbackResponseWithResponse:response]; 134 | if (!response) { 135 | NSError *error = [NSError errorWithDomain:@"response not found !!" code:-999 userInfo:nil]; 136 | NSLog(@"%@", error.domain); 137 | } else { 138 | self.receivedResponse = YES; 139 | [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; 140 | } 141 | } 142 | 143 | - (void)callbackRequestDidLoadData:(NSData *)data 144 | { 145 | if (self.stoppedLoading) { 146 | return; 147 | } 148 | if (!self.receivedResponse) { 149 | NSError *error = [NSError errorWithDomain:@"didReceiveData to be scheduled before didReceiveResponse !!" code:-999 userInfo:nil]; 150 | NSLog(@"%@", error.domain); 151 | } else if (data.length > 0) { 152 | [self.client URLProtocol:self didLoadData:data]; 153 | } 154 | } 155 | 156 | - (void)callbackRequestDidFailWithError:(NSError *)error 157 | { 158 | if (self.stoppedLoading) { 159 | return; 160 | } 161 | NSLog(@"%@", error.domain); 162 | [self.client URLProtocol:self didFailWithError:error]; 163 | } 164 | 165 | - (void)callbackRequestDidFinishLoading 166 | { 167 | if (self.stoppedLoading) { 168 | return; 169 | } 170 | if (!self.receivedResponse) { 171 | NSError *error = [NSError errorWithDomain:@"didFinishLoading to be scheduled before didReceiveResponse !!" code:-999 userInfo:nil]; 172 | [self callbackRequestDidFailWithError:error]; 173 | } else { 174 | [self.client URLProtocolDidFinishLoading:self]; 175 | } 176 | } 177 | 178 | #pragma mark - request delegate 179 | 180 | - (void)request:(id)request wasRedirectedToRequest:(NSURLRequest *)redirectRequest redirectResponse:(NSURLResponse *)redirectResponse 181 | { 182 | [self callbackRequestWasRedirectedToRequest:redirectRequest redirectResponse:redirectResponse]; 183 | } 184 | 185 | - (void)request:(id)request didReceiveResponse:(NSURLResponse *)response 186 | { 187 | [self callbackRequestDidReceiveResponse:response]; 188 | } 189 | 190 | - (void)request:(id)request didReceiveData:(NSData *)data 191 | { 192 | [self callbackRequestDidLoadData:data]; 193 | } 194 | 195 | - (void)requestDidFinishLoading:(id)request 196 | { 197 | [self callbackRequestDidFinishLoading]; 198 | } 199 | 200 | - (void)request:(id)request didFailWithError:(NSError *)error 201 | { 202 | [self callbackRequestDidFailWithError:error]; 203 | } 204 | 205 | @end 206 | 207 | @implementation IMYWebURLProtocol (WKCustomProtocol) 208 | 209 | static BOOL kIMYEnableWKCustomProtocol = NO; 210 | + (void)setEnableWKCustomProtocol:(BOOL)enableWKCustomProtocol 211 | { 212 | kIMYEnableWKCustomProtocol = enableWKCustomProtocol; 213 | id contextController = NSClassFromString([NSString stringWithFormat:@"%@%@%@",@"WK",@"Browsing",@"ContextController"]); 214 | if (!contextController) { 215 | return; 216 | } 217 | SEL performSEL = nil; 218 | if (enableWKCustomProtocol) { 219 | performSEL = NSSelectorFromString([NSString stringWithFormat:@"%@%@",@"register",@"SchemeForCustomProtocol:"]); 220 | } else { 221 | performSEL = NSSelectorFromString([NSString stringWithFormat:@"%@%@",@"unregister",@"SchemeForCustomProtocol:"]); 222 | } 223 | #pragma clang diagnostic push 224 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 225 | if ([contextController respondsToSelector:performSEL]) { 226 | [contextController performSelector:performSEL withObject:@"http"]; 227 | [contextController performSelector:performSEL withObject:@"https"]; 228 | } 229 | #pragma clang diagnostic pop 230 | } 231 | 232 | + (BOOL)enableWKCustomProtocol 233 | { 234 | return kIMYEnableWKCustomProtocol; 235 | } 236 | 237 | @end 238 | -------------------------------------------------------------------------------- /Sources/Private/URLProtocol/NSObject+WKCustomProtocolLoader.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+WKCustomProtocolLoader.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // 7 | // 8 | 9 | #import 10 | 11 | /// 原始 WKCustomProtocolLoader 是在主线程进行网络请求,这边进行 hook 改为在子线程请求 12 | @interface NSObject (WKCustomProtocolLoader) 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Sources/Private/URLProtocol/NSObject+WKCustomProtocolLoader.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+WKCustomProtocolLoader.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // 7 | // 8 | 9 | #import "NSObject+WKCustomProtocolLoader.h" 10 | #import "IMYWebLoader.h" 11 | #import "IMYWebUtils.h" 12 | 13 | @interface _IMYWKProtocolLoader : NSObject 14 | @property (nonatomic, weak) id wkloader; 15 | @property (nonatomic, strong) NSURLRequest *request; 16 | @end 17 | 18 | @implementation _IMYWKProtocolLoader 19 | 20 | @end 21 | 22 | /// 原始 WKCustomProtocolLoader 是在主线程进行网络请求,这边进行 hook 改为在子线程请求 23 | @implementation NSObject (WKCustomProtocolLoader) 24 | 25 | + (void)load 26 | { 27 | Class clazz = NSClassFromString([NSString stringWithFormat:@"%@%@%@",@"WK",@"Custom",@"ProtocolLoader"]); 28 | SEL swizzleSEL = NSSelectorFromString([NSString stringWithFormat:@"%@%@%@",@"initWithCustomProtocol",@"ManagerProxy:customProtocolID",@":request:connection:"]); 29 | [IMYWebUtils swizzleClass:clazz origMethod:swizzleSEL withMethod:@selector(wkloader_initWithProtocolManager:protocolID:request:connection:)]; 30 | 31 | SEL swizzleLegacySEL = NSSelectorFromString([NSString stringWithFormat:@"%@%@",@"initWithLegacyCustomProtocolManagerProxy",@":customProtocolID:request:"]); 32 | [IMYWebUtils swizzleClass:clazz origMethod:swizzleLegacySEL withMethod:@selector(wkloader_initWithLegacyCustomProtocolManagerProxy:customProtocolID:request:)]; 33 | } 34 | 35 | - (id)wkloader_initWithLegacyCustomProtocolManagerProxy:(void*)customProtocolManagerProxy customProtocolID:(uint64_t)customProtocolID request:(NSURLRequest *)request 36 | { 37 | id wkloader = [self wkloader_initWithLegacyCustomProtocolManagerProxy:customProtocolManagerProxy customProtocolID:customProtocolID request:[NSURLRequest new]]; 38 | [NSObject wkloader_hookWKLoader:wkloader request:request]; 39 | return wkloader; 40 | } 41 | 42 | - (id)wkloader_initWithProtocolManager:(void *)protocolManager protocolID:(uint64_t)protocolID request:(NSURLRequest *)request connection:(void *)connection 43 | { 44 | id wkloader = [self wkloader_initWithProtocolManager:protocolManager protocolID:protocolID request:[NSURLRequest new] connection:connection]; 45 | [NSObject wkloader_hookWKLoader:wkloader request:request]; 46 | return wkloader; 47 | } 48 | 49 | + (void)wkloader_hookWKLoader:(id)wkloader request:(NSURLRequest *)request 50 | { 51 | NSURLConnection *urlConnection = [wkloader valueForKey:@"_urlConnection"]; 52 | [urlConnection setValue:nil forKeyPath:@"_internal._delegate"]; 53 | [urlConnection unscheduleFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 54 | [urlConnection cancel]; 55 | 56 | _IMYWKProtocolLoader *weakObj = [_IMYWKProtocolLoader new]; 57 | weakObj.wkloader = wkloader; 58 | weakObj.request = request; 59 | 60 | [self performSelector:@selector(wkloader_startConnection:) onThread:[[IMYWebLoader defaultNetworkHandler] networkRequestThread] withObject:weakObj waitUntilDone:NO]; 61 | } 62 | 63 | + (void)wkloader_startConnection:(_IMYWKProtocolLoader *)weakObj 64 | { 65 | id wkloader = weakObj.wkloader; 66 | if (!wkloader) { 67 | return; 68 | } 69 | NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:weakObj.request delegate:wkloader startImmediately:NO]; 70 | [urlConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 71 | [urlConnection start]; 72 | [wkloader setValue:urlConnection forKey:@"_urlConnection"]; 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Sources/Private/Utils/IMYWebUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebUtils.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface IMYWebUtils : NSObject 12 | 13 | ///模拟网页的 URL 生成规则 14 | + (NSURL *)URLWithString:(NSString *)urlString baseURL:(NSURL *)baseURL; 15 | 16 | + (BOOL)swizzleClass:(Class)clazz origMethod:(SEL)origSel_ withMethod:(SEL)altSel_; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Sources/Private/Utils/IMYWebUtils.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebUtils.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // 7 | // 8 | 9 | #import "IMYWebUtils.h" 10 | #import 11 | 12 | @implementation IMYWebUtils 13 | 14 | + (NSURL *)URLWithString:(NSString *)urlString baseURL:(NSURL *)baseURL 15 | { 16 | if (!urlString.length) { 17 | return nil; 18 | } 19 | if (![urlString containsString:@"://"]) { 20 | if ([urlString hasPrefix:@"//"]) { 21 | urlString = [NSString stringWithFormat:@"%@:%@", baseURL.scheme?:@"http", urlString]; 22 | } 23 | else if ([urlString hasPrefix:@"/"]) { 24 | urlString = [NSString stringWithFormat:@"%@://%@%@", baseURL.scheme?:@"http", baseURL.host, urlString]; 25 | } 26 | else { 27 | urlString = [NSString stringWithFormat:@"%@://%@", baseURL.scheme?:@"http", urlString]; 28 | } 29 | } 30 | NSURL *URL = [NSURL URLWithString:urlString]; 31 | if (!URL) { 32 | urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 33 | URL = [NSURL URLWithString:urlString]; 34 | } 35 | return URL; 36 | } 37 | 38 | + (BOOL)swizzleClass:(Class)clazz origMethod:(SEL)origSel_ withMethod:(SEL)altSel_ 39 | { 40 | Method origMethod = class_getInstanceMethod(clazz, origSel_); 41 | if (!origMethod) { 42 | return NO; 43 | } 44 | Method altMethod = class_getInstanceMethod(clazz, altSel_); 45 | if (!altMethod) { 46 | return NO; 47 | } 48 | 49 | class_addMethod(clazz, 50 | origSel_, 51 | class_getMethodImplementation(clazz, origSel_), 52 | method_getTypeEncoding(origMethod)); 53 | class_addMethod(clazz, 54 | altSel_, 55 | class_getMethodImplementation(clazz, altSel_), 56 | method_getTypeEncoding(altMethod)); 57 | 58 | method_exchangeImplementations(class_getInstanceMethod(clazz, origSel_), class_getInstanceMethod(clazz, altSel_)); 59 | 60 | return YES; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Sources/Public/Data/IMYWebData.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebData.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface IMYWebData : NSObject 14 | 15 | @property (nullable, nonatomic, copy) NSURLRequest *request; /**< 请求链接*/ 16 | @property (nullable, nonatomic, copy) NSURLRequest *redirectRequest; /**< 重定向链接*/ 17 | @property (nullable, nonatomic, copy) NSURLResponse *response; /**< 服务器返回的response*/ 18 | @property (nullable, nonatomic, copy) NSData *data; /**< 数据*/ 19 | @property (nullable, nonatomic, copy) NSError *error; /**< 错误*/ 20 | 21 | @property (nullable, nonatomic, copy) NSDate *createDate; /**< 创建时间*/ 22 | @property (nullable, nonatomic, copy) NSDictionary *userInfo; /**< 用户数据*/ 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /Sources/Public/Data/IMYWebData.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebData.m 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // 7 | // 8 | 9 | #import "IMYWebData.h" 10 | 11 | static NSString * const kRequestKey = @"request"; 12 | static NSString * const kRedirectRequestKey = @"redirectRequest"; 13 | static NSString * const kResponseKey = @"response"; 14 | static NSString * const kDataKey = @"data"; 15 | static NSString * const kErrorKey = @"error"; 16 | 17 | static NSString * const kCreateDateKey = @"createDate"; 18 | static NSString * const kUserInfoKey = @"userInfo"; 19 | 20 | @implementation IMYWebData 21 | 22 | - (instancetype)init 23 | { 24 | self = [super init]; 25 | if (self) { 26 | _createDate = [NSDate date]; 27 | } 28 | return self; 29 | } 30 | 31 | + (BOOL)supportsSecureCoding 32 | { 33 | return YES; 34 | } 35 | 36 | - (void)encodeWithCoder:(NSCoder *)aCoder 37 | { 38 | [aCoder encodeObject:_request forKey:kRequestKey]; 39 | [aCoder encodeObject:_redirectRequest forKey:kRedirectRequestKey]; 40 | [aCoder encodeObject:_response forKey:kResponseKey]; 41 | [aCoder encodeObject:_data forKey:kDataKey]; 42 | [aCoder encodeObject:_error forKey:kErrorKey]; 43 | 44 | [aCoder encodeObject:_createDate forKey:kCreateDateKey]; 45 | [aCoder encodeObject:_userInfo forKey:kUserInfoKey]; 46 | } 47 | 48 | - (id)initWithCoder:(NSCoder*)aDecoder 49 | { 50 | self = [super init]; 51 | if (self) { 52 | _request = [aDecoder decodeObjectForKey:kRequestKey]; 53 | _redirectRequest = [aDecoder decodeObjectForKey:kRedirectRequestKey]; 54 | _response = [aDecoder decodeObjectForKey:kResponseKey]; 55 | _data = [aDecoder decodeObjectForKey:kDataKey]; 56 | _error = [aDecoder decodeObjectForKey:kErrorKey]; 57 | 58 | _createDate = [aDecoder decodeObjectForKey:kCreateDateKey]; 59 | _userInfo = [aDecoder decodeObjectForKey:kUserInfoKey]; 60 | } 61 | return self; 62 | } 63 | 64 | @end 65 | 66 | -------------------------------------------------------------------------------- /Sources/Public/Handler/IMYWebAjaxHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebAjaxHandler.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol IMYWebAjaxHandler 14 | 15 | - (void)startWithMethod:(NSString *)method 16 | url:(NSString *)urlString 17 | baseURL:(nullable NSURL *)baseURL 18 | headers:(nullable NSDictionary *)headers 19 | body:(nullable id)body 20 | completedBlock:(void (^)(NSInteger httpCode, NSDictionary * _Nullable headers, NSString * _Nullable data))completedBlock; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /Sources/Public/Handler/IMYWebCacheHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebCacheHandler.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // 7 | // 8 | 9 | #import 10 | #import "IMYWebData.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @protocol IMYWebCacheHandler 15 | 16 | - (NSString *)cacheKeyForRequest:(NSURLRequest *)request; 17 | 18 | - (nullable IMYWebData *)dataForKey:(NSString *)key; 19 | - (void)setData:(nullable IMYWebData *)data forKey:(NSString *)key; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /Sources/Public/Handler/IMYWebNetworkHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebNetworkHandler.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import 10 | #import "IMYWebOperation.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @protocol IMYWebNetworkHandler 15 | 16 | /// 生成 发起请求的 Request 17 | - (NSURLRequest *)requestWithString:(NSString *)urlString; 18 | 19 | /// 发起网络请求 20 | - (id)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler; 21 | 22 | /// 子线程 23 | - (NSThread *)networkRequestThread; 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /Sources/Public/Handler/IMYWebPrefetchHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebPrefetchHandler.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import 10 | #import "IMYWebOperation.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @protocol IMYWebPrefetcherProtocol 15 | 16 | @property (nonatomic, copy) NSString *webUrl; 17 | 18 | @property (nonatomic, assign, readonly, getter=isComplated) BOOL complated; 19 | 20 | @end 21 | 22 | @protocol IMYWebPrefetchHandler 23 | 24 | ///一次生命周期内 对同一个 url,只会预加载一次,除非已经被移除了 25 | - (id)prefetchWebUrl:(NSString *)webUrl; 26 | 27 | ///取消全部预加载操作 28 | - (void)cancelAllPrefetcherLoading; 29 | 30 | ///移除预加载对象 31 | - (void)removePrefetcherForWebUrl:(NSString *)webUrl; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /Sources/Public/Handler/IMYWebRequestHandleManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebRequestHandleManager.h 3 | // MeetYou 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol IMYWebRequestHandler,IMYWebCacheHandler; 14 | 15 | @protocol IMYWebRequestDelegate 16 | @required 17 | 18 | - (void)request:(id)request wasRedirectedToRequest:(NSURLRequest *)redirectRequest redirectResponse:(nullable NSURLResponse *)redirectResponse; 19 | 20 | - (void)request:(id)request didReceiveResponse:(NSURLResponse *)response; 21 | 22 | - (void)request:(id)request didReceiveData:(NSData *)data; 23 | 24 | - (void)requestDidFinishLoading:(id)request; 25 | 26 | - (void)request:(id)request didFailWithError:(NSError *)error; 27 | 28 | @end 29 | 30 | @protocol IMYWebRequestHandler 31 | @required 32 | 33 | /// 是否要拦截该 request 34 | + (BOOL)shouldHookWithRequest:(NSURLRequest *)request; 35 | 36 | /// 取消该 Request 的拦截,只要有一个 class 返回 YES,则不拦截该请求 37 | + (BOOL)cancelHookWithRequest:(NSURLRequest *)request; 38 | 39 | /// 根据 Request 返回具体的请求实例, 会使用第一个返回的请求对象 40 | + (nullable id)requestHandlerWithRequest:(NSURLRequest *)request; 41 | 42 | /// 开始加载 43 | - (void)startLoadingWithDelegate:(id)delegate; 44 | 45 | /// 停止加载 46 | - (void)stopLoading; 47 | 48 | @optional 49 | 50 | /// 实现后 会用返回的缓存控制器 , default:[IMYWebLoader defaultCacheHandler] 51 | + (id)cacheHandler; 52 | 53 | /// 拦截器优先级,默认 0,越大排序越前 54 | + (NSInteger)priority; 55 | 56 | @end 57 | 58 | @protocol IMYWebRequestHandleManager 59 | 60 | - (void)addRequestHandlerClass:(Class)handlerClass; 61 | 62 | - (void)removeRequestHandlerClass:(Class)handlerClass; 63 | 64 | - (NSArray> *)requestHandlerClass; 65 | 66 | ///是否开启 WKWebView Custom Protocol 拦截 http、https。 default:YES 67 | @property (nonatomic, assign) BOOL enableWKCustomProtocol; 68 | 69 | @end 70 | 71 | NS_ASSUME_NONNULL_END 72 | -------------------------------------------------------------------------------- /Sources/Public/Header/IMYWebDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebDefines.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/4/10. 6 | // 7 | // 8 | 9 | #ifndef IMYWebDefines_h 10 | #define IMYWebDefines_h 11 | 12 | #import "IMYWebRequestHandleManager.h" 13 | #import "IMYWebCacheHandler.h" 14 | #import "IMYWebPrefetchHandler.h" 15 | #import "IMYWebNetworkHandler.h" 16 | #import "IMYWebAjaxHandler.h" 17 | #import "WKUserContentController+IMYHookAjax.h" 18 | 19 | #endif /* IMYWebDefines_h */ 20 | -------------------------------------------------------------------------------- /Sources/Public/Header/IMYWebOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebOperation.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/28. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @protocol IMYWebOperation 12 | 13 | - (void)cancel; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Sources/Public/Header/WKUserContentController+IMYHookAjax.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKUserContentController+IMYHookAjax.h 3 | // IMYViewKit 4 | // 5 | // Created by ljh on 2017/3/24. 6 | // Copyright © 2017年 IMY. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WKUserContentController (IMYHookAjax) 12 | 13 | /// 当 WebView 释放的时候,必须手动调用 uninstall 来移除该对象, 不然该对象永远不会释放 14 | - (void)imy_installHookAjax; 15 | 16 | /// 卸载 hook ajax 17 | - (void)imy_uninstallHookAjax; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Sources/Public/IMYWebLoader.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMYWebPrefetcher.h 3 | // Pods 4 | // 5 | // Created by ljh on 2017/2/27. 6 | // 7 | // 8 | 9 | #import 10 | #import "IMYWebDefines.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface IMYWebLoader : NSObject 15 | 16 | + (id)handlerForProtocol:(Protocol *)protocol; 17 | 18 | + (void)setHandlerClass:(Class)handlerClass forProtocol:(Protocol *)protocol; 19 | 20 | @end 21 | 22 | @interface IMYWebLoader (Guest) 23 | 24 | ///请求拦截控制器 25 | @property (class, readonly, nonatomic) id defaultRequestManager; 26 | ///缓存控制器 27 | @property (class, readonly, nonatomic) id defaultCacheHandler; 28 | ///预加载控制器 29 | @property (class, readonly, nonatomic) id defaultPrefetchHandler; 30 | ///数据请求 31 | @property (class, readonly, nonatomic) id defaultNetworkHandler; 32 | /// Ajax 需要手动 执行 imy_installHookAjax,目前需要针对 WKWebView 33 | @property (class, readonly, nonatomic) id defaultAjaxHandler; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /Sources/Resources/imywk_hookajax.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | if (window.imy_realxhr) { 3 | return 4 | } 5 | window.imy_realxhr = XMLHttpRequest; 6 | var timestamp = new Date().getTime(); 7 | timestamp = parseInt((timestamp / 1000) % 100000); 8 | var global_index = timestamp + 1; 9 | var global_map = {}; 10 | window.imy_realxhr_callback = function(id, message) { 11 | var hookAjax = global_map[id]; 12 | if (hookAjax) { 13 | hookAjax.callbackNative(message) 14 | } 15 | global_map[id] = null 16 | }; 17 | 18 | function BaseHookAjax() {} 19 | BaseHookAjax.prototype = window.imy_realxhr; 20 | 21 | function hookAjax() {} 22 | hookAjax.prototype = BaseHookAjax; 23 | hookAjax.prototype.readyState = 0; 24 | hookAjax.prototype.responseText = ""; 25 | hookAjax.prototype.responseHeaders = {}; 26 | hookAjax.prototype.status = 0; 27 | hookAjax.prototype.statusText = ""; 28 | hookAjax.prototype.onreadystatechange = null; 29 | hookAjax.prototype.onload = null; 30 | hookAjax.prototype.onerror = null; 31 | hookAjax.prototype.onabort = null; 32 | hookAjax.prototype.open = function() { 33 | this.open_arguments = arguments; 34 | this.readyState = 1; 35 | if (this.onreadystatechange) { 36 | this.onreadystatechange() 37 | } 38 | }; 39 | hookAjax.prototype.setRequestHeader = function(name, value) { 40 | if (!this._headers) { 41 | this._headers = {} 42 | } 43 | this._headers[name] = value 44 | }; 45 | hookAjax.prototype.send = function() { 46 | if (arguments.length >= 1 && !!arguments[0]) { 47 | this.sendNative(arguments[0]) 48 | } else { 49 | var xhr = new window.imy_realxhr(); 50 | this._xhr = xhr; 51 | var that = this; 52 | xhr.onreadystatechange = function() { 53 | that.readyState = xhr.readyState; 54 | if (that.readyState <= 1) { 55 | return 56 | } 57 | if (xhr.readyState >= 3) { 58 | that.status = xhr.status; 59 | that.statusText = xhr.statusText; 60 | that.responseText = xhr.responseText; 61 | } 62 | that.callbackStateChanged(); 63 | }; 64 | xhr.open.apply(xhr, this.open_arguments); 65 | for (name in this._headers) { 66 | xhr.setRequestHeader(name, this._headers[name]) 67 | } 68 | xhr.send.apply(xhr, arguments) 69 | } 70 | }; 71 | hookAjax.prototype.sendNative = function(data) { 72 | this.request_id = global_index; 73 | global_map[this.request_id] = this; 74 | global_index++; 75 | var message = {}; 76 | message.id = this.request_id; 77 | message.data = data; 78 | message.method = this.open_arguments[0]; 79 | message.url = this.open_arguments[1]; 80 | message.headers = this._headers; 81 | window.webkit.messageHandlers.IMYXHR.postMessage(message) 82 | }; 83 | hookAjax.prototype.callbackNative = function(message) { 84 | if (!this.is_abort) { 85 | this.status = message.status; 86 | this.responseText = (!!message.data) ? message.data : ""; 87 | this.responseHeaders = message.headers; 88 | this.readyState = 4 89 | } else { 90 | this.readyState = 1 91 | } 92 | this.callbackStateChanged(); 93 | }; 94 | hookAjax.prototype.callbackStateChanged = function() { 95 | if (this.readyState >= 3) { 96 | if (this.status >= 200 && this.status < 300) { 97 | this.statusText = "OK" 98 | } else { 99 | this.statusText = "Fail" 100 | } 101 | } 102 | if (this.onreadystatechange) { 103 | this.onreadystatechange() 104 | } 105 | if (this.readyState == 4) { 106 | if (this.statusText == "OK") { 107 | this.onload ? this.onload() : "" 108 | } else { 109 | this.onerror ? this.onerror() : "" 110 | } 111 | } 112 | }; 113 | hookAjax.prototype.abort = function() { 114 | this.is_abort = true; 115 | if (this._xhr) { 116 | this._xhr.abort() 117 | } 118 | if (this.onabort) { 119 | this.onabort() 120 | } 121 | }; 122 | hookAjax.prototype.getAllResponseHeaders = function() { 123 | if (this._xhr) { 124 | return this._xhr.getAllResponseHeaders() 125 | } else { 126 | return this.responseHeaders 127 | } 128 | }; 129 | hookAjax.prototype.getResponseHeader = function(name) { 130 | if (this._xhr) { 131 | return this._xhr.getResponseHeader(name) 132 | } else { 133 | for (key in this.responseHeaders) { 134 | if (key.toLowerCase() == name.toLowerCase()) { 135 | return this.responseHeaders[key] 136 | } 137 | } 138 | return null 139 | } 140 | }; 141 | XMLHttpRequest = hookAjax; 142 | window.imy_hookAjax = function() { 143 | XMLHttpRequest = hookAjax 144 | }; 145 | window.imy_unhookAjax = function() { 146 | XMLHttpRequest = window.imy_realxhr 147 | } 148 | })(); 149 | --------------------------------------------------------------------------------