├── .DS_Store ├── .gitignore ├── LICENSE ├── NetworkInterceptor.podspec ├── NetworkInterceptor.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ └── NetworkInterceptor.xcscheme └── xcuserdata │ └── kennethpoon.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── NetworkInterceptor.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ ├── IDEWorkspaceChecks.plist │ └── WorkspaceSettings.xcsettings └── xcuserdata │ └── kennethpoon.xcuserdatad │ ├── WorkspaceSettings.xcsettings │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── NetworkInterceptor ├── Info.plist ├── NetworkInterceptor.h └── Source │ ├── Helpers │ └── URLRequestFactory.swift │ ├── InterceptedRequestHandlerRegistrable.swift │ ├── NetworkInterceptor.swift │ ├── NetworkInterceptorConfig.swift │ ├── NetworkRequestInterceptor.swift │ ├── NetworkRequestRedirectHandler.swift │ ├── RequestEvaluator │ ├── AnyHttpRequestEvaluator.swift │ └── DomainHttpRequestEvaluator.swift │ ├── RequestRedirector │ ├── AlternateDomainRequestRedirector.swift │ └── AlternateUrlRequestRedirector.swift │ ├── SniffableRequestHandler │ ├── AlternateDomainSniffableRequestHandler.swift │ ├── ConsoleLoggerSniffableRequestHandler.swift │ ├── SlackHookSniffableRequestHandler.swift │ └── SlackSniffableRequestHandler.swift │ └── URLProtocol │ ├── NetworkRedirectUrlProtocol.swift │ └── NetworkRequestSniffableUrlProtocol.swift ├── NetworkInterceptorExample ├── .DS_Store ├── NetworkInterceptorExample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── kennethpoon.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── NetworkInterceptorExample.xcscheme │ └── xcuserdata │ │ └── kennethpoon.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── NetworkInterceptorExample │ ├── .DS_Store │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── CodeInjection.m │ ├── CodeInjectionSwift.swift │ ├── Info.plist │ ├── NetworkInterceptorExample-Bridging-Header.h │ └── ViewController.swift ├── Podfile ├── Podfile.lock └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/depoon/NetworkInterceptor/0222c86aa9822bd90e16fc930232f3614ef23883/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | NetworkInterceptor.xcodeproj/project.xcworkspace/xcuserdata/** 3 | NetworkInterceptorExample/.DS_Store 4 | *.xcuserdatad 5 | NetworkInterceptor.xcworkspace/xcuserdata/*.xcuserdatad/** 6 | Pods/** 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kenneth Poon 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 | -------------------------------------------------------------------------------- /NetworkInterceptor.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.name = "NetworkInterceptor" 4 | s.version = "0.0.8" 5 | s.swift_version = '4.2' 6 | s.summary = "Intercepting Network Requests" 7 | s.description = <<-DESC 8 | Features 9 | 1. Observe all outgoing URLRequests, including SSL pinned 10 | 2. Ability to redirect URLRequests to any target domain or URL 11 | DESC 12 | s.homepage = "https://github.com/depoon/NetworkInterceptor" 13 | s.license = 'MIT' 14 | s.author = { "depoon" => "de_poon@hotmail.com" } 15 | s.source = { :git => "https://github.com/depoon/NetworkInterceptor.git", :tag => s.version.to_s } 16 | 17 | s.platform = :ios, '10.0' 18 | s.requires_arc = true 19 | 20 | s.source_files = 'NetworkInterceptor/Source/**/*' 21 | s.dependency 'GzipSwift', '4.1.0' 22 | s.dependency 'URLRequest-cURL' 23 | end 24 | -------------------------------------------------------------------------------- /NetworkInterceptor.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 03915A41CCCF5103124B33E6 /* Pods_NetworkInterceptor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C737E0A347E83ED712E90F0 /* Pods_NetworkInterceptor.framework */; }; 11 | 1C19A2A9216A0D140041F5BE /* URLRequestFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C19A2A8216A0D140041F5BE /* URLRequestFactory.swift */; }; 12 | 1C2E2F2820F4783C003CFD58 /* NetworkInterceptorConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2E2F2720F4783C003CFD58 /* NetworkInterceptorConfig.swift */; }; 13 | 1C2E2F2A20F47B2E003CFD58 /* ConsoleLoggerSniffableRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2E2F2920F47B2E003CFD58 /* ConsoleLoggerSniffableRequestHandler.swift */; }; 14 | 1C2E2F7521062A79003CFD58 /* InterceptedRequestHandlerRegistrable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2E2F7421062A79003CFD58 /* InterceptedRequestHandlerRegistrable.swift */; }; 15 | 1C36592F2129CE1D0040A19B /* AnyHttpRequestEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C36592E2129CE1D0040A19B /* AnyHttpRequestEvaluator.swift */; }; 16 | 1C831DBC20B9A3AE003648C6 /* NetworkInterceptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C831DBA20B9A3AE003648C6 /* NetworkInterceptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 1C831DC620B9A420003648C6 /* NetworkInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C831DC520B9A420003648C6 /* NetworkInterceptor.swift */; }; 18 | 1C831DC920B9A706003648C6 /* NetworkRequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C831DC820B9A706003648C6 /* NetworkRequestInterceptor.swift */; }; 19 | 1C831DCE20B9AA03003648C6 /* SlackSniffableRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C831DCD20B9AA03003648C6 /* SlackSniffableRequestHandler.swift */; }; 20 | 1CAD80CC21328D5600CA2160 /* DomainHttpRequestEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD80CB21328D5600CA2160 /* DomainHttpRequestEvaluator.swift */; }; 21 | 1CAD80CE21328E9400CA2160 /* AlternateDomainSniffableRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD80CD21328E9400CA2160 /* AlternateDomainSniffableRequestHandler.swift */; }; 22 | 1CAD80D02132947500CA2160 /* NetworkRequestRedirectHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD80CF2132947500CA2160 /* NetworkRequestRedirectHandler.swift */; }; 23 | 1CAD80D221329B7100CA2160 /* NetworkRedirectUrlProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD80D121329B7100CA2160 /* NetworkRedirectUrlProtocol.swift */; }; 24 | 1CAD80D521329BE900CA2160 /* NetworkRequestSniffableUrlProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD80D421329BE900CA2160 /* NetworkRequestSniffableUrlProtocol.swift */; }; 25 | 1CAD80D82132A7DC00CA2160 /* AlternateDomainRequestRedirector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD80D72132A7DC00CA2160 /* AlternateDomainRequestRedirector.swift */; }; 26 | 1CAD80DA2133042400CA2160 /* AlternateUrlRequestRedirector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD80D92133042400CA2160 /* AlternateUrlRequestRedirector.swift */; }; 27 | 56F02822227165B400137037 /* SlackHookSniffableRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56F02821227165B400137037 /* SlackHookSniffableRequestHandler.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 1C19A2A8216A0D140041F5BE /* URLRequestFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestFactory.swift; sourceTree = ""; }; 32 | 1C2E2F2720F4783C003CFD58 /* NetworkInterceptorConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkInterceptorConfig.swift; sourceTree = ""; }; 33 | 1C2E2F2920F47B2E003CFD58 /* ConsoleLoggerSniffableRequestHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLoggerSniffableRequestHandler.swift; sourceTree = ""; }; 34 | 1C2E2F7421062A79003CFD58 /* InterceptedRequestHandlerRegistrable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterceptedRequestHandlerRegistrable.swift; sourceTree = ""; }; 35 | 1C36592E2129CE1D0040A19B /* AnyHttpRequestEvaluator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyHttpRequestEvaluator.swift; sourceTree = ""; }; 36 | 1C3659302129D26A0040A19B /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 37 | 1C831DB720B9A3AE003648C6 /* NetworkInterceptor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NetworkInterceptor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 1C831DBA20B9A3AE003648C6 /* NetworkInterceptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NetworkInterceptor.h; sourceTree = ""; }; 39 | 1C831DBB20B9A3AE003648C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 1C831DC520B9A420003648C6 /* NetworkInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkInterceptor.swift; sourceTree = ""; }; 41 | 1C831DC820B9A706003648C6 /* NetworkRequestInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkRequestInterceptor.swift; sourceTree = ""; }; 42 | 1C831DCD20B9AA03003648C6 /* SlackSniffableRequestHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlackSniffableRequestHandler.swift; sourceTree = ""; }; 43 | 1CAD80CB21328D5600CA2160 /* DomainHttpRequestEvaluator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainHttpRequestEvaluator.swift; sourceTree = ""; }; 44 | 1CAD80CD21328E9400CA2160 /* AlternateDomainSniffableRequestHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlternateDomainSniffableRequestHandler.swift; sourceTree = ""; }; 45 | 1CAD80CF2132947500CA2160 /* NetworkRequestRedirectHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkRequestRedirectHandler.swift; sourceTree = ""; }; 46 | 1CAD80D121329B7100CA2160 /* NetworkRedirectUrlProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkRedirectUrlProtocol.swift; sourceTree = ""; }; 47 | 1CAD80D421329BE900CA2160 /* NetworkRequestSniffableUrlProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkRequestSniffableUrlProtocol.swift; sourceTree = ""; }; 48 | 1CAD80D72132A7DC00CA2160 /* AlternateDomainRequestRedirector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlternateDomainRequestRedirector.swift; sourceTree = ""; }; 49 | 1CAD80D92133042400CA2160 /* AlternateUrlRequestRedirector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlternateUrlRequestRedirector.swift; sourceTree = ""; }; 50 | 3C737E0A347E83ED712E90F0 /* Pods_NetworkInterceptor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NetworkInterceptor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 56F02821227165B400137037 /* SlackHookSniffableRequestHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlackHookSniffableRequestHandler.swift; sourceTree = ""; }; 52 | 7CA727150E0B1AD1D8F1EAED /* Pods-NetworkInterceptor.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NetworkInterceptor.debug.xcconfig"; path = "Pods/Target Support Files/Pods-NetworkInterceptor/Pods-NetworkInterceptor.debug.xcconfig"; sourceTree = ""; }; 53 | E75255131FB11CF5B99D8E6F /* Pods-NetworkInterceptor.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NetworkInterceptor.release.xcconfig"; path = "Pods/Target Support Files/Pods-NetworkInterceptor/Pods-NetworkInterceptor.release.xcconfig"; sourceTree = ""; }; 54 | /* End PBXFileReference section */ 55 | 56 | /* Begin PBXFrameworksBuildPhase section */ 57 | 1C831DB320B9A3AE003648C6 /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | 03915A41CCCF5103124B33E6 /* Pods_NetworkInterceptor.framework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | 1C19A2A7216A0CEF0041F5BE /* Helpers */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 1C19A2A8216A0D140041F5BE /* URLRequestFactory.swift */, 72 | ); 73 | path = Helpers; 74 | sourceTree = ""; 75 | }; 76 | 1C831DAD20B9A3AE003648C6 = { 77 | isa = PBXGroup; 78 | children = ( 79 | 1C3659302129D26A0040A19B /* README.md */, 80 | 1C831DB920B9A3AE003648C6 /* NetworkInterceptor */, 81 | 1C831DB820B9A3AE003648C6 /* Products */, 82 | DBD58177650D2CBC63592ED2 /* Pods */, 83 | 5C139292E477F0FBD0A43FDF /* Frameworks */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | 1C831DB820B9A3AE003648C6 /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 1C831DB720B9A3AE003648C6 /* NetworkInterceptor.framework */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | 1C831DB920B9A3AE003648C6 /* NetworkInterceptor */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 1C831DC220B9A3C8003648C6 /* Source */, 99 | 1C831DBA20B9A3AE003648C6 /* NetworkInterceptor.h */, 100 | 1C831DBB20B9A3AE003648C6 /* Info.plist */, 101 | ); 102 | path = NetworkInterceptor; 103 | sourceTree = ""; 104 | }; 105 | 1C831DC220B9A3C8003648C6 /* Source */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 1C831DC520B9A420003648C6 /* NetworkInterceptor.swift */, 109 | 1C2E2F2720F4783C003CFD58 /* NetworkInterceptorConfig.swift */, 110 | 1C831DC820B9A706003648C6 /* NetworkRequestInterceptor.swift */, 111 | 1CAD80CF2132947500CA2160 /* NetworkRequestRedirectHandler.swift */, 112 | 1C2E2F7421062A79003CFD58 /* InterceptedRequestHandlerRegistrable.swift */, 113 | 1CAD80D321329BCA00CA2160 /* URLProtocol */, 114 | 1C831DC720B9A6DF003648C6 /* RequestEvaluator */, 115 | 1C831DCC20B9A9D2003648C6 /* SniffableRequestHandler */, 116 | 1CAD80D62132A7C600CA2160 /* RequestRedirector */, 117 | 1C19A2A7216A0CEF0041F5BE /* Helpers */, 118 | ); 119 | path = Source; 120 | sourceTree = ""; 121 | }; 122 | 1C831DC720B9A6DF003648C6 /* RequestEvaluator */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 1C36592E2129CE1D0040A19B /* AnyHttpRequestEvaluator.swift */, 126 | 1CAD80CB21328D5600CA2160 /* DomainHttpRequestEvaluator.swift */, 127 | ); 128 | path = RequestEvaluator; 129 | sourceTree = ""; 130 | }; 131 | 1C831DCC20B9A9D2003648C6 /* SniffableRequestHandler */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 1C831DCD20B9AA03003648C6 /* SlackSniffableRequestHandler.swift */, 135 | 1C2E2F2920F47B2E003CFD58 /* ConsoleLoggerSniffableRequestHandler.swift */, 136 | 1CAD80CD21328E9400CA2160 /* AlternateDomainSniffableRequestHandler.swift */, 137 | 56F02821227165B400137037 /* SlackHookSniffableRequestHandler.swift */, 138 | ); 139 | path = SniffableRequestHandler; 140 | sourceTree = ""; 141 | }; 142 | 1CAD80D321329BCA00CA2160 /* URLProtocol */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 1CAD80D421329BE900CA2160 /* NetworkRequestSniffableUrlProtocol.swift */, 146 | 1CAD80D121329B7100CA2160 /* NetworkRedirectUrlProtocol.swift */, 147 | ); 148 | path = URLProtocol; 149 | sourceTree = ""; 150 | }; 151 | 1CAD80D62132A7C600CA2160 /* RequestRedirector */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 1CAD80D72132A7DC00CA2160 /* AlternateDomainRequestRedirector.swift */, 155 | 1CAD80D92133042400CA2160 /* AlternateUrlRequestRedirector.swift */, 156 | ); 157 | path = RequestRedirector; 158 | sourceTree = ""; 159 | }; 160 | 5C139292E477F0FBD0A43FDF /* Frameworks */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 3C737E0A347E83ED712E90F0 /* Pods_NetworkInterceptor.framework */, 164 | ); 165 | name = Frameworks; 166 | sourceTree = ""; 167 | }; 168 | DBD58177650D2CBC63592ED2 /* Pods */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 7CA727150E0B1AD1D8F1EAED /* Pods-NetworkInterceptor.debug.xcconfig */, 172 | E75255131FB11CF5B99D8E6F /* Pods-NetworkInterceptor.release.xcconfig */, 173 | ); 174 | name = Pods; 175 | sourceTree = ""; 176 | }; 177 | /* End PBXGroup section */ 178 | 179 | /* Begin PBXHeadersBuildPhase section */ 180 | 1C831DB420B9A3AE003648C6 /* Headers */ = { 181 | isa = PBXHeadersBuildPhase; 182 | buildActionMask = 2147483647; 183 | files = ( 184 | 1C831DBC20B9A3AE003648C6 /* NetworkInterceptor.h in Headers */, 185 | ); 186 | runOnlyForDeploymentPostprocessing = 0; 187 | }; 188 | /* End PBXHeadersBuildPhase section */ 189 | 190 | /* Begin PBXNativeTarget section */ 191 | 1C831DB620B9A3AE003648C6 /* NetworkInterceptor */ = { 192 | isa = PBXNativeTarget; 193 | buildConfigurationList = 1C831DBF20B9A3AE003648C6 /* Build configuration list for PBXNativeTarget "NetworkInterceptor" */; 194 | buildPhases = ( 195 | B72A005A4D84655B64382A3D /* [CP] Check Pods Manifest.lock */, 196 | 1C831DB220B9A3AE003648C6 /* Sources */, 197 | 1C831DB320B9A3AE003648C6 /* Frameworks */, 198 | 1C831DB420B9A3AE003648C6 /* Headers */, 199 | 1C831DB520B9A3AE003648C6 /* Resources */, 200 | ); 201 | buildRules = ( 202 | ); 203 | dependencies = ( 204 | ); 205 | name = NetworkInterceptor; 206 | productName = NetworkInterceptor; 207 | productReference = 1C831DB720B9A3AE003648C6 /* NetworkInterceptor.framework */; 208 | productType = "com.apple.product-type.framework"; 209 | }; 210 | /* End PBXNativeTarget section */ 211 | 212 | /* Begin PBXProject section */ 213 | 1C831DAE20B9A3AE003648C6 /* Project object */ = { 214 | isa = PBXProject; 215 | attributes = { 216 | LastUpgradeCheck = 0930; 217 | ORGANIZATIONNAME = "Kenneth Poon"; 218 | TargetAttributes = { 219 | 1C831DB620B9A3AE003648C6 = { 220 | CreatedOnToolsVersion = 9.3; 221 | LastSwiftMigration = 0930; 222 | }; 223 | }; 224 | }; 225 | buildConfigurationList = 1C831DB120B9A3AE003648C6 /* Build configuration list for PBXProject "NetworkInterceptor" */; 226 | compatibilityVersion = "Xcode 9.3"; 227 | developmentRegion = en; 228 | hasScannedForEncodings = 0; 229 | knownRegions = ( 230 | en, 231 | ); 232 | mainGroup = 1C831DAD20B9A3AE003648C6; 233 | productRefGroup = 1C831DB820B9A3AE003648C6 /* Products */; 234 | projectDirPath = ""; 235 | projectRoot = ""; 236 | targets = ( 237 | 1C831DB620B9A3AE003648C6 /* NetworkInterceptor */, 238 | ); 239 | }; 240 | /* End PBXProject section */ 241 | 242 | /* Begin PBXResourcesBuildPhase section */ 243 | 1C831DB520B9A3AE003648C6 /* Resources */ = { 244 | isa = PBXResourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | }; 250 | /* End PBXResourcesBuildPhase section */ 251 | 252 | /* Begin PBXShellScriptBuildPhase section */ 253 | B72A005A4D84655B64382A3D /* [CP] Check Pods Manifest.lock */ = { 254 | isa = PBXShellScriptBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | ); 258 | inputPaths = ( 259 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 260 | "${PODS_ROOT}/Manifest.lock", 261 | ); 262 | name = "[CP] Check Pods Manifest.lock"; 263 | outputPaths = ( 264 | "$(DERIVED_FILE_DIR)/Pods-NetworkInterceptor-checkManifestLockResult.txt", 265 | ); 266 | runOnlyForDeploymentPostprocessing = 0; 267 | shellPath = /bin/sh; 268 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 269 | showEnvVarsInLog = 0; 270 | }; 271 | /* End PBXShellScriptBuildPhase section */ 272 | 273 | /* Begin PBXSourcesBuildPhase section */ 274 | 1C831DB220B9A3AE003648C6 /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | 1C2E2F7521062A79003CFD58 /* InterceptedRequestHandlerRegistrable.swift in Sources */, 279 | 56F02822227165B400137037 /* SlackHookSniffableRequestHandler.swift in Sources */, 280 | 1C831DC620B9A420003648C6 /* NetworkInterceptor.swift in Sources */, 281 | 1C36592F2129CE1D0040A19B /* AnyHttpRequestEvaluator.swift in Sources */, 282 | 1CAD80D02132947500CA2160 /* NetworkRequestRedirectHandler.swift in Sources */, 283 | 1CAD80CE21328E9400CA2160 /* AlternateDomainSniffableRequestHandler.swift in Sources */, 284 | 1C19A2A9216A0D140041F5BE /* URLRequestFactory.swift in Sources */, 285 | 1CAD80DA2133042400CA2160 /* AlternateUrlRequestRedirector.swift in Sources */, 286 | 1C2E2F2820F4783C003CFD58 /* NetworkInterceptorConfig.swift in Sources */, 287 | 1CAD80D521329BE900CA2160 /* NetworkRequestSniffableUrlProtocol.swift in Sources */, 288 | 1C831DC920B9A706003648C6 /* NetworkRequestInterceptor.swift in Sources */, 289 | 1CAD80D221329B7100CA2160 /* NetworkRedirectUrlProtocol.swift in Sources */, 290 | 1C2E2F2A20F47B2E003CFD58 /* ConsoleLoggerSniffableRequestHandler.swift in Sources */, 291 | 1CAD80D82132A7DC00CA2160 /* AlternateDomainRequestRedirector.swift in Sources */, 292 | 1CAD80CC21328D5600CA2160 /* DomainHttpRequestEvaluator.swift in Sources */, 293 | 1C831DCE20B9AA03003648C6 /* SlackSniffableRequestHandler.swift in Sources */, 294 | ); 295 | runOnlyForDeploymentPostprocessing = 0; 296 | }; 297 | /* End PBXSourcesBuildPhase section */ 298 | 299 | /* Begin XCBuildConfiguration section */ 300 | 1C831DBD20B9A3AE003648C6 /* Debug */ = { 301 | isa = XCBuildConfiguration; 302 | buildSettings = { 303 | ALWAYS_SEARCH_USER_PATHS = NO; 304 | CLANG_ANALYZER_NONNULL = YES; 305 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 306 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 307 | CLANG_CXX_LIBRARY = "libc++"; 308 | CLANG_ENABLE_MODULES = YES; 309 | CLANG_ENABLE_OBJC_ARC = YES; 310 | CLANG_ENABLE_OBJC_WEAK = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INFINITE_RECURSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 324 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 326 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 327 | CLANG_WARN_STRICT_PROTOTYPES = YES; 328 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 329 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 330 | CLANG_WARN_UNREACHABLE_CODE = YES; 331 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 332 | CODE_SIGN_IDENTITY = "iPhone Developer"; 333 | COPY_PHASE_STRIP = NO; 334 | CURRENT_PROJECT_VERSION = 1; 335 | DEBUG_INFORMATION_FORMAT = dwarf; 336 | ENABLE_STRICT_OBJC_MSGSEND = YES; 337 | ENABLE_TESTABILITY = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu11; 339 | GCC_DYNAMIC_NO_PIC = NO; 340 | GCC_NO_COMMON_BLOCKS = YES; 341 | GCC_OPTIMIZATION_LEVEL = 0; 342 | GCC_PREPROCESSOR_DEFINITIONS = ( 343 | "DEBUG=1", 344 | "$(inherited)", 345 | ); 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 353 | MTL_ENABLE_DEBUG_INFO = YES; 354 | ONLY_ACTIVE_ARCH = YES; 355 | SDKROOT = iphoneos; 356 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 357 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 358 | VERSIONING_SYSTEM = "apple-generic"; 359 | VERSION_INFO_PREFIX = ""; 360 | }; 361 | name = Debug; 362 | }; 363 | 1C831DBE20B9A3AE003648C6 /* Release */ = { 364 | isa = XCBuildConfiguration; 365 | buildSettings = { 366 | ALWAYS_SEARCH_USER_PATHS = NO; 367 | CLANG_ANALYZER_NONNULL = YES; 368 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 370 | CLANG_CXX_LIBRARY = "libc++"; 371 | CLANG_ENABLE_MODULES = YES; 372 | CLANG_ENABLE_OBJC_ARC = YES; 373 | CLANG_ENABLE_OBJC_WEAK = YES; 374 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 375 | CLANG_WARN_BOOL_CONVERSION = YES; 376 | CLANG_WARN_COMMA = YES; 377 | CLANG_WARN_CONSTANT_CONVERSION = YES; 378 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 379 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 380 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 381 | CLANG_WARN_EMPTY_BODY = YES; 382 | CLANG_WARN_ENUM_CONVERSION = YES; 383 | CLANG_WARN_INFINITE_RECURSION = YES; 384 | CLANG_WARN_INT_CONVERSION = YES; 385 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 387 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 388 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 389 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 390 | CLANG_WARN_STRICT_PROTOTYPES = YES; 391 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 392 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 393 | CLANG_WARN_UNREACHABLE_CODE = YES; 394 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 395 | CODE_SIGN_IDENTITY = "iPhone Developer"; 396 | COPY_PHASE_STRIP = NO; 397 | CURRENT_PROJECT_VERSION = 1; 398 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 399 | ENABLE_NS_ASSERTIONS = NO; 400 | ENABLE_STRICT_OBJC_MSGSEND = YES; 401 | GCC_C_LANGUAGE_STANDARD = gnu11; 402 | GCC_NO_COMMON_BLOCKS = YES; 403 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 404 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 405 | GCC_WARN_UNDECLARED_SELECTOR = YES; 406 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 407 | GCC_WARN_UNUSED_FUNCTION = YES; 408 | GCC_WARN_UNUSED_VARIABLE = YES; 409 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 410 | MTL_ENABLE_DEBUG_INFO = NO; 411 | SDKROOT = iphoneos; 412 | SWIFT_COMPILATION_MODE = wholemodule; 413 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 414 | VALIDATE_PRODUCT = YES; 415 | VERSIONING_SYSTEM = "apple-generic"; 416 | VERSION_INFO_PREFIX = ""; 417 | }; 418 | name = Release; 419 | }; 420 | 1C831DC020B9A3AE003648C6 /* Debug */ = { 421 | isa = XCBuildConfiguration; 422 | baseConfigurationReference = 7CA727150E0B1AD1D8F1EAED /* Pods-NetworkInterceptor.debug.xcconfig */; 423 | buildSettings = { 424 | CLANG_ENABLE_MODULES = YES; 425 | CODE_SIGN_IDENTITY = ""; 426 | CODE_SIGN_STYLE = Automatic; 427 | DEFINES_MODULE = YES; 428 | DYLIB_COMPATIBILITY_VERSION = 1; 429 | DYLIB_CURRENT_VERSION = 1; 430 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 431 | INFOPLIST_FILE = NetworkInterceptor/Info.plist; 432 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 433 | LD_RUNPATH_SEARCH_PATHS = ( 434 | "$(inherited)", 435 | "@executable_path/Frameworks", 436 | "@loader_path/Frameworks", 437 | ); 438 | PRODUCT_BUNDLE_IDENTIFIER = com.depoon.NetworkInterceptor; 439 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 440 | SKIP_INSTALL = YES; 441 | SWIFT_INSTALL_OBJC_HEADER = NO; 442 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 443 | SWIFT_VERSION = 4.0; 444 | TARGETED_DEVICE_FAMILY = "1,2"; 445 | }; 446 | name = Debug; 447 | }; 448 | 1C831DC120B9A3AE003648C6 /* Release */ = { 449 | isa = XCBuildConfiguration; 450 | baseConfigurationReference = E75255131FB11CF5B99D8E6F /* Pods-NetworkInterceptor.release.xcconfig */; 451 | buildSettings = { 452 | CLANG_ENABLE_MODULES = YES; 453 | CODE_SIGN_IDENTITY = ""; 454 | CODE_SIGN_STYLE = Automatic; 455 | DEFINES_MODULE = YES; 456 | DYLIB_COMPATIBILITY_VERSION = 1; 457 | DYLIB_CURRENT_VERSION = 1; 458 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 459 | INFOPLIST_FILE = NetworkInterceptor/Info.plist; 460 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 461 | LD_RUNPATH_SEARCH_PATHS = ( 462 | "$(inherited)", 463 | "@executable_path/Frameworks", 464 | "@loader_path/Frameworks", 465 | ); 466 | PRODUCT_BUNDLE_IDENTIFIER = com.depoon.NetworkInterceptor; 467 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 468 | SKIP_INSTALL = YES; 469 | SWIFT_INSTALL_OBJC_HEADER = NO; 470 | SWIFT_VERSION = 4.0; 471 | TARGETED_DEVICE_FAMILY = "1,2"; 472 | }; 473 | name = Release; 474 | }; 475 | /* End XCBuildConfiguration section */ 476 | 477 | /* Begin XCConfigurationList section */ 478 | 1C831DB120B9A3AE003648C6 /* Build configuration list for PBXProject "NetworkInterceptor" */ = { 479 | isa = XCConfigurationList; 480 | buildConfigurations = ( 481 | 1C831DBD20B9A3AE003648C6 /* Debug */, 482 | 1C831DBE20B9A3AE003648C6 /* Release */, 483 | ); 484 | defaultConfigurationIsVisible = 0; 485 | defaultConfigurationName = Release; 486 | }; 487 | 1C831DBF20B9A3AE003648C6 /* Build configuration list for PBXNativeTarget "NetworkInterceptor" */ = { 488 | isa = XCConfigurationList; 489 | buildConfigurations = ( 490 | 1C831DC020B9A3AE003648C6 /* Debug */, 491 | 1C831DC120B9A3AE003648C6 /* Release */, 492 | ); 493 | defaultConfigurationIsVisible = 0; 494 | defaultConfigurationName = Release; 495 | }; 496 | /* End XCConfigurationList section */ 497 | }; 498 | rootObject = 1C831DAE20B9A3AE003648C6 /* Project object */; 499 | } 500 | -------------------------------------------------------------------------------- /NetworkInterceptor.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NetworkInterceptor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NetworkInterceptor.xcodeproj/xcshareddata/xcschemes/NetworkInterceptor.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /NetworkInterceptor.xcodeproj/xcuserdata/kennethpoon.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | NetworkInterceptor.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 1C831DB620B9A3AE003648C6 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /NetworkInterceptor.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /NetworkInterceptor.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NetworkInterceptor.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NetworkInterceptor.xcworkspace/xcuserdata/kennethpoon.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | EnabledFullIndexStoreVisibility 12 | 13 | IssueFilterStyle 14 | ShowActiveSchemeOnly 15 | LiveSourceIssuesEnabled 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /NetworkInterceptor.xcworkspace/xcuserdata/kennethpoon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /NetworkInterceptor/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /NetworkInterceptor/NetworkInterceptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkInterceptor.h 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/5/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for NetworkInterceptor. 12 | FOUNDATION_EXPORT double NetworkInterceptorVersionNumber; 13 | 14 | //! Project version string for NetworkInterceptor. 15 | FOUNDATION_EXPORT const unsigned char NetworkInterceptorVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/Helpers/URLRequestFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLRequestFactory.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 7/10/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import URLRequest_cURL 11 | 12 | class URLRequestFactory { 13 | 14 | public func createURLRequest(originalUrlRequest: URLRequest, url: URL) -> URLRequest { 15 | var urlString = "\(url.absoluteString)\(originalUrlRequest.url!.path)" 16 | if let query = originalUrlRequest.url?.query { 17 | urlString = "\(urlString)?\(query)" 18 | } 19 | var redirectedRequest = URLRequest(url: URL(string: urlString)!) 20 | if let _ = originalUrlRequest.httpBodyStream, 21 | let httpBodyStreamData = originalUrlRequest.getHttpBodyStreamData() { 22 | redirectedRequest.httpBody = httpBodyStreamData 23 | } else { 24 | redirectedRequest.httpBody = originalUrlRequest.httpBody 25 | } 26 | redirectedRequest.httpMethod = originalUrlRequest.httpMethod! 27 | redirectedRequest.allHTTPHeaderFields = originalUrlRequest.allHTTPHeaderFields 28 | redirectedRequest.cachePolicy = originalUrlRequest.cachePolicy 29 | return redirectedRequest 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/InterceptedRequestHandlerRegistrable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InterceptedRequestHandlerRegistrable.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 23/7/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum SniffableRequestHandlerRegistrable { 12 | case console(logginMode: ConsoleLoggingMode) 13 | case slack(slackToken: String, channel: String, username: String) 14 | case alternateDomain(domainURL: URL) 15 | case slackHook(hooksUrl: String) 16 | 17 | public func requestHandler() -> SniffableRequestHandler { 18 | switch self { 19 | case .console(let loggingMode): 20 | return ConsoleLoggerSniffableRequestHandler(loggingMode: loggingMode) 21 | case .slack(let slackToken, let channel, let username): 22 | return SlackSniffableRequestHandler(slackToken: slackToken, channel: channel, username: username) 23 | case .alternateDomain(let domainURL): 24 | return AlternateDomainSniffableRequestHandler(domainURL: domainURL) 25 | case .slackHook(let hookUrl): 26 | return SlackHookSniffableRequestHandler(hookUrl: hookUrl) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/NetworkInterceptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkInterceptor.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/5/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol RequestRefirer { 12 | func refireURLRequest(urlRequest: URLRequest) 13 | } 14 | 15 | public protocol RequestEvaluator: class { 16 | func isActionAllowed(urlRequest: URLRequest) -> Bool 17 | } 18 | 19 | public protocol SniffableRequestHandler { 20 | func sniffRequest(urlRequest: URLRequest) 21 | } 22 | 23 | public protocol RedirectableRequestHandler { 24 | func redirectedRequest(originalUrlRequest: URLRequest) -> URLRequest 25 | } 26 | 27 | public struct RequestSniffer { 28 | public let requestEvaluator: RequestEvaluator 29 | public let handlers: [SniffableRequestHandler] 30 | public init(requestEvaluator: RequestEvaluator, handlers: [SniffableRequestHandler]) { 31 | self.requestEvaluator = requestEvaluator 32 | self.handlers = handlers 33 | } 34 | } 35 | 36 | public struct RequestRedirector { 37 | public let requestEvaluator: RequestEvaluator 38 | public let redirectableRequestHandler: RedirectableRequestHandler 39 | public init(requestEvaluator: RequestEvaluator, redirectableRequestHandler: RedirectableRequestHandler) { 40 | self.requestEvaluator = requestEvaluator 41 | self.redirectableRequestHandler = redirectableRequestHandler 42 | } 43 | } 44 | 45 | @objc public class NetworkInterceptor: NSObject { 46 | 47 | @objc public static let shared = NetworkInterceptor() 48 | let networkRequestInterceptor = NetworkRequestInterceptor() 49 | var config: NetworkInterceptorConfig? 50 | 51 | public func setup(config: NetworkInterceptorConfig){ 52 | self.config = config 53 | } 54 | 55 | @objc public func startRecording(){ 56 | self.networkRequestInterceptor.startRecording() 57 | } 58 | 59 | @objc public func stopRecording(){ 60 | self.networkRequestInterceptor.stopRecording() 61 | } 62 | 63 | func sniffRequest(urlRequest: URLRequest){ 64 | guard let config = self.config else { 65 | return 66 | } 67 | for sniffer in config.requestSniffers { 68 | if sniffer.requestEvaluator.isActionAllowed(urlRequest: urlRequest) { 69 | for handler in sniffer.handlers { 70 | handler.sniffRequest(urlRequest: urlRequest) 71 | } 72 | } 73 | } 74 | } 75 | 76 | func isRequestRedirectable(urlRequest: URLRequest) -> Bool { 77 | guard let config = self.config else { 78 | return false 79 | } 80 | for redirector in config.requestRedirectors { 81 | if redirector.requestEvaluator.isActionAllowed(urlRequest: urlRequest) { 82 | return true 83 | } 84 | } 85 | return false 86 | } 87 | 88 | func redirectedRequest(urlRequest: URLRequest) -> URLRequest? { 89 | guard let config = self.config else { 90 | return nil 91 | } 92 | for redirector in config.requestRedirectors { 93 | if redirector.requestEvaluator.isActionAllowed(urlRequest: urlRequest) { 94 | return redirector.redirectableRequestHandler.redirectedRequest(originalUrlRequest: urlRequest) 95 | } 96 | } 97 | return nil 98 | } 99 | 100 | } 101 | 102 | extension NetworkInterceptor: RequestRefirer { 103 | func refireURLRequest(urlRequest: URLRequest) { 104 | var request = urlRequest 105 | request.addValue("true", forHTTPHeaderField: "Refired") 106 | let task = URLSession.shared.dataTask(with: request as URLRequest) { (data: Data?, response: URLResponse?, error: Error?) in 107 | guard let data = data else { 108 | return 109 | } 110 | do { 111 | _ = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String:Any] 112 | } catch _ as NSError { 113 | } 114 | if error != nil{ 115 | return 116 | } 117 | } 118 | task.resume() 119 | 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/NetworkInterceptorConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkInterceptorConfig.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 10/7/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct NetworkInterceptorConfig { 12 | let requestSniffers: [RequestSniffer] 13 | let requestRedirectors: [RequestRedirector] 14 | 15 | public init(requestSniffers: [RequestSniffer] = [], 16 | requestRedirectors: [RequestRedirector] = []){ 17 | self.requestSniffers = requestSniffers 18 | self.requestRedirectors = requestRedirectors 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/NetworkRequestInterceptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkRequestInterceptor.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/5/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | @objc public class NetworkRequestInterceptor: NSObject{ 13 | 14 | func swizzleProtocolClasses(){ 15 | let instance = URLSessionConfiguration.default 16 | let uRLSessionConfigurationClass: AnyClass = object_getClass(instance)! 17 | 18 | let method1: Method = class_getInstanceMethod(uRLSessionConfigurationClass, #selector(getter: uRLSessionConfigurationClass.protocolClasses))! 19 | let method2: Method = class_getInstanceMethod(URLSessionConfiguration.self, #selector(URLSessionConfiguration.fakeProcotolClasses))! 20 | 21 | method_exchangeImplementations(method1, method2) 22 | } 23 | 24 | public func startRecording() { 25 | URLProtocol.registerClass(NetworkRedirectUrlProtocol.self) 26 | URLProtocol.registerClass(NetworkRequestSniffableUrlProtocol.self) 27 | swizzleProtocolClasses() 28 | } 29 | 30 | public func stopRecording() { 31 | URLProtocol.unregisterClass(NetworkRedirectUrlProtocol.self) 32 | URLProtocol.unregisterClass(NetworkRequestSniffableUrlProtocol.self) 33 | swizzleProtocolClasses() 34 | } 35 | } 36 | 37 | extension URLSessionConfiguration { 38 | 39 | @objc func fakeProcotolClasses() -> [AnyClass]? { 40 | // return [NetworkRedirectUrlProtocol.self] 41 | guard let fakeProcotolClasses = self.fakeProcotolClasses() else { 42 | return [] 43 | } 44 | var originalProtocolClasses = fakeProcotolClasses.filter { 45 | return $0 != NetworkRequestSniffableUrlProtocol.self && $0 != NetworkRedirectUrlProtocol.self 46 | } 47 | originalProtocolClasses.insert(NetworkRequestSniffableUrlProtocol.self, at: 0) 48 | originalProtocolClasses.insert(NetworkRedirectUrlProtocol.self, at: 0) 49 | return originalProtocolClasses 50 | } 51 | 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/NetworkRequestRedirectHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkRequestRedirectHandler.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/8/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class NetworkRequestRedirectHandler: NSObject { 12 | var urlProtocol: URLProtocol? 13 | var urlSession: URLSession? 14 | var urlSessionTask: URLSessionTask? 15 | 16 | 17 | 18 | func startLoading(request: URLRequest, urlProtocol: URLProtocol){ 19 | self.urlProtocol = urlProtocol 20 | 21 | urlSession = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil) 22 | urlSessionTask = urlSession?.dataTask(with: request as URLRequest, completionHandler: { [weak self] (data, response, error) in 23 | 24 | guard let handlingProtocol = self?.urlProtocol, let client = handlingProtocol.client else { 25 | return 26 | } 27 | 28 | if let error = error { 29 | client.urlProtocol(handlingProtocol, didFailWithError: error) 30 | return 31 | } 32 | 33 | client.urlProtocol(handlingProtocol, didReceive: response!, cacheStoragePolicy: .allowed) 34 | client.urlProtocol(handlingProtocol, didLoad: data!) 35 | client.urlProtocolDidFinishLoading(handlingProtocol) 36 | }) 37 | 38 | self.urlSessionTask?.resume() 39 | } 40 | } 41 | 42 | extension NetworkRequestRedirectHandler: URLSessionDataDelegate { 43 | public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 44 | guard let handlingProtocol = self.urlProtocol, let client = handlingProtocol.client else { 45 | return 46 | } 47 | if let error = error { 48 | client.urlProtocol(handlingProtocol, didFailWithError: error) 49 | return 50 | } 51 | client.urlProtocolDidFinishLoading(handlingProtocol) 52 | } 53 | 54 | public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { 55 | guard let handlingProtocol = self.urlProtocol, let client = handlingProtocol.client else { 56 | return 57 | } 58 | client.urlProtocol(handlingProtocol, didReceive: response, cacheStoragePolicy: .notAllowed) 59 | completionHandler(.allow) 60 | } 61 | 62 | public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { 63 | guard let handlingProtocol = self.urlProtocol, let client = handlingProtocol.client else { 64 | return 65 | } 66 | client.urlProtocol(handlingProtocol, didLoad: data) 67 | } 68 | 69 | public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { 70 | guard let handlingProtocol = self.urlProtocol, let client = handlingProtocol.client else { 71 | return 72 | } 73 | client.urlProtocol(handlingProtocol, wasRedirectedTo: request, redirectResponse: response) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/RequestEvaluator/AnyHttpRequestEvaluator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnyHttpRequestEvaluator.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 20/8/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class AnyHttpRequestEvaluator: RequestEvaluator { 12 | 13 | public init(){} 14 | 15 | public func isActionAllowed(urlRequest: URLRequest) -> Bool { 16 | guard let scheme = urlRequest.url?.scheme else { 17 | return false 18 | } 19 | if ["https", "http"].contains(scheme) { 20 | return true 21 | } 22 | return false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/RequestEvaluator/DomainHttpRequestEvaluator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DomainHttpRequestEvaluator.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/8/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class DomainHttpRequestEvaluator: RequestEvaluator { 12 | let domain: String 13 | public init(domain: String){ 14 | self.domain = domain 15 | } 16 | 17 | public func isActionAllowed(urlRequest: URLRequest) -> Bool { 18 | guard AnyHttpRequestEvaluator().isActionAllowed(urlRequest: urlRequest) else { 19 | return false 20 | } 21 | guard let host = urlRequest.url?.host else { 22 | return false 23 | } 24 | if host == self.domain { 25 | return true 26 | } 27 | return false 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/RequestRedirector/AlternateDomainRequestRedirector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlternateDomainRequestRedirector.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/8/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class AlternateDomainRequestRedirector: RedirectableRequestHandler { 12 | 13 | let domainURL: URL 14 | 15 | public init(domainURL: URL){ 16 | self.domainURL = domainURL 17 | } 18 | 19 | public func redirectedRequest(originalUrlRequest: URLRequest) -> URLRequest { 20 | let redirectedRequest = URLRequestFactory().createURLRequest(originalUrlRequest: originalUrlRequest, url: self.domainURL) 21 | return redirectedRequest 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/RequestRedirector/AlternateUrlRequestRedirector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlternateUrlRequestRedirector.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/8/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class AlternateUrlRequestRedirector: RedirectableRequestHandler { 12 | 13 | let url: URL 14 | 15 | public init(url: URL){ 16 | self.url = url 17 | } 18 | 19 | public func redirectedRequest(originalUrlRequest: URLRequest) -> URLRequest { 20 | let mutableRequest = (originalUrlRequest as NSURLRequest).mutableCopy() as! NSMutableURLRequest 21 | mutableRequest.url = self.url 22 | return mutableRequest as URLRequest 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/SniffableRequestHandler/AlternateDomainSniffableRequestHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlternateDomainSniffableRequestHandler.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/8/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class AlternateDomainSniffableRequestHandler: SniffableRequestHandler { 12 | 13 | let domainURL: URL 14 | 15 | public init(domainURL: URL){ 16 | self.domainURL = domainURL 17 | } 18 | 19 | public func sniffRequest(urlRequest: URLRequest) { 20 | let alternateRequest = URLRequestFactory().createURLRequest(originalUrlRequest: urlRequest, url: self.domainURL) 21 | NetworkInterceptor.shared.refireURLRequest(urlRequest: alternateRequest as URLRequest) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/SniffableRequestHandler/ConsoleLoggerSniffableRequestHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsoleLoggerSniffableRequestHandler.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 10/7/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import URLRequest_cURL 11 | 12 | public enum ConsoleLoggingMode { 13 | case print, nslog 14 | } 15 | 16 | public class ConsoleLoggerSniffableRequestHandler: SniffableRequestHandler { 17 | 18 | let loggingMode: ConsoleLoggingMode 19 | public init(loggingMode: ConsoleLoggingMode){ 20 | self.loggingMode = loggingMode 21 | } 22 | 23 | fileprivate var requestCount: Int = 0 24 | public func sniffRequest(urlRequest: URLRequest) { 25 | requestCount = requestCount + 1 26 | let loggableText = "Request #\(requestCount): CURL => \(urlRequest.cURL)" 27 | switch self.loggingMode { 28 | case .nslog: 29 | NSLog(loggableText) 30 | case .print: 31 | print(loggableText) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/SniffableRequestHandler/SlackHookSniffableRequestHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SlackHookSniffableRequestHandler.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by steven lee on 25/4/19. 6 | // Copyright © 2019 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Foundation 11 | public class SlackHookSniffableRequestHandler: SniffableRequestHandler { 12 | 13 | let hookUrl: String 14 | 15 | init(hookUrl: String){ 16 | self.hookUrl = hookUrl 17 | } 18 | 19 | public func sniffRequest(urlRequest: URLRequest) { 20 | NetworkInterceptor.shared.refireURLRequest(urlRequest: self.generateSlackPayloadFromRequest(originalRequest: urlRequest)) 21 | } 22 | } 23 | 24 | extension SlackHookSniffableRequestHandler { 25 | 26 | fileprivate func generateSlackForwardingRequest() -> NSMutableURLRequest{ 27 | let request = NSMutableURLRequest() 28 | request.url = URL(string: hookUrl) 29 | request.allHTTPHeaderFields = [ 30 | "Content-Type": "application/json", 31 | ] 32 | request.httpMethod = "POST" 33 | return request 34 | } 35 | 36 | fileprivate func generateSlackPayloadFromRequest(originalRequest: URLRequest) -> URLRequest{ 37 | let request = self.generateSlackForwardingRequest() 38 | var json: [String: String] = [:] 39 | 40 | let bundleName = Bundle.main.infoDictionary!["CFBundleName"] as! String 41 | let text: String = originalRequest.cURL 42 | json["text"] = "```[\(bundleName)] \(text)```" 43 | 44 | let jsonData = try? JSONSerialization.data(withJSONObject: json) 45 | request.httpBody = jsonData 46 | return request as URLRequest 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/SniffableRequestHandler/SlackSniffableRequestHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SlackSniffableRequestHandler.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/5/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class SlackSniffableRequestHandler: SniffableRequestHandler { 12 | 13 | let slackToken: String 14 | let channel: String 15 | let username: String 16 | 17 | init(slackToken: String, channel: String, username: String){ 18 | self.slackToken = slackToken 19 | self.channel = channel 20 | self.username = username 21 | } 22 | 23 | public func sniffRequest(urlRequest: URLRequest) { 24 | NetworkInterceptor.shared.refireURLRequest(urlRequest: self.generateSlackPayloadFromRequest(originalRequest: urlRequest)) 25 | } 26 | } 27 | 28 | extension SlackSniffableRequestHandler { 29 | 30 | fileprivate func generateSlackForwardingRequest() -> NSMutableURLRequest{ 31 | let request = NSMutableURLRequest() 32 | request.url = URL(string: "https://slack.com/api/chat.postMessage") 33 | request.allHTTPHeaderFields = [ 34 | "Content-Type": "application/json", 35 | "Authorization": "Bearer \(slackToken)" 36 | ] 37 | request.httpMethod = "POST" 38 | return request 39 | } 40 | 41 | fileprivate func generateForwardingJsonBody() -> [String: String] { 42 | let json: [String: String] = [ 43 | "channel": channel, 44 | "username": username, 45 | "pretty": "1", 46 | ] 47 | return json 48 | } 49 | 50 | fileprivate func generateSlackPayloadFromRequest(originalRequest: URLRequest) -> URLRequest{ 51 | let request = self.generateSlackForwardingRequest() 52 | var json = self.generateForwardingJsonBody() 53 | 54 | let bundleName = Bundle.main.infoDictionary!["CFBundleName"] as! String 55 | let text: String = originalRequest.cURL 56 | json["text"] = "```[\(bundleName)] \(text)```" 57 | 58 | let jsonData = try? JSONSerialization.data(withJSONObject: json) 59 | request.httpBody = jsonData 60 | return request as URLRequest 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/URLProtocol/NetworkRedirectUrlProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkRedirectUrlProtocol.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/8/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class NetworkRedirectUrlProtocol: URLProtocol { 12 | 13 | var session: URLSession? 14 | var sessionTask: URLSessionTask? 15 | 16 | open override class func canInit(with request: URLRequest) -> Bool { 17 | if let httpHeaders = request.allHTTPHeaderFields, let refiredValue = httpHeaders["Redirected"], refiredValue == "true" { 18 | return false 19 | } 20 | return NetworkInterceptor.shared.isRequestRedirectable(urlRequest: request) 21 | } 22 | 23 | open override class func canonicalRequest(for request: URLRequest) -> URLRequest { 24 | let mutableRequest: NSMutableURLRequest = (request as NSURLRequest).mutableCopy() as! NSMutableURLRequest 25 | URLProtocol.setProperty("YES", forKey: "NetworkRedirectUrlProtocol", in: mutableRequest) 26 | return mutableRequest.copy() as! URLRequest 27 | } 28 | 29 | open override func startLoading() { 30 | 31 | guard var redirectedRequest = NetworkInterceptor.shared.redirectedRequest(urlRequest: self.request) else { 32 | return 33 | } 34 | #if DEBUG 35 | NSLog("Redirected Request CURL => \(redirectedRequest.cURL)") 36 | #endif 37 | redirectedRequest.addValue("true", forHTTPHeaderField: "Redirected") 38 | 39 | let config = URLSessionConfiguration.default 40 | config.protocolClasses = [type(of: self)] 41 | 42 | session = URLSession(configuration: config, delegate: self, delegateQueue: nil) 43 | sessionTask = session?.dataTask(with: redirectedRequest, completionHandler: { [weak self] (data, response, error) in 44 | guard let strongSelf = self else { return } 45 | 46 | if let error = error { 47 | strongSelf.client?.urlProtocol(strongSelf, didFailWithError: error) 48 | return 49 | } 50 | 51 | strongSelf.client?.urlProtocol(strongSelf, didReceive: response!, cacheStoragePolicy: .allowed) 52 | strongSelf.client?.urlProtocol(strongSelf, didLoad: data!) 53 | strongSelf.client?.urlProtocolDidFinishLoading(strongSelf) 54 | }) 55 | 56 | sessionTask?.resume() 57 | } 58 | 59 | override public func stopLoading() { 60 | sessionTask?.cancel() 61 | sessionTask = nil 62 | } 63 | } 64 | 65 | extension NetworkRedirectUrlProtocol: URLSessionDataDelegate { 66 | public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 67 | if let error = error { 68 | self.client?.urlProtocol(self, didFailWithError: error) 69 | return 70 | } 71 | self.client?.urlProtocolDidFinishLoading(self) 72 | } 73 | 74 | public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { 75 | self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) 76 | completionHandler(.allow) 77 | } 78 | 79 | public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { 80 | self.client?.urlProtocol(self, didLoad: data) 81 | } 82 | 83 | public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { 84 | self.client?.urlProtocol(self, wasRedirectedTo: request, redirectResponse: response) 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /NetworkInterceptor/Source/URLProtocol/NetworkRequestSniffableUrlProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkRequestSniffableUrlProtocol.swift 3 | // NetworkInterceptor 4 | // 5 | // Created by Kenneth Poon on 26/8/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class NetworkRequestSniffableUrlProtocol: URLProtocol { 12 | 13 | open override class func canInit(with request: URLRequest) -> Bool { 14 | if NetworkInterceptor.shared.isRequestRedirectable(urlRequest: request) { 15 | return false 16 | } 17 | if let httpHeaders = request.allHTTPHeaderFields, httpHeaders.isEmpty { 18 | return false 19 | } 20 | if let httpHeaders = request.allHTTPHeaderFields, let refiredValue = httpHeaders["Refired"], refiredValue == "true" { 21 | return false 22 | } 23 | if let _ = URLProtocol.property(forKey: "NetworkRequestSniffableUrlProtocol", in: request) { 24 | return false 25 | } 26 | NetworkInterceptor.shared.sniffRequest(urlRequest: request) 27 | return false 28 | } 29 | 30 | open override class func canonicalRequest(for request: URLRequest) -> URLRequest { 31 | let mutableRequest: NSMutableURLRequest = (request as NSURLRequest).mutableCopy() as! NSMutableURLRequest 32 | URLProtocol.setProperty("YES", forKey: "NetworkRequestSniffableUrlProtocol", in: mutableRequest) 33 | return mutableRequest.copy() as! URLRequest 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/depoon/NetworkInterceptor/0222c86aa9822bd90e16fc930232f3614ef23883/NetworkInterceptorExample/.DS_Store -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1C2E2F5A20F48285003CFD58 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2E2F5920F48285003CFD58 /* AppDelegate.swift */; }; 11 | 1C2E2F5C20F48285003CFD58 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2E2F5B20F48285003CFD58 /* ViewController.swift */; }; 12 | 1C2E2F5F20F48285003CFD58 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1C2E2F5D20F48285003CFD58 /* Main.storyboard */; }; 13 | 1C2E2F6120F48286003CFD58 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1C2E2F6020F48286003CFD58 /* Assets.xcassets */; }; 14 | 1C2E2F6420F48286003CFD58 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1C2E2F6220F48286003CFD58 /* LaunchScreen.storyboard */; }; 15 | 1C2E2F7320F48337003CFD58 /* NetworkInterceptor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C2E2F7220F48337003CFD58 /* NetworkInterceptor.framework */; }; 16 | 1C2E2F7821062DB9003CFD58 /* CodeInjection.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C2E2F7721062DB9003CFD58 /* CodeInjection.m */; }; 17 | 1C2E2F7A21062E5F003CFD58 /* CodeInjectionSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2E2F7921062E5F003CFD58 /* CodeInjectionSwift.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 1C2E2F5620F48285003CFD58 /* NetworkInterceptorExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NetworkInterceptorExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 1C2E2F5920F48285003CFD58 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23 | 1C2E2F5B20F48285003CFD58 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 24 | 1C2E2F5E20F48285003CFD58 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 25 | 1C2E2F6020F48286003CFD58 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 26 | 1C2E2F6320F48286003CFD58 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 27 | 1C2E2F6520F48286003CFD58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | 1C2E2F7220F48337003CFD58 /* NetworkInterceptor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = NetworkInterceptor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 1C2E2F7621062DB8003CFD58 /* NetworkInterceptorExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NetworkInterceptorExample-Bridging-Header.h"; sourceTree = ""; }; 30 | 1C2E2F7721062DB9003CFD58 /* CodeInjection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CodeInjection.m; sourceTree = ""; }; 31 | 1C2E2F7921062E5F003CFD58 /* CodeInjectionSwift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeInjectionSwift.swift; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | 1C2E2F5320F48285003CFD58 /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | 1C2E2F7320F48337003CFD58 /* NetworkInterceptor.framework in Frameworks */, 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 1C2E2F4D20F48285003CFD58 = { 47 | isa = PBXGroup; 48 | children = ( 49 | 1C2E2F5820F48285003CFD58 /* NetworkInterceptorExample */, 50 | 1C2E2F5720F48285003CFD58 /* Products */, 51 | 1C2E2F7120F48337003CFD58 /* Frameworks */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 1C2E2F5720F48285003CFD58 /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 1C2E2F5620F48285003CFD58 /* NetworkInterceptorExample.app */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 1C2E2F5820F48285003CFD58 /* NetworkInterceptorExample */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 1C2E2F5920F48285003CFD58 /* AppDelegate.swift */, 67 | 1C2E2F5B20F48285003CFD58 /* ViewController.swift */, 68 | 1C2E2F7721062DB9003CFD58 /* CodeInjection.m */, 69 | 1C2E2F7921062E5F003CFD58 /* CodeInjectionSwift.swift */, 70 | 1C2E2F5D20F48285003CFD58 /* Main.storyboard */, 71 | 1C2E2F6020F48286003CFD58 /* Assets.xcassets */, 72 | 1C2E2F6220F48286003CFD58 /* LaunchScreen.storyboard */, 73 | 1C2E2F6520F48286003CFD58 /* Info.plist */, 74 | 1C2E2F7621062DB8003CFD58 /* NetworkInterceptorExample-Bridging-Header.h */, 75 | ); 76 | path = NetworkInterceptorExample; 77 | sourceTree = ""; 78 | }; 79 | 1C2E2F7120F48337003CFD58 /* Frameworks */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 1C2E2F7220F48337003CFD58 /* NetworkInterceptor.framework */, 83 | ); 84 | name = Frameworks; 85 | sourceTree = ""; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXNativeTarget section */ 90 | 1C2E2F5520F48285003CFD58 /* NetworkInterceptorExample */ = { 91 | isa = PBXNativeTarget; 92 | buildConfigurationList = 1C2E2F6820F48286003CFD58 /* Build configuration list for PBXNativeTarget "NetworkInterceptorExample" */; 93 | buildPhases = ( 94 | 1C2E2F5220F48285003CFD58 /* Sources */, 95 | 1C2E2F5320F48285003CFD58 /* Frameworks */, 96 | 1C2E2F5420F48285003CFD58 /* Resources */, 97 | ); 98 | buildRules = ( 99 | ); 100 | dependencies = ( 101 | ); 102 | name = NetworkInterceptorExample; 103 | productName = NetworkInterceptorExample; 104 | productReference = 1C2E2F5620F48285003CFD58 /* NetworkInterceptorExample.app */; 105 | productType = "com.apple.product-type.application"; 106 | }; 107 | /* End PBXNativeTarget section */ 108 | 109 | /* Begin PBXProject section */ 110 | 1C2E2F4E20F48285003CFD58 /* Project object */ = { 111 | isa = PBXProject; 112 | attributes = { 113 | LastSwiftUpdateCheck = 0930; 114 | LastUpgradeCheck = 0930; 115 | ORGANIZATIONNAME = "Kenneth Poon"; 116 | TargetAttributes = { 117 | 1C2E2F5520F48285003CFD58 = { 118 | CreatedOnToolsVersion = 9.3; 119 | LastSwiftMigration = 0930; 120 | }; 121 | }; 122 | }; 123 | buildConfigurationList = 1C2E2F5120F48285003CFD58 /* Build configuration list for PBXProject "NetworkInterceptorExample" */; 124 | compatibilityVersion = "Xcode 9.3"; 125 | developmentRegion = en; 126 | hasScannedForEncodings = 0; 127 | knownRegions = ( 128 | en, 129 | Base, 130 | ); 131 | mainGroup = 1C2E2F4D20F48285003CFD58; 132 | productRefGroup = 1C2E2F5720F48285003CFD58 /* Products */; 133 | projectDirPath = ""; 134 | projectRoot = ""; 135 | targets = ( 136 | 1C2E2F5520F48285003CFD58 /* NetworkInterceptorExample */, 137 | ); 138 | }; 139 | /* End PBXProject section */ 140 | 141 | /* Begin PBXResourcesBuildPhase section */ 142 | 1C2E2F5420F48285003CFD58 /* Resources */ = { 143 | isa = PBXResourcesBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | 1C2E2F6420F48286003CFD58 /* LaunchScreen.storyboard in Resources */, 147 | 1C2E2F6120F48286003CFD58 /* Assets.xcassets in Resources */, 148 | 1C2E2F5F20F48285003CFD58 /* Main.storyboard in Resources */, 149 | ); 150 | runOnlyForDeploymentPostprocessing = 0; 151 | }; 152 | /* End PBXResourcesBuildPhase section */ 153 | 154 | /* Begin PBXSourcesBuildPhase section */ 155 | 1C2E2F5220F48285003CFD58 /* Sources */ = { 156 | isa = PBXSourcesBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | 1C2E2F7821062DB9003CFD58 /* CodeInjection.m in Sources */, 160 | 1C2E2F5C20F48285003CFD58 /* ViewController.swift in Sources */, 161 | 1C2E2F7A21062E5F003CFD58 /* CodeInjectionSwift.swift in Sources */, 162 | 1C2E2F5A20F48285003CFD58 /* AppDelegate.swift in Sources */, 163 | ); 164 | runOnlyForDeploymentPostprocessing = 0; 165 | }; 166 | /* End PBXSourcesBuildPhase section */ 167 | 168 | /* Begin PBXVariantGroup section */ 169 | 1C2E2F5D20F48285003CFD58 /* Main.storyboard */ = { 170 | isa = PBXVariantGroup; 171 | children = ( 172 | 1C2E2F5E20F48285003CFD58 /* Base */, 173 | ); 174 | name = Main.storyboard; 175 | sourceTree = ""; 176 | }; 177 | 1C2E2F6220F48286003CFD58 /* LaunchScreen.storyboard */ = { 178 | isa = PBXVariantGroup; 179 | children = ( 180 | 1C2E2F6320F48286003CFD58 /* Base */, 181 | ); 182 | name = LaunchScreen.storyboard; 183 | sourceTree = ""; 184 | }; 185 | /* End PBXVariantGroup section */ 186 | 187 | /* Begin XCBuildConfiguration section */ 188 | 1C2E2F6620F48286003CFD58 /* Debug */ = { 189 | isa = XCBuildConfiguration; 190 | buildSettings = { 191 | ALWAYS_SEARCH_USER_PATHS = NO; 192 | CLANG_ANALYZER_NONNULL = YES; 193 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 194 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 195 | CLANG_CXX_LIBRARY = "libc++"; 196 | CLANG_ENABLE_MODULES = YES; 197 | CLANG_ENABLE_OBJC_ARC = YES; 198 | CLANG_ENABLE_OBJC_WEAK = YES; 199 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 200 | CLANG_WARN_BOOL_CONVERSION = YES; 201 | CLANG_WARN_COMMA = YES; 202 | CLANG_WARN_CONSTANT_CONVERSION = YES; 203 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 204 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 205 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 206 | CLANG_WARN_EMPTY_BODY = YES; 207 | CLANG_WARN_ENUM_CONVERSION = YES; 208 | CLANG_WARN_INFINITE_RECURSION = YES; 209 | CLANG_WARN_INT_CONVERSION = YES; 210 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 211 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 212 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 213 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 214 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 215 | CLANG_WARN_STRICT_PROTOTYPES = YES; 216 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 217 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 218 | CLANG_WARN_UNREACHABLE_CODE = YES; 219 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 220 | CODE_SIGN_IDENTITY = "iPhone Developer"; 221 | COPY_PHASE_STRIP = NO; 222 | DEBUG_INFORMATION_FORMAT = dwarf; 223 | ENABLE_STRICT_OBJC_MSGSEND = YES; 224 | ENABLE_TESTABILITY = YES; 225 | GCC_C_LANGUAGE_STANDARD = gnu11; 226 | GCC_DYNAMIC_NO_PIC = NO; 227 | GCC_NO_COMMON_BLOCKS = YES; 228 | GCC_OPTIMIZATION_LEVEL = 0; 229 | GCC_PREPROCESSOR_DEFINITIONS = ( 230 | "DEBUG=1", 231 | "$(inherited)", 232 | ); 233 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 234 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 235 | GCC_WARN_UNDECLARED_SELECTOR = YES; 236 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 237 | GCC_WARN_UNUSED_FUNCTION = YES; 238 | GCC_WARN_UNUSED_VARIABLE = YES; 239 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 240 | MTL_ENABLE_DEBUG_INFO = YES; 241 | ONLY_ACTIVE_ARCH = YES; 242 | SDKROOT = iphoneos; 243 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 244 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 245 | }; 246 | name = Debug; 247 | }; 248 | 1C2E2F6720F48286003CFD58 /* Release */ = { 249 | isa = XCBuildConfiguration; 250 | buildSettings = { 251 | ALWAYS_SEARCH_USER_PATHS = NO; 252 | CLANG_ANALYZER_NONNULL = YES; 253 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 254 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 255 | CLANG_CXX_LIBRARY = "libc++"; 256 | CLANG_ENABLE_MODULES = YES; 257 | CLANG_ENABLE_OBJC_ARC = YES; 258 | CLANG_ENABLE_OBJC_WEAK = YES; 259 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 260 | CLANG_WARN_BOOL_CONVERSION = YES; 261 | CLANG_WARN_COMMA = YES; 262 | CLANG_WARN_CONSTANT_CONVERSION = YES; 263 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 264 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 265 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 266 | CLANG_WARN_EMPTY_BODY = YES; 267 | CLANG_WARN_ENUM_CONVERSION = YES; 268 | CLANG_WARN_INFINITE_RECURSION = YES; 269 | CLANG_WARN_INT_CONVERSION = YES; 270 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 271 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 272 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 273 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 274 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 275 | CLANG_WARN_STRICT_PROTOTYPES = YES; 276 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 277 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 278 | CLANG_WARN_UNREACHABLE_CODE = YES; 279 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 280 | CODE_SIGN_IDENTITY = "iPhone Developer"; 281 | COPY_PHASE_STRIP = NO; 282 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 283 | ENABLE_NS_ASSERTIONS = NO; 284 | ENABLE_STRICT_OBJC_MSGSEND = YES; 285 | GCC_C_LANGUAGE_STANDARD = gnu11; 286 | GCC_NO_COMMON_BLOCKS = YES; 287 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 288 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 289 | GCC_WARN_UNDECLARED_SELECTOR = YES; 290 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 291 | GCC_WARN_UNUSED_FUNCTION = YES; 292 | GCC_WARN_UNUSED_VARIABLE = YES; 293 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 294 | MTL_ENABLE_DEBUG_INFO = NO; 295 | SDKROOT = iphoneos; 296 | SWIFT_COMPILATION_MODE = wholemodule; 297 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 298 | VALIDATE_PRODUCT = YES; 299 | }; 300 | name = Release; 301 | }; 302 | 1C2E2F6920F48286003CFD58 /* Debug */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 306 | CLANG_ENABLE_MODULES = YES; 307 | CODE_SIGN_STYLE = Automatic; 308 | INFOPLIST_FILE = NetworkInterceptorExample/Info.plist; 309 | LD_RUNPATH_SEARCH_PATHS = ( 310 | "$(inherited)", 311 | "@executable_path/Frameworks", 312 | ); 313 | PRODUCT_BUNDLE_IDENTIFIER = com.depoon.NetworkInterceptorExample; 314 | PRODUCT_NAME = "$(TARGET_NAME)"; 315 | SWIFT_OBJC_BRIDGING_HEADER = "NetworkInterceptorExample/NetworkInterceptorExample-Bridging-Header.h"; 316 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 317 | SWIFT_VERSION = 4.0; 318 | TARGETED_DEVICE_FAMILY = "1,2"; 319 | }; 320 | name = Debug; 321 | }; 322 | 1C2E2F6A20F48286003CFD58 /* Release */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 326 | CLANG_ENABLE_MODULES = YES; 327 | CODE_SIGN_STYLE = Automatic; 328 | INFOPLIST_FILE = NetworkInterceptorExample/Info.plist; 329 | LD_RUNPATH_SEARCH_PATHS = ( 330 | "$(inherited)", 331 | "@executable_path/Frameworks", 332 | ); 333 | PRODUCT_BUNDLE_IDENTIFIER = com.depoon.NetworkInterceptorExample; 334 | PRODUCT_NAME = "$(TARGET_NAME)"; 335 | SWIFT_OBJC_BRIDGING_HEADER = "NetworkInterceptorExample/NetworkInterceptorExample-Bridging-Header.h"; 336 | SWIFT_VERSION = 4.0; 337 | TARGETED_DEVICE_FAMILY = "1,2"; 338 | }; 339 | name = Release; 340 | }; 341 | /* End XCBuildConfiguration section */ 342 | 343 | /* Begin XCConfigurationList section */ 344 | 1C2E2F5120F48285003CFD58 /* Build configuration list for PBXProject "NetworkInterceptorExample" */ = { 345 | isa = XCConfigurationList; 346 | buildConfigurations = ( 347 | 1C2E2F6620F48286003CFD58 /* Debug */, 348 | 1C2E2F6720F48286003CFD58 /* Release */, 349 | ); 350 | defaultConfigurationIsVisible = 0; 351 | defaultConfigurationName = Release; 352 | }; 353 | 1C2E2F6820F48286003CFD58 /* Build configuration list for PBXNativeTarget "NetworkInterceptorExample" */ = { 354 | isa = XCConfigurationList; 355 | buildConfigurations = ( 356 | 1C2E2F6920F48286003CFD58 /* Debug */, 357 | 1C2E2F6A20F48286003CFD58 /* Release */, 358 | ); 359 | defaultConfigurationIsVisible = 0; 360 | defaultConfigurationName = Release; 361 | }; 362 | /* End XCConfigurationList section */ 363 | }; 364 | rootObject = 1C2E2F4E20F48285003CFD58 /* Project object */; 365 | } 366 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample.xcodeproj/project.xcworkspace/xcuserdata/kennethpoon.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/depoon/NetworkInterceptor/0222c86aa9822bd90e16fc930232f3614ef23883/NetworkInterceptorExample/NetworkInterceptorExample.xcodeproj/project.xcworkspace/xcuserdata/kennethpoon.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample.xcodeproj/xcshareddata/xcschemes/NetworkInterceptorExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample.xcodeproj/xcuserdata/kennethpoon.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | NetworkInterceptorExample.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 1C2E2F5520F48285003CFD58 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/depoon/NetworkInterceptor/0222c86aa9822bd90e16fc930232f3614ef23883/NetworkInterceptorExample/NetworkInterceptorExample/.DS_Store -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // NetworkInterceptorExample 4 | // 5 | // Created by Kenneth Poon on 10/7/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/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 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/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 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/CodeInjection.m: -------------------------------------------------------------------------------- 1 | // 2 | // CodeInjection.m 3 | // NetworkInterceptorExample 4 | // 5 | // Created by Kenneth Poon on 23/7/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NetworkInterceptorExample-Swift.h" 11 | 12 | @interface CodeInjection: NSObject 13 | @end 14 | 15 | @implementation CodeInjection 16 | 17 | static void __attribute__((constructor)) initialize(void){ 18 | [[CodeInjectionSwift shared] performTask]; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/CodeInjectionSwift.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeInjectionSwift.swift 3 | // NetworkInterceptorExample 4 | // 5 | // Created by Kenneth Poon on 23/7/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import NetworkInterceptor 11 | 12 | @objc class CodeInjectionSwift: NSObject { 13 | @objc public static let shared = CodeInjectionSwift() 14 | 15 | override private init(){} 16 | 17 | @objc func performTask(){ 18 | let requestSniffers: [RequestSniffer] = [ 19 | RequestSniffer(requestEvaluator: AnyHttpRequestEvaluator(), handlers: [ 20 | SniffableRequestHandlerRegistrable.console(logginMode: .nslog).requestHandler() 21 | ]) 22 | ] 23 | 24 | let requestRedirectors: [RequestRedirector] = [ 25 | RequestRedirector(requestEvaluator: DomainHttpRequestEvaluator(domain: "www.antennahouse.com"), redirectableRequestHandler: AlternateUrlRequestRedirector(url: URL(string: "https://www.rhodeshouse.ox.ac.uk/media/1002/sample-pdf-file.pdf")!)) 26 | ] 27 | 28 | let networkConfig = NetworkInterceptorConfig(requestSniffers: requestSniffers, 29 | requestRedirectors: requestRedirectors) 30 | NetworkInterceptor.shared.setup(config: networkConfig) 31 | NetworkInterceptor.shared.startRecording() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | NSAppTransportSecurity 45 | 46 | NSAllowsLocalNetworking 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/NetworkInterceptorExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /NetworkInterceptorExample/NetworkInterceptorExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // NetworkInterceptorExample 4 | // 5 | // Created by Kenneth Poon on 10/7/18. 6 | // Copyright © 2018 Kenneth Poon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import NetworkInterceptor 11 | 12 | class ViewController: UIViewController { 13 | 14 | var session: URLSession? 15 | let webView = UIWebView() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | self.view.addSubview(self.webView) 21 | self.webView.frame = self.view.bounds 22 | 23 | let urlRequest = URLRequest(url: URL(string: "https://www.antennahouse.com/XSLsample/pdf/sample-link_1.pdf")!) 24 | self.webView.loadRequest(urlRequest) 25 | 26 | // 27 | // 28 | // 29 | // self.session = URLSession(configuration: URLSessionConfiguration.default) 30 | // if let url = URL(string: "https://www.antennahouse.com/XSLsample/pdf/sample-link_1.pdf") { 31 | // let request = URLRequest(url: url) 32 | // if let task = self.session?.dataTask(with: request) { 33 | // task.resume() 34 | // print("task started") 35 | // } 36 | // } 37 | } 38 | 39 | override func didReceiveMemoryWarning() { 40 | super.didReceiveMemoryWarning() 41 | // Dispose of any resources that can be recreated. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | 2 | # frozen_string_literal: true 3 | 4 | platform :ios, '10.0' 5 | 6 | use_frameworks! 7 | inhibit_all_warnings! 8 | 9 | def pods 10 | pod 'GzipSwift' 11 | pod 'URLRequest-cURL' 12 | end 13 | 14 | target 'NetworkInterceptor' do 15 | pods 16 | end 17 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - GzipSwift (4.0.4) 3 | - URLRequest-cURL (0.0.3): 4 | - GzipSwift 5 | 6 | DEPENDENCIES: 7 | - GzipSwift 8 | - URLRequest-cURL 9 | 10 | SPEC REPOS: 11 | https://github.com/cocoapods/specs.git: 12 | - GzipSwift 13 | - URLRequest-cURL 14 | 15 | SPEC CHECKSUMS: 16 | GzipSwift: 1a5bc5f7827e09846b0635b5079f1224bc3b1c3a 17 | URLRequest-cURL: b159525e416ead3133ae60450660b8c82d2f2aa0 18 | 19 | PODFILE CHECKSUM: 705ea690507f6390f795ead03efbad69830e36d4 20 | 21 | COCOAPODS: 1.5.3 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NetworkInterceptor 2 | Simple framework to demo how we can intercept URLRequest in iOS Apps. This framework allows you to inspect the details of all outgoing requests from the app. This includes requests sent out by 3rd party framework like FacebookSDK, Google Analytics, etc. It is possible to use this framework to inspect and intercept App Store apps even on non-jailbroken devices. 3 | 4 | ### Installation 5 | 6 | ##### [CocoaPods](http://cocoapods.org) 7 | 8 | NetworkInterceptor is available through CocoaPods. To install it, simply add the following line to your Podfile: 9 | ```ruby 10 | pod 'NetworkInterceptor' 11 | ``` 12 | 13 | ## Main Components 14 | - [NetworkInterceptor.swift](./NetworkInterceptor/Source/NetworkInterceptor.swift#L32) Main class that manages the URLRequest interception process. 15 | - [NetworkRequestSniffableUrlProtocol.swift](./NetworkInterceptor/Source/URLProtocol/NetworkRequestSniffableUrlProtocol.swift) 16 | UrlProtocol class that allows us to observe (and sniff) outgoing requests. 17 | that manages the URLRequest interception process. 18 | - [NetworkRedirectUrlProtocol.swift](./NetworkInterceptor/Source/URLProtocol/NetworkRedirectUrlProtocol.swift) 19 | UrlProtocol class that allows us to redirect requets to a different URL. 20 | - [RequestEvaluator](./NetworkInterceptor/Source/NetworkInterceptor.swift#15) Protocol for classes to evaluate whether operation on URLRequest is allowed 21 | - [SniffableRequestHandler](./NetworkInterceptor/Source/NetworkInterceptor.swift#19) Protocol for classes to handle sniffing/spying on URLRequest 22 | - [RedirectableRequestHandler](./NetworkInterceptor/Source/NetworkInterceptor.swift#23) Protocol for classes to handle the creation of Redirect URLRequests. 23 | - [NetworkInterceptorConfig](./NetworkInterceptor/Source/NetworkInterceptorConfig.swift) Struct that defines the config object used to setup the Interception process 24 | 25 | 26 | ### How to use NetworkInterceptor 27 | 28 | Example 1: Log all http/https requests using NSLog 29 | ```swift 30 | let requestSniffers: [RequestSniffer] = [ 31 | RequestSniffer(requestEvaluator: AnyHttpRequestEvaluator(), handlers: [ 32 | SniffableRequestHandlerRegistrable.console(logginMode: .nslog).requestHandler() 33 | ]) 34 | ] 35 | 36 | let networkConfig = NetworkInterceptorConfig(requestSniffers: requestSniffers) 37 | NetworkInterceptor.shared.setup(config: networkConfig) 38 | NetworkInterceptor.shared.startRecording() 39 | ``` 40 | 41 | Example 2: For all requests that points to "www.antennahouse.com", redirect all matching requests to a custom URL 42 | ```swift 43 | let requestRedirectors: [RequestRedirector] = [ 44 | RequestRedirector(requestEvaluator: DomainHttpRequestEvaluator(domain: "www.antennahouse.com"), 45 | redirectableRequestHandler: AlternateUrlRequestRedirector(url: URL(string: "https://www.rhodeshouse.ox.ac.uk/media/1002/sample-pdf-file.pdf")!)) 46 | ] 47 | 48 | let networkConfig = NetworkInterceptorConfig(requestRedirectors: requestRedirectors) 49 | NetworkInterceptor.shared.setup(config: networkConfig) 50 | NetworkInterceptor.shared.startRecording() 51 | ``` 52 | 53 | ### Request Evaluators available 54 | 55 | [AnyHttpRequestInterceptor.swift](./NetworkInterceptor/Source/RequestEvaluator/AnyHttpRequestEvaluator.swift) Intercepts all http and https requests 56 | 57 | [DomainHttpRequestEvaluator.swift](./NetworkInterceptor/Source/RequestEvaluator/DomainHttpRequestEvaluator.swift) 58 | Intercepts all http and https requests that matches a given doman URL 59 | 60 | ### Sniffable Request Handlers available 61 | 62 | [ConsoleLoggerSniffableRequestHandler.swift](./NetworkInterceptor/Source/SniffableRequestHandler/ConsoleLoggerSniffableRequestHandler.swift) Prints request in cURL format to the console 63 | 64 | [SlackSniffableRequestHandler.swift](./NetworkInterceptor/Source/SniffableRequestHandler/SlackSniffableRequestHandler.swift) Sends the request in cURL format to a designated [Slack](https://slack.com) channel. You are required to provide your own Slack Authentication Token and slack channel ID for this to work. 65 | 66 | [SlackHookSniffableRequestHandler.swift](.NetworkInterceptor/Source/SniffableRequestHandler/SlackHookSniffableRequestHandler.swift) Sends the request in cURL format to a designated [Slack](https://slack.com) using [Webhooks](https://api.slack.com/incoming-webhooks). You are required to provide the Webhook url 67 | 68 | [AlternateDomainSniffableRequestHandler.swift](./NetworkInterceptor/Source/SniffableRequestHandler/AlternateDomainSniffableRequestHandler.swift) Sends the copy of the request to an alternate domain 69 | 70 | ### Request Redirectors available 71 | 72 | [AlternateDomainRequestRedirector.swift](./NetworkInterceptor/Source/RequestRedirector/AlternateDomainRequestRedirector.swift) Redirects request to a different domain. 73 | 74 | [AlternateUrlRequestRedirector.swift](./NetworkInterceptor/Source/RequestRedirector/AlternateUrlRequestRedirector.swift) Requests request to a complete different URL 75 | 76 | 77 | ### If you want to use this framework in iOS Device apps you do not own 78 | - Create a new Dynamic Framework Project and use **NetworkInterceptor** pod. We will only use this framework to start NetworkInterceptor recording. 79 | - Use Objective C to load code into memory. Refer to the example project in this repository. 80 | ```swift 81 | static void __attribute__((constructor)) initialize(void) 82 | ``` 83 | - Build the library using iphoneos architecture. 84 | - Get an .ipa file that does not have Digital Rights Management protection. You can download cracked .ipa from https://www.iphonecake.com 85 | - Inject both the **NetworkInterceptor** pod framework and your new Dynamic Framework into the .ipa using [optool](https://github.com/alexzielenski/optool). You can also use the scripts from in this repository https://github.com/depoon/iOSDylibInjectionDemo. Make sure you included any necessary dependent framework or libraries. 86 | - Use [Cydia Impactor](http://www.cydiaimpactor.com/) to sideload the modified app. 87 | --------------------------------------------------------------------------------