├── .idea ├── RNCachingURLProtocol.iml ├── encodings.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml ├── vcs.xml └── xcode.xml ├── CachedWebView.xcodeproj ├── .gitignore ├── project.pbxproj └── project.xcworkspace │ ├── .gitignore │ └── contents.xcworkspacedata ├── CachedWebView ├── AppDelegate.h ├── AppDelegate.m ├── CachedWebView-Info.plist ├── CachedWebView-Prefix.pch ├── ViewController.h ├── ViewController.m ├── en.lproj │ ├── InfoPlist.strings │ └── ViewController.xib └── main.m ├── NSString+Sha1.h ├── NSString+Sha1.m ├── README.md ├── RNCachingURLProtocol.h ├── RNCachingURLProtocol.m ├── Reachability.h └── Reachability.m /.idea/RNCachingURLProtocol.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Classes 14 | 15 | 16 | 17 | 18 | 19 | 20 | http://www.w3.org/1999/xhtml 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/xcode.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CachedWebView.xcodeproj/.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata 2 | -------------------------------------------------------------------------------- /CachedWebView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C800AAE8166E77B20027E1E5 /* NSString+Sha1.m in Sources */ = {isa = PBXBuildFile; fileRef = C800AAE7166E77B20027E1E5 /* NSString+Sha1.m */; }; 11 | FB237ECE14D5B86100F30AD8 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB237ECD14D5B86100F30AD8 /* UIKit.framework */; }; 12 | FB237ED014D5B86100F30AD8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB237ECF14D5B86100F30AD8 /* Foundation.framework */; }; 13 | FB237ED214D5B86100F30AD8 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB237ED114D5B86100F30AD8 /* CoreGraphics.framework */; }; 14 | FB237ED814D5B86100F30AD8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FB237ED614D5B86100F30AD8 /* InfoPlist.strings */; }; 15 | FB237EDA14D5B86100F30AD8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FB237ED914D5B86100F30AD8 /* main.m */; }; 16 | FB237EDE14D5B86100F30AD8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FB237EDD14D5B86100F30AD8 /* AppDelegate.m */; }; 17 | FB237EE114D5B86100F30AD8 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB237EE014D5B86100F30AD8 /* ViewController.m */; }; 18 | FB237EE414D5B86100F30AD8 /* ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = FB237EE214D5B86100F30AD8 /* ViewController.xib */; }; 19 | FB39D07F14D5BB09002A6D19 /* RNCachingURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = FB237EEB14D5B87400F30AD8 /* RNCachingURLProtocol.m */; }; 20 | FB39D08014D5BB09002A6D19 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = FB237EEC14D5B89700F30AD8 /* Reachability.m */; }; 21 | FB39D08214D5BB1F002A6D19 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB39D08114D5BB1F002A6D19 /* SystemConfiguration.framework */; }; 22 | FB39D08214D5BB1F002A6D1B /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = FB39D08214D5BB1F002A6D1A /* README.md */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | C800AAE6166E77B20027E1E5 /* NSString+Sha1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Sha1.h"; sourceTree = ""; }; 27 | C800AAE7166E77B20027E1E5 /* NSString+Sha1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Sha1.m"; sourceTree = ""; }; 28 | FB237EC914D5B86100F30AD8 /* CachedWebView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CachedWebView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | FB237ECD14D5B86100F30AD8 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 30 | FB237ECF14D5B86100F30AD8 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 31 | FB237ED114D5B86100F30AD8 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 32 | FB237ED514D5B86100F30AD8 /* CachedWebView-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CachedWebView-Info.plist"; sourceTree = ""; }; 33 | FB237ED714D5B86100F30AD8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 34 | FB237ED914D5B86100F30AD8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | FB237EDB14D5B86100F30AD8 /* CachedWebView-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CachedWebView-Prefix.pch"; sourceTree = ""; }; 36 | FB237EDC14D5B86100F30AD8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 37 | FB237EDD14D5B86100F30AD8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 38 | FB237EDF14D5B86100F30AD8 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 39 | FB237EE014D5B86100F30AD8 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 40 | FB237EE314D5B86100F30AD8 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController.xib; sourceTree = ""; }; 41 | FB237EEA14D5B87400F30AD8 /* RNCachingURLProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNCachingURLProtocol.h; sourceTree = ""; }; 42 | FB237EEB14D5B87400F30AD8 /* RNCachingURLProtocol.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNCachingURLProtocol.m; sourceTree = ""; }; 43 | FB237EEC14D5B89700F30AD8 /* Reachability.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; 44 | FB237EED14D5B89700F30AD8 /* Reachability.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; 45 | FB39D08114D5BB1F002A6D19 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 46 | FB39D08214D5BB1F002A6D1A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = file.md; path = README.md; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | FB237EC614D5B86100F30AD8 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | FB39D08214D5BB1F002A6D19 /* SystemConfiguration.framework in Frameworks */, 55 | FB237ECE14D5B86100F30AD8 /* UIKit.framework in Frameworks */, 56 | FB237ED014D5B86100F30AD8 /* Foundation.framework in Frameworks */, 57 | FB237ED214D5B86100F30AD8 /* CoreGraphics.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | FB237EBE14D5B86100F30AD8 = { 65 | isa = PBXGroup; 66 | children = ( 67 | C800AAE6166E77B20027E1E5 /* NSString+Sha1.h */, 68 | C800AAE7166E77B20027E1E5 /* NSString+Sha1.m */, 69 | FB39D08214D5BB1F002A6D1A /* README.md */, 70 | FB237EEA14D5B87400F30AD8 /* RNCachingURLProtocol.h */, 71 | FB237EEB14D5B87400F30AD8 /* RNCachingURLProtocol.m */, 72 | FB237EEC14D5B89700F30AD8 /* Reachability.m */, 73 | FB237EED14D5B89700F30AD8 /* Reachability.h */, 74 | FB237ED314D5B86100F30AD8 /* CachedWebView */, 75 | FB237ECC14D5B86100F30AD8 /* Frameworks */, 76 | FB237ECA14D5B86100F30AD8 /* Products */, 77 | ); 78 | sourceTree = ""; 79 | }; 80 | FB237ECA14D5B86100F30AD8 /* Products */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | FB237EC914D5B86100F30AD8 /* CachedWebView.app */, 84 | ); 85 | name = Products; 86 | sourceTree = ""; 87 | }; 88 | FB237ECC14D5B86100F30AD8 /* Frameworks */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | FB39D08114D5BB1F002A6D19 /* SystemConfiguration.framework */, 92 | FB237ECD14D5B86100F30AD8 /* UIKit.framework */, 93 | FB237ECF14D5B86100F30AD8 /* Foundation.framework */, 94 | FB237ED114D5B86100F30AD8 /* CoreGraphics.framework */, 95 | ); 96 | name = Frameworks; 97 | sourceTree = ""; 98 | }; 99 | FB237ED314D5B86100F30AD8 /* CachedWebView */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | FB237EDC14D5B86100F30AD8 /* AppDelegate.h */, 103 | FB237EDD14D5B86100F30AD8 /* AppDelegate.m */, 104 | FB237EDF14D5B86100F30AD8 /* ViewController.h */, 105 | FB237EE014D5B86100F30AD8 /* ViewController.m */, 106 | FB237EE214D5B86100F30AD8 /* ViewController.xib */, 107 | FB237ED414D5B86100F30AD8 /* Supporting Files */, 108 | ); 109 | path = CachedWebView; 110 | sourceTree = ""; 111 | }; 112 | FB237ED414D5B86100F30AD8 /* Supporting Files */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | FB237ED514D5B86100F30AD8 /* CachedWebView-Info.plist */, 116 | FB237ED614D5B86100F30AD8 /* InfoPlist.strings */, 117 | FB237ED914D5B86100F30AD8 /* main.m */, 118 | FB237EDB14D5B86100F30AD8 /* CachedWebView-Prefix.pch */, 119 | ); 120 | name = "Supporting Files"; 121 | sourceTree = ""; 122 | }; 123 | /* End PBXGroup section */ 124 | 125 | /* Begin PBXNativeTarget section */ 126 | FB237EC814D5B86100F30AD8 /* CachedWebView */ = { 127 | isa = PBXNativeTarget; 128 | buildConfigurationList = FB237EE714D5B86100F30AD8 /* Build configuration list for PBXNativeTarget "CachedWebView" */; 129 | buildPhases = ( 130 | FB237EC514D5B86100F30AD8 /* Sources */, 131 | FB237EC614D5B86100F30AD8 /* Frameworks */, 132 | FB237EC714D5B86100F30AD8 /* Resources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | ); 138 | name = CachedWebView; 139 | productName = CachedWebView; 140 | productReference = FB237EC914D5B86100F30AD8 /* CachedWebView.app */; 141 | productType = "com.apple.product-type.application"; 142 | }; 143 | /* End PBXNativeTarget section */ 144 | 145 | /* Begin PBXProject section */ 146 | FB237EC014D5B86100F30AD8 /* Project object */ = { 147 | isa = PBXProject; 148 | attributes = { 149 | LastUpgradeCheck = 0830; 150 | }; 151 | buildConfigurationList = FB237EC314D5B86100F30AD8 /* Build configuration list for PBXProject "CachedWebView" */; 152 | compatibilityVersion = "Xcode 3.2"; 153 | developmentRegion = English; 154 | hasScannedForEncodings = 0; 155 | knownRegions = ( 156 | en, 157 | ); 158 | mainGroup = FB237EBE14D5B86100F30AD8; 159 | productRefGroup = FB237ECA14D5B86100F30AD8 /* Products */; 160 | projectDirPath = ""; 161 | projectRoot = ""; 162 | targets = ( 163 | FB237EC814D5B86100F30AD8 /* CachedWebView */, 164 | ); 165 | }; 166 | /* End PBXProject section */ 167 | 168 | /* Begin PBXResourcesBuildPhase section */ 169 | FB237EC714D5B86100F30AD8 /* Resources */ = { 170 | isa = PBXResourcesBuildPhase; 171 | buildActionMask = 2147483647; 172 | files = ( 173 | FB237ED814D5B86100F30AD8 /* InfoPlist.strings in Resources */, 174 | FB237EE414D5B86100F30AD8 /* ViewController.xib in Resources */, 175 | FB39D08214D5BB1F002A6D1B /* README.md in Resources */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXResourcesBuildPhase section */ 180 | 181 | /* Begin PBXSourcesBuildPhase section */ 182 | FB237EC514D5B86100F30AD8 /* Sources */ = { 183 | isa = PBXSourcesBuildPhase; 184 | buildActionMask = 2147483647; 185 | files = ( 186 | FB39D07F14D5BB09002A6D19 /* RNCachingURLProtocol.m in Sources */, 187 | FB39D08014D5BB09002A6D19 /* Reachability.m in Sources */, 188 | FB237EDA14D5B86100F30AD8 /* main.m in Sources */, 189 | FB237EDE14D5B86100F30AD8 /* AppDelegate.m in Sources */, 190 | FB237EE114D5B86100F30AD8 /* ViewController.m in Sources */, 191 | C800AAE8166E77B20027E1E5 /* NSString+Sha1.m in Sources */, 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | }; 195 | /* End PBXSourcesBuildPhase section */ 196 | 197 | /* Begin PBXVariantGroup section */ 198 | FB237ED614D5B86100F30AD8 /* InfoPlist.strings */ = { 199 | isa = PBXVariantGroup; 200 | children = ( 201 | FB237ED714D5B86100F30AD8 /* en */, 202 | ); 203 | name = InfoPlist.strings; 204 | sourceTree = ""; 205 | }; 206 | FB237EE214D5B86100F30AD8 /* ViewController.xib */ = { 207 | isa = PBXVariantGroup; 208 | children = ( 209 | FB237EE314D5B86100F30AD8 /* en */, 210 | ); 211 | name = ViewController.xib; 212 | sourceTree = ""; 213 | }; 214 | /* End PBXVariantGroup section */ 215 | 216 | /* Begin XCBuildConfiguration section */ 217 | FB237EE514D5B86100F30AD8 /* Debug */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | ALWAYS_SEARCH_USER_PATHS = NO; 221 | CLANG_ENABLE_OBJC_ARC = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_CONSTANT_CONVERSION = YES; 224 | CLANG_WARN_EMPTY_BODY = YES; 225 | CLANG_WARN_ENUM_CONVERSION = YES; 226 | CLANG_WARN_INFINITE_RECURSION = YES; 227 | CLANG_WARN_INT_CONVERSION = YES; 228 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 229 | CLANG_WARN_UNREACHABLE_CODE = YES; 230 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 231 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 232 | COPY_PHASE_STRIP = NO; 233 | ENABLE_STRICT_OBJC_MSGSEND = YES; 234 | ENABLE_TESTABILITY = YES; 235 | GCC_C_LANGUAGE_STANDARD = gnu99; 236 | GCC_DYNAMIC_NO_PIC = NO; 237 | GCC_NO_COMMON_BLOCKS = YES; 238 | GCC_OPTIMIZATION_LEVEL = 0; 239 | GCC_PREPROCESSOR_DEFINITIONS = ( 240 | "DEBUG=1", 241 | "$(inherited)", 242 | ); 243 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 244 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 245 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 246 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 247 | GCC_WARN_UNDECLARED_SELECTOR = YES; 248 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 249 | GCC_WARN_UNUSED_FUNCTION = YES; 250 | GCC_WARN_UNUSED_VARIABLE = YES; 251 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 252 | ONLY_ACTIVE_ARCH = YES; 253 | SDKROOT = iphoneos; 254 | TARGETED_DEVICE_FAMILY = 2; 255 | }; 256 | name = Debug; 257 | }; 258 | FB237EE614D5B86100F30AD8 /* Release */ = { 259 | isa = XCBuildConfiguration; 260 | buildSettings = { 261 | ALWAYS_SEARCH_USER_PATHS = NO; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_WARN_BOOL_CONVERSION = YES; 264 | CLANG_WARN_CONSTANT_CONVERSION = YES; 265 | CLANG_WARN_EMPTY_BODY = YES; 266 | CLANG_WARN_ENUM_CONVERSION = YES; 267 | CLANG_WARN_INFINITE_RECURSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 273 | COPY_PHASE_STRIP = YES; 274 | ENABLE_STRICT_OBJC_MSGSEND = YES; 275 | GCC_C_LANGUAGE_STANDARD = gnu99; 276 | GCC_NO_COMMON_BLOCKS = YES; 277 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 279 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 280 | GCC_WARN_UNDECLARED_SELECTOR = YES; 281 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 282 | GCC_WARN_UNUSED_FUNCTION = YES; 283 | GCC_WARN_UNUSED_VARIABLE = YES; 284 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 285 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 286 | SDKROOT = iphoneos; 287 | TARGETED_DEVICE_FAMILY = 2; 288 | VALIDATE_PRODUCT = YES; 289 | }; 290 | name = Release; 291 | }; 292 | FB237EE814D5B86100F30AD8 /* Debug */ = { 293 | isa = XCBuildConfiguration; 294 | buildSettings = { 295 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 296 | GCC_PREFIX_HEADER = "CachedWebView/CachedWebView-Prefix.pch"; 297 | INFOPLIST_FILE = "CachedWebView/CachedWebView-Info.plist"; 298 | PRODUCT_BUNDLE_IDENTIFIER = "net.robnapier.${PRODUCT_NAME:rfc1034identifier}"; 299 | PRODUCT_NAME = "$(TARGET_NAME)"; 300 | WRAPPER_EXTENSION = app; 301 | }; 302 | name = Debug; 303 | }; 304 | FB237EE914D5B86100F30AD8 /* Release */ = { 305 | isa = XCBuildConfiguration; 306 | buildSettings = { 307 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 308 | GCC_PREFIX_HEADER = "CachedWebView/CachedWebView-Prefix.pch"; 309 | INFOPLIST_FILE = "CachedWebView/CachedWebView-Info.plist"; 310 | PRODUCT_BUNDLE_IDENTIFIER = "net.robnapier.${PRODUCT_NAME:rfc1034identifier}"; 311 | PRODUCT_NAME = "$(TARGET_NAME)"; 312 | WRAPPER_EXTENSION = app; 313 | }; 314 | name = Release; 315 | }; 316 | /* End XCBuildConfiguration section */ 317 | 318 | /* Begin XCConfigurationList section */ 319 | FB237EC314D5B86100F30AD8 /* Build configuration list for PBXProject "CachedWebView" */ = { 320 | isa = XCConfigurationList; 321 | buildConfigurations = ( 322 | FB237EE514D5B86100F30AD8 /* Debug */, 323 | FB237EE614D5B86100F30AD8 /* Release */, 324 | ); 325 | defaultConfigurationIsVisible = 0; 326 | defaultConfigurationName = Release; 327 | }; 328 | FB237EE714D5B86100F30AD8 /* Build configuration list for PBXNativeTarget "CachedWebView" */ = { 329 | isa = XCConfigurationList; 330 | buildConfigurations = ( 331 | FB237EE814D5B86100F30AD8 /* Debug */, 332 | FB237EE914D5B86100F30AD8 /* Release */, 333 | ); 334 | defaultConfigurationIsVisible = 0; 335 | defaultConfigurationName = Release; 336 | }; 337 | /* End XCConfigurationList section */ 338 | }; 339 | rootObject = FB237EC014D5B86100F30AD8 /* Project object */; 340 | } 341 | -------------------------------------------------------------------------------- /CachedWebView.xcodeproj/project.xcworkspace/.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata 2 | -------------------------------------------------------------------------------- /CachedWebView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CachedWebView/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CachedWebView 4 | // 5 | // Created by Robert Napier on 1/29/12. 6 | // Copyright (c) 2012 Rob Napier. 7 | // 8 | // This code is licensed under the MIT License: 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a 11 | // copy of this software and associated documentation files (the "Software"), 12 | // to deal in the Software without restriction, including without limitation 13 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | // and/or sell copies of the Software, and to permit persons to whom the 15 | // Software is furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | // DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | @class ViewController; 32 | 33 | @interface AppDelegate : UIResponder 34 | 35 | @property (strong, nonatomic) UIWindow *window; 36 | 37 | @property (strong, nonatomic) ViewController *viewController; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /CachedWebView/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CachedWebView 4 | // 5 | // Created by Robert Napier on 1/29/12. 6 | // Copyright (c) 2012 Rob Napier. 7 | // 8 | // This code is licensed under the MIT License: 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a 11 | // copy of this software and associated documentation files (the "Software"), 12 | // to deal in the Software without restriction, including without limitation 13 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | // and/or sell copies of the Software, and to permit persons to whom the 15 | // Software is furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | // DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import "AppDelegate.h" 30 | 31 | #import "ViewController.h" 32 | #import "RNCachingURLProtocol.h" 33 | 34 | @implementation AppDelegate 35 | 36 | @synthesize window = _window; 37 | @synthesize viewController = _viewController; 38 | 39 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 40 | { 41 | [NSURLProtocol registerClass:[RNCachingURLProtocol class]]; 42 | 43 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 44 | self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil]; 45 | self.window.rootViewController = self.viewController; 46 | [self.window makeKeyAndVisible]; 47 | return YES; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /CachedWebView/CachedWebView-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations~ipad 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationPortraitUpsideDown 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationLandscapeRight 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /CachedWebView/CachedWebView-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CachedWebView' target in the 'CachedWebView' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | 13 | #import 14 | #import 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /CachedWebView/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // CachedWebView 4 | // 5 | // Created by Robert Napier on 1/29/12. 6 | // Copyright (c) 2012 Rob Napier. 7 | // 8 | // This code is licensed under the MIT License: 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a 11 | // copy of this software and associated documentation files (the "Software"), 12 | // to deal in the Software without restriction, including without limitation 13 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | // and/or sell copies of the Software, and to permit persons to whom the 15 | // Software is furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | // DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | @interface ViewController : UIViewController 32 | @property (strong, nonatomic) IBOutlet UIWebView *webView; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /CachedWebView/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // CachedWebView 4 | // 5 | // Created by Robert Napier on 1/29/12. 6 | // Copyright (c) 2012 Rob Napier. 7 | // 8 | // This code is licensed under the MIT License: 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a 11 | // copy of this software and associated documentation files (the "Software"), 12 | // to deal in the Software without restriction, including without limitation 13 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | // and/or sell copies of the Software, and to permit persons to whom the 15 | // Software is furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | // DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | #import "ViewController.h" 30 | 31 | @interface ViewController () 32 | 33 | @end 34 | 35 | @implementation ViewController 36 | @synthesize webView = webView_; 37 | 38 | - (void)viewDidLoad 39 | { 40 | [super viewDidLoad]; 41 | [self.webView loadRequest:[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://cnn.com"]]]; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /CachedWebView/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /CachedWebView/en.lproj/ViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 11C74 6 | 1938 7 | 1138.23 8 | 567.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 933 12 | 13 | 14 | IBProxyObject 15 | IBUIView 16 | IBUIWebView 17 | 18 | 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | PluginDependencyRecalculationVersion 23 | 24 | 25 | 26 | 27 | IBFilesOwner 28 | IBIPadFramework 29 | 30 | 31 | IBFirstResponder 32 | IBIPadFramework 33 | 34 | 35 | 36 | 274 37 | 38 | 39 | 40 | 274 41 | {768, 1004} 42 | 43 | 44 | _NS:693 45 | 46 | 1 47 | MSAxIDEAA 48 | 49 | IBIPadFramework 50 | 1 51 | YES 52 | 53 | 54 | {{0, 20}, {768, 1004}} 55 | 56 | 57 | 58 | 59 | 3 60 | MQA 61 | 62 | 2 63 | 64 | 65 | 66 | 2 67 | 68 | IBIPadFramework 69 | 70 | 71 | 72 | 73 | 74 | 75 | view 76 | 77 | 78 | 79 | 3 80 | 81 | 82 | 83 | webView 84 | 85 | 86 | 87 | 5 88 | 89 | 90 | 91 | 92 | 93 | 0 94 | 95 | 96 | 97 | 98 | 99 | -1 100 | 101 | 102 | File's Owner 103 | 104 | 105 | -2 106 | 107 | 108 | 109 | 110 | 2 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 4 119 | 120 | 121 | 122 | 123 | 124 | 125 | ViewController 126 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 127 | UIResponder 128 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 129 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 130 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 131 | 132 | 133 | 134 | 135 | 136 | 5 137 | 138 | 139 | 140 | 141 | ViewController 142 | UIViewController 143 | 144 | webView 145 | UIWebView 146 | 147 | 148 | webView 149 | 150 | webView 151 | UIWebView 152 | 153 | 154 | 155 | IBProjectSource 156 | ./Classes/ViewController.h 157 | 158 | 159 | 160 | 161 | 0 162 | IBIPadFramework 163 | YES 164 | 3 165 | 933 166 | 167 | 168 | -------------------------------------------------------------------------------- /CachedWebView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CachedWebView 4 | // 5 | // Created by Robert Napier on 1/29/12. 6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char *argv[]) { 12 | @autoreleasepool { 13 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /NSString+Sha1.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import 4 | 5 | /** 6 | * This extension contains several a helper 7 | * for creating a sha1 hash from instances of NSString 8 | */ 9 | @interface NSString (Sha1) 10 | 11 | /** 12 | * Creates a SHA1 (hash) representation of NSString. 13 | * 14 | * @return NSString 15 | */ 16 | - (NSString *)sha1; 17 | 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /NSString+Sha1.m: -------------------------------------------------------------------------------- 1 | 2 | #import "NSString+Sha1.h" 3 | 4 | @implementation NSString (Sha1) 5 | 6 | - (NSString *)sha1 7 | { 8 | // see http://www.makebetterthings.com/iphone/how-to-get-md5-and-sha1-in-objective-c-ios-sdk/ 9 | NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding]; 10 | uint8_t digest[CC_SHA1_DIGEST_LENGTH]; 11 | 12 | CC_SHA1(data.bytes, (CC_LONG)data.length, digest); 13 | 14 | NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; 15 | 16 | for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { 17 | [output appendFormat:@"%02x", digest[i]]; 18 | } 19 | 20 | return output; 21 | } 22 | 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BACKGROUND 2 | 3 | RNCachingURLProtocol is a simple shim for the HTTP protocol (that’s not 4 | nearly as scary as it sounds). Anytime a URL is downloaded, the response is 5 | cached to disk. Anytime a URL is requested, if we’re online then things 6 | proceed normally. If we’re offline, then we retrieve the cached version. 7 | 8 | The point of RNCachingURLProtocol is mostly to demonstrate how this is done. 9 | The current implementation is extremely simple. In particular, it doesn’t 10 | worry about cleaning up the cache. The assumption is that you’re caching just 11 | a few simple things, like your “Latest News” page (which was the problem I 12 | was solving). It caches all HTTP traffic, so without some modifications, it’s 13 | not appropriate for an app that has a lot of HTTP connections (see 14 | MKNetworkKit for that). But if you need to cache some URLs and not others, 15 | that is easy to implement. 16 | 17 | You should also look at [AFCache](https://github.com/artifacts/AFCache) for a 18 | more powerful caching engine that is currently integrating the ideas of 19 | RNCachingURLProtocol. 20 | 21 | # USAGE 22 | 23 | 1. To build, you will need the Reachability code from Apple (included). That requires that you link with 24 | `SystemConfiguration.framework`. 25 | 26 | 2. At some point early in the program (usually `application:didFinishLaunchingWithOptions:`), 27 | call the following: 28 | 29 | `[NSURLProtocol registerClass:[RNCachingURLProtocol class]];` 30 | 31 | 3. There is no step 3. 32 | 33 | For more details see 34 | [Drop-in offline caching for UIWebView (and NSURLProtocol)](http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588). 35 | 36 | # EXAMPLE 37 | 38 | See the CachedWebView project for example usage. 39 | 40 | # LICENSE 41 | 42 | This code is licensed under the MIT License: 43 | 44 | Permission is hereby granted, free of charge, to any person obtaining a 45 | copy of this software and associated documentation files (the "Software"), 46 | to deal in the Software without restriction, including without limitation 47 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 48 | and/or sell copies of the Software, and to permit persons to whom the 49 | Software is furnished to do so, subject to the following conditions: 50 | 51 | The above copyright notice and this permission notice shall be included in 52 | all copies or substantial portions of the Software. 53 | 54 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 55 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 56 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 57 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 58 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 59 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 60 | DEALINGS IN THE SOFTWARE. 61 | -------------------------------------------------------------------------------- /RNCachingURLProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNCachingURLProtocol.h 3 | // 4 | // Created by Robert Napier on 1/10/12. 5 | // Copyright (c) 2012 Rob Napier. All rights reserved. 6 | // 7 | // This code is licensed under the MIT License: 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a 10 | // copy of this software and associated documentation files (the "Software"), 11 | // to deal in the Software without restriction, including without limitation 12 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 | // and/or sell copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | // DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | // RNCachingURLProtocol is a simple shim for the HTTP protocol (that’s not 29 | // nearly as scary as it sounds). Anytime a URL is download, the response is 30 | // cached to disk. Anytime a URL is requested, if we’re online then things 31 | // proceed normally. If we’re offline, then we retrieve the cached version. 32 | // 33 | // The point of RNCachingURLProtocol is mostly to demonstrate how this is done. 34 | // The current implementation is extremely simple. In particular, it doesn’t 35 | // worry about cleaning up the cache. The assumption is that you’re caching just 36 | // a few simple things, like your “Latest News” page (which was the problem I 37 | // was solving). It caches all HTTP traffic, so without some modifications, it’s 38 | // not appropriate for an app that has a lot of HTTP connections (see 39 | // MKNetworkKit for that). But if you need to cache some URLs and not others, 40 | // that is easy to implement. 41 | // 42 | // You should also look at [AFCache](https://github.com/artifacts/AFCache) for a 43 | // more powerful caching engine that is currently integrating the ideas of 44 | // RNCachingURLProtocol. 45 | // 46 | // A quick rundown of how to use it: 47 | // 48 | // 1. To build, you will need the Reachability code from Apple (included). That requires that you link with 49 | // `SystemConfiguration.framework`. 50 | // 51 | // 2. At some point early in the program (application:didFinishLaunchingWithOptions:), 52 | // call the following: 53 | // 54 | // `[NSURLProtocol registerClass:[RNCachingURLProtocol class]];` 55 | // 56 | // 3. There is no step 3. 57 | // 58 | // For more details see 59 | // [Drop-in offline caching for UIWebView (and NSURLProtocol)](http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588). 60 | 61 | #import 62 | 63 | @interface RNCachingURLProtocol : NSURLProtocol 64 | 65 | + (NSSet *)supportedSchemes; 66 | + (void)setSupportedSchemes:(NSSet *)supportedSchemes; 67 | 68 | - (NSString *)cachePathForRequest:(NSURLRequest *)aRequest; 69 | - (BOOL) useCache; 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /RNCachingURLProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNCachingURLProtocol.m 3 | // 4 | // Created by Robert Napier on 1/10/12. 5 | // Copyright (c) 2012 Rob Napier. 6 | // 7 | // This code is licensed under the MIT License: 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a 10 | // copy of this software and associated documentation files (the "Software"), 11 | // to deal in the Software without restriction, including without limitation 12 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 | // and/or sell copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | // DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #import "RNCachingURLProtocol.h" 29 | #import "Reachability.h" 30 | #import "NSString+Sha1.h" 31 | 32 | #define WORKAROUND_MUTABLE_COPY_LEAK 1 33 | 34 | #if WORKAROUND_MUTABLE_COPY_LEAK 35 | // required to workaround http://openradar.appspot.com/11596316 36 | @interface NSURLRequest(MutableCopyWorkaround) 37 | 38 | - (id) mutableCopyWorkaround; 39 | 40 | @end 41 | #endif 42 | 43 | @interface RNCachedData : NSObject 44 | @property (nonatomic, readwrite, strong) NSData *data; 45 | @property (nonatomic, readwrite, strong) NSURLResponse *response; 46 | @property (nonatomic, readwrite, strong) NSURLRequest *redirectRequest; 47 | @end 48 | 49 | static NSString *RNCachingURLHeader = @"X-RNCache"; 50 | 51 | @interface RNCachingURLProtocol () // iOS5-only 52 | @property (nonatomic, readwrite, strong) NSURLConnection *connection; 53 | @property (nonatomic, readwrite, strong) NSMutableData *data; 54 | @property (nonatomic, readwrite, strong) NSURLResponse *response; 55 | - (void)appendData:(NSData *)newData; 56 | @end 57 | 58 | static NSObject *RNCachingSupportedSchemesMonitor; 59 | static NSSet *RNCachingSupportedSchemes; 60 | 61 | @implementation RNCachingURLProtocol 62 | @synthesize connection = connection_; 63 | @synthesize data = data_; 64 | @synthesize response = response_; 65 | 66 | + (void)initialize 67 | { 68 | if (self == [RNCachingURLProtocol class]) 69 | { 70 | static dispatch_once_t onceToken; 71 | dispatch_once(&onceToken, ^{ 72 | RNCachingSupportedSchemesMonitor = [NSObject new]; 73 | }); 74 | 75 | [self setSupportedSchemes:[NSSet setWithObject:@"http"]]; 76 | } 77 | } 78 | 79 | + (BOOL)canInitWithRequest:(NSURLRequest *)request 80 | { 81 | // only handle http requests we haven't marked with our header. 82 | if ([[self supportedSchemes] containsObject:[[request URL] scheme]] && 83 | ([request valueForHTTPHeaderField:RNCachingURLHeader] == nil)) 84 | { 85 | return YES; 86 | } 87 | return NO; 88 | } 89 | 90 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request 91 | { 92 | return request; 93 | } 94 | 95 | - (NSString *)cachePathForRequest:(NSURLRequest *)aRequest 96 | { 97 | // This stores in the Caches directory, which can be deleted when space is low, but we only use it for offline access 98 | NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; 99 | NSString *fileName = [[[aRequest URL] absoluteString] sha1]; 100 | 101 | return [cachesPath stringByAppendingPathComponent:fileName]; 102 | } 103 | 104 | - (void)startLoading 105 | { 106 | if (![self useCache]) { 107 | NSMutableURLRequest *connectionRequest = 108 | #if WORKAROUND_MUTABLE_COPY_LEAK 109 | [[self request] mutableCopyWorkaround]; 110 | #else 111 | [[self request] mutableCopy]; 112 | #endif 113 | // we need to mark this request with our header so we know not to handle it in +[NSURLProtocol canInitWithRequest:]. 114 | [connectionRequest setValue:@"" forHTTPHeaderField:RNCachingURLHeader]; 115 | NSURLConnection *connection = [NSURLConnection connectionWithRequest:connectionRequest 116 | delegate:self]; 117 | [self setConnection:connection]; 118 | } 119 | else { 120 | RNCachedData *cache = [NSKeyedUnarchiver unarchiveObjectWithFile:[self cachePathForRequest:[self request]]]; 121 | if (cache) { 122 | NSData *data = [cache data]; 123 | NSURLResponse *response = [cache response]; 124 | NSURLRequest *redirectRequest = [cache redirectRequest]; 125 | if (redirectRequest) { 126 | [[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response]; 127 | } else { 128 | 129 | [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; // we handle caching ourselves. 130 | [[self client] URLProtocol:self didLoadData:data]; 131 | [[self client] URLProtocolDidFinishLoading:self]; 132 | } 133 | } 134 | else { 135 | [[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotConnectToHost userInfo:nil]]; 136 | } 137 | } 138 | } 139 | 140 | - (void)stopLoading 141 | { 142 | [[self connection] cancel]; 143 | } 144 | 145 | // NSURLConnection delegates (generally we pass these on to our client) 146 | 147 | - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response 148 | { 149 | // Thanks to Nick Dowell https://gist.github.com/1885821 150 | if (response != nil) { 151 | NSMutableURLRequest *redirectableRequest = 152 | #if WORKAROUND_MUTABLE_COPY_LEAK 153 | [request mutableCopyWorkaround]; 154 | #else 155 | [request mutableCopy]; 156 | #endif 157 | // We need to remove our header so we know to handle this request and cache it. 158 | // There are 3 requests in flight: the outside request, which we handled, the internal request, 159 | // which we marked with our header, and the redirectableRequest, which we're modifying here. 160 | // The redirectable request will cause a new outside request from the NSURLProtocolClient, which 161 | // must not be marked with our header. 162 | [redirectableRequest setValue:nil forHTTPHeaderField:RNCachingURLHeader]; 163 | 164 | NSString *cachePath = [self cachePathForRequest:[self request]]; 165 | RNCachedData *cache = [RNCachedData new]; 166 | [cache setResponse:response]; 167 | [cache setData:[self data]]; 168 | [cache setRedirectRequest:redirectableRequest]; 169 | [NSKeyedArchiver archiveRootObject:cache toFile:cachePath]; 170 | [[self client] URLProtocol:self wasRedirectedToRequest:redirectableRequest redirectResponse:response]; 171 | return redirectableRequest; 172 | } else { 173 | return request; 174 | } 175 | } 176 | 177 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 178 | { 179 | [[self client] URLProtocol:self didLoadData:data]; 180 | [self appendData:data]; 181 | } 182 | 183 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 184 | { 185 | [[self client] URLProtocol:self didFailWithError:error]; 186 | [self setConnection:nil]; 187 | [self setData:nil]; 188 | [self setResponse:nil]; 189 | } 190 | 191 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 192 | { 193 | [self setResponse:response]; 194 | [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; // We cache ourselves. 195 | } 196 | 197 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection 198 | { 199 | [[self client] URLProtocolDidFinishLoading:self]; 200 | 201 | NSString *cachePath = [self cachePathForRequest:[self request]]; 202 | RNCachedData *cache = [RNCachedData new]; 203 | [cache setResponse:[self response]]; 204 | [cache setData:[self data]]; 205 | [NSKeyedArchiver archiveRootObject:cache toFile:cachePath]; 206 | 207 | [self setConnection:nil]; 208 | [self setData:nil]; 209 | [self setResponse:nil]; 210 | } 211 | 212 | - (BOOL) useCache 213 | { 214 | BOOL reachable = (BOOL) [[Reachability reachabilityWithHostName:[[[self request] URL] host]] currentReachabilityStatus] != NotReachable; 215 | return !reachable; 216 | } 217 | 218 | - (void)appendData:(NSData *)newData 219 | { 220 | if ([self data] == nil) { 221 | [self setData:[newData mutableCopy]]; 222 | } 223 | else { 224 | [[self data] appendData:newData]; 225 | } 226 | } 227 | 228 | + (NSSet *)supportedSchemes { 229 | NSSet *supportedSchemes; 230 | @synchronized(RNCachingSupportedSchemesMonitor) 231 | { 232 | supportedSchemes = RNCachingSupportedSchemes; 233 | } 234 | return supportedSchemes; 235 | } 236 | 237 | + (void)setSupportedSchemes:(NSSet *)supportedSchemes 238 | { 239 | @synchronized(RNCachingSupportedSchemesMonitor) 240 | { 241 | RNCachingSupportedSchemes = supportedSchemes; 242 | } 243 | } 244 | 245 | @end 246 | 247 | static NSString *const kDataKey = @"data"; 248 | static NSString *const kResponseKey = @"response"; 249 | static NSString *const kRedirectRequestKey = @"redirectRequest"; 250 | 251 | @implementation RNCachedData 252 | @synthesize data = data_; 253 | @synthesize response = response_; 254 | @synthesize redirectRequest = redirectRequest_; 255 | 256 | - (void)encodeWithCoder:(NSCoder *)aCoder 257 | { 258 | [aCoder encodeObject:[self data] forKey:kDataKey]; 259 | [aCoder encodeObject:[self response] forKey:kResponseKey]; 260 | [aCoder encodeObject:[self redirectRequest] forKey:kRedirectRequestKey]; 261 | } 262 | 263 | - (id)initWithCoder:(NSCoder *)aDecoder 264 | { 265 | self = [super init]; 266 | if (self != nil) { 267 | [self setData:[aDecoder decodeObjectForKey:kDataKey]]; 268 | [self setResponse:[aDecoder decodeObjectForKey:kResponseKey]]; 269 | [self setRedirectRequest:[aDecoder decodeObjectForKey:kRedirectRequestKey]]; 270 | } 271 | 272 | return self; 273 | } 274 | 275 | @end 276 | 277 | #if WORKAROUND_MUTABLE_COPY_LEAK 278 | @implementation NSURLRequest(MutableCopyWorkaround) 279 | 280 | - (id) mutableCopyWorkaround { 281 | NSMutableURLRequest *mutableURLRequest = [[NSMutableURLRequest alloc] initWithURL:[self URL] 282 | cachePolicy:[self cachePolicy] 283 | timeoutInterval:[self timeoutInterval]]; 284 | [mutableURLRequest setAllHTTPHeaderFields:[self allHTTPHeaderFields]]; 285 | if ([self HTTPBodyStream]) { 286 | [mutableURLRequest setHTTPBodyStream:[self HTTPBodyStream]]; 287 | } else { 288 | [mutableURLRequest setHTTPBody:[self HTTPBody]]; 289 | } 290 | [mutableURLRequest setHTTPMethod:[self HTTPMethod]]; 291 | 292 | return mutableURLRequest; 293 | } 294 | 295 | @end 296 | #endif 297 | -------------------------------------------------------------------------------- /Reachability.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2016 Apple Inc. All Rights Reserved. 3 | See LICENSE.txt for this sample’s licensing information 4 | 5 | Abstract: 6 | Basic demonstration of how to use the SystemConfiguration Reachablity APIs. 7 | */ 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | 14 | typedef enum : NSInteger { 15 | NotReachable = 0, 16 | ReachableViaWiFi, 17 | ReachableViaWWAN 18 | } NetworkStatus; 19 | 20 | #pragma mark IPv6 Support 21 | //Reachability fully support IPv6. For full details, see ReadMe.md. 22 | 23 | 24 | extern NSString *kReachabilityChangedNotification; 25 | 26 | 27 | @interface Reachability : NSObject 28 | 29 | /*! 30 | * Use to check the reachability of a given host name. 31 | */ 32 | + (instancetype)reachabilityWithHostName:(NSString *)hostName; 33 | 34 | /*! 35 | * Use to check the reachability of a given IP address. 36 | */ 37 | + (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress; 38 | 39 | /*! 40 | * Checks whether the default route is available. Should be used by applications that do not connect to a particular host. 41 | */ 42 | + (instancetype)reachabilityForInternetConnection; 43 | 44 | 45 | #pragma mark reachabilityForLocalWiFi 46 | //reachabilityForLocalWiFi has been removed from the sample. See ReadMe.md for more information. 47 | //+ (instancetype)reachabilityForLocalWiFi; 48 | 49 | /*! 50 | * Start listening for reachability notifications on the current run loop. 51 | */ 52 | - (BOOL)startNotifier; 53 | - (void)stopNotifier; 54 | 55 | - (NetworkStatus)currentReachabilityStatus; 56 | 57 | /*! 58 | * WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand. 59 | */ 60 | - (BOOL)connectionRequired; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Reachability.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2016 Apple Inc. All Rights Reserved. 3 | See LICENSE.txt for this sample’s licensing information 4 | 5 | Abstract: 6 | Basic demonstration of how to use the SystemConfiguration Reachablity APIs. 7 | */ 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | #import 16 | 17 | #import "Reachability.h" 18 | 19 | #pragma mark IPv6 Support 20 | //Reachability fully support IPv6. For full details, see ReadMe.md. 21 | 22 | 23 | NSString *kReachabilityChangedNotification = @"kNetworkReachabilityChangedNotification"; 24 | 25 | 26 | #pragma mark - Supporting functions 27 | 28 | #define kShouldPrintReachabilityFlags 1 29 | 30 | static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment) 31 | { 32 | #if kShouldPrintReachabilityFlags 33 | 34 | NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n", 35 | (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', 36 | (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', 37 | 38 | (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', 39 | (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', 40 | (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', 41 | (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', 42 | (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', 43 | (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', 44 | (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-', 45 | comment 46 | ); 47 | #endif 48 | } 49 | 50 | 51 | static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) 52 | { 53 | #pragma unused (target, flags) 54 | NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); 55 | NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback"); 56 | 57 | Reachability* noteObject = (__bridge Reachability *)info; 58 | // Post a notification to notify the client that the network reachability changed. 59 | [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject]; 60 | } 61 | 62 | 63 | #pragma mark - Reachability implementation 64 | 65 | @implementation Reachability 66 | { 67 | SCNetworkReachabilityRef _reachabilityRef; 68 | } 69 | 70 | + (instancetype)reachabilityWithHostName:(NSString *)hostName 71 | { 72 | Reachability* returnValue = NULL; 73 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); 74 | if (reachability != NULL) 75 | { 76 | returnValue= [[self alloc] init]; 77 | if (returnValue != NULL) 78 | { 79 | returnValue->_reachabilityRef = reachability; 80 | } 81 | else { 82 | CFRelease(reachability); 83 | } 84 | } 85 | return returnValue; 86 | } 87 | 88 | 89 | + (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress 90 | { 91 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress); 92 | 93 | Reachability* returnValue = NULL; 94 | 95 | if (reachability != NULL) 96 | { 97 | returnValue = [[self alloc] init]; 98 | if (returnValue != NULL) 99 | { 100 | returnValue->_reachabilityRef = reachability; 101 | } 102 | else { 103 | CFRelease(reachability); 104 | } 105 | } 106 | return returnValue; 107 | } 108 | 109 | 110 | + (instancetype)reachabilityForInternetConnection 111 | { 112 | struct sockaddr_in zeroAddress; 113 | bzero(&zeroAddress, sizeof(zeroAddress)); 114 | zeroAddress.sin_len = sizeof(zeroAddress); 115 | zeroAddress.sin_family = AF_INET; 116 | 117 | return [self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress]; 118 | } 119 | 120 | #pragma mark reachabilityForLocalWiFi 121 | //reachabilityForLocalWiFi has been removed from the sample. See ReadMe.md for more information. 122 | //+ (instancetype)reachabilityForLocalWiFi 123 | 124 | 125 | 126 | #pragma mark - Start and stop notifier 127 | 128 | - (BOOL)startNotifier 129 | { 130 | BOOL returnValue = NO; 131 | SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; 132 | 133 | if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context)) 134 | { 135 | if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) 136 | { 137 | returnValue = YES; 138 | } 139 | } 140 | 141 | return returnValue; 142 | } 143 | 144 | 145 | - (void)stopNotifier 146 | { 147 | if (_reachabilityRef != NULL) 148 | { 149 | SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 150 | } 151 | } 152 | 153 | 154 | - (void)dealloc 155 | { 156 | [self stopNotifier]; 157 | if (_reachabilityRef != NULL) 158 | { 159 | CFRelease(_reachabilityRef); 160 | } 161 | } 162 | 163 | 164 | #pragma mark - Network Flag Handling 165 | 166 | - (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags 167 | { 168 | PrintReachabilityFlags(flags, "networkStatusForFlags"); 169 | if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) 170 | { 171 | // The target host is not reachable. 172 | return NotReachable; 173 | } 174 | 175 | NetworkStatus returnValue = NotReachable; 176 | 177 | if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) 178 | { 179 | /* 180 | If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi... 181 | */ 182 | returnValue = ReachableViaWiFi; 183 | } 184 | 185 | if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || 186 | (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) 187 | { 188 | /* 189 | ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs... 190 | */ 191 | 192 | if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) 193 | { 194 | /* 195 | ... and no [user] intervention is needed... 196 | */ 197 | returnValue = ReachableViaWiFi; 198 | } 199 | } 200 | 201 | if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) 202 | { 203 | /* 204 | ... but WWAN connections are OK if the calling application is using the CFNetwork APIs. 205 | */ 206 | returnValue = ReachableViaWWAN; 207 | } 208 | 209 | return returnValue; 210 | } 211 | 212 | 213 | - (BOOL)connectionRequired 214 | { 215 | NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef"); 216 | SCNetworkReachabilityFlags flags; 217 | 218 | if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) 219 | { 220 | return (flags & kSCNetworkReachabilityFlagsConnectionRequired); 221 | } 222 | 223 | return NO; 224 | } 225 | 226 | 227 | - (NetworkStatus)currentReachabilityStatus 228 | { 229 | NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef"); 230 | NetworkStatus returnValue = NotReachable; 231 | SCNetworkReachabilityFlags flags; 232 | 233 | if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) 234 | { 235 | returnValue = [self networkStatusForFlags:flags]; 236 | } 237 | 238 | return returnValue; 239 | } 240 | 241 | 242 | @end 243 | --------------------------------------------------------------------------------