├── .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 |
4 |
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 |
13 |
14 | IBProxyObject
15 | IBUIView
16 | IBUIWebView
17 |
18 |
19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin
20 |
21 |
25 |
26 |
30 |
34 |
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 |
--------------------------------------------------------------------------------