Is there an alternative to ASIWebPageRequest in the ASIHTTPRequest library that I can use for downloading an entire web page in iOS including the CSS, JavaScript and image files, etc? I can't seem to find a similar class in the AFNetworking framework and so far my searches haven't been successful. I can't use ASIHTTPRequest as I can't seem to get it to work at all in any of my apps, no examples I've found work for iOS7 and I'd much rather use something more recent anyway.
I basically want to store an entire webpage locally in a directory on the iPhone/iPad so that a user can then edit it locally and send the whole directory to their web server later. The user also needs to be able to view the webpage at any time in a UIWebView.
If this isn't possible I'll have to just download the HTML file and then parse it to find the URLs of external resources, then download those separately. I'd much rather not do this, but if I have to then what's the best library for accomplishing this?
Thanks to anyone to helps me out!
It seems you are after something like wget but I'm not sure if you can do that in iOS.
You can use the native method NSString stringWithContentsOfURL:encoding:error: to fetch the HTML of a web page.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/clm/NSString/stringWithContentsOfURL:encoding:error:
You can intercept all of the HTTP requests made from your app via a custom NSURLProtocol. You'll have to expand on this concept, but here's a simple class that when dropped into your project will log out all of the requests being made (e.g. from a UIWebView).
#interface TSHttpLoggingURLProtocol : NSURLProtocol
#end
#implementation TSHttpLoggingURLProtocol
+ (void) load
{
[NSURLProtocol registerClass: self];
}
+ (BOOL) canInitWithRequest: (NSURLRequest *) request
{
NSLog( #"%#", request.URL );
return NO;
}
#end
You could expand on this and actually perform the requests from the protocol implementation (see https://github.com/rnapier/RNCachingURLProtocol for a more complete example). Or you could just track the requests and download them yourself later.
There's no easy way that I know of to associate a protocol handler like this with a particular instance of a UIWebView. You're going to intercept ALL of the requests in the app with this. (Caveat - in iOS7, protocols can be registered to specific NSURLSessions; for these I think a globally registered protocol handler won't intercept.)
I'd use a UIWebView and cache all Requests it makes
Related
In my app users can open a website. The website is heavy with content (20MB average). A lot of my users don't have access to the internet at all times, so it's essential that we save any content they open (at the same time they view it). And then the next time they open it, it's completely loaded from the disk.
Here's what I'm thinking:
Use UIWebView and cache everything .. that's working but UIWebview is depreciated.
Use WKWebView, but I cannot cache everything :( Im not even sure I can intercept all the requests and responses.
Use a proxy server inside the app and save all data as it's being loaded from the remote server, I have no idea how to start this.
Basically, I want something similar to https://github.com/evermeer/EVURLCache
but for WKWebView
The question:
Which approach do you recommend? And how can I get around the downsides mentioned ?
With WKWebView you can use URLRequests to load your website data and cache it. The URLRequest can be configured in their caching policy during initialization.
init(url URL: URL,
cachePolicy: NSURLRequest.CachePolicy,
timeoutInterval: TimeInterval)
You could try to go forward with returnCacheDataElseLoad. However, this probably would not cover your particular case even if the cache is used primarily. Have a look at this answer regarding application cache and WKWebView.
I'm having some issues with video requests handled through a special protocol scheme in a NSURLProtocol subclass. Every other resource (images/text) are getting handled correctly, however, when a video request is sent, I only get a call to 'canInitWithRequest' and no follow up. So, my video resource doesn't get resolved. Now, I've looked around and I found no definite solution for this. Some people use instead an embedded HTTP server, but that seems an overkill. Does anyone know if this is a bug or if not, why is this limitation, is there an workaround for it?
A similar issue: Custom NSURLProtocol class for WebView doesn't work when loading video in HTML5 document , but unfortunately without an answer.
#Meda, I was facing the similar issue. Here what I found and hope it is useful to you.
I assume that you are using NSUrlProtocol because you want to intercept the video requests.
I was using web view which makes request for video over HTTP. It goes to NSURLProtocol and makes the request. When it receives the data, webView loads the video rendering plugin (looking at the mime type in HTTP header). The plugin needs the data to come as Partial HTTP response (Response code 206). Further, the plugin does not use NSURLProtocol class but uses network layer below it. So requests that plugin makes, do not go thru NSURLProtocol. Considering this, there could be 2 problems in your case.
1. HTTP server you are using may not be supporting partial responses.
2. HTTP server is not reachable directly (Can you access the video from safari or any other
browser on your device?)
You can verify both the cases by taking network trace. use tcpdump (available on Mac) to take network trace and see what is happening there.
I'm looking for a way to use NSMutableURLRequest with app level proxyHost/Port settings, essentially a replacement for ASIHTTPRequest lib with proxyHost/proxyPort. I've tried modifying the CFReadStream (from NSURLRequest HTTPBodyStream), but it SIGSEGs when setting the proxy settings. I would rather not have to rewrite my app with CFNetworking, and it looks like AFNetwork lib doesn't include this feature yet either.
Has anyone successfully done this with NSMutableURLRequest?
The real answer appears to be creating a custom NSURLProtocol. Should be a straight forward derivation, and add the appropriate proxyHost/proxyPort to the request (along with any other values such as a customized User Agent string). Then, supposedly, all requests will be routed through this custom protocol (including UIWebView requests..direct or derived).
relevant samples:
https://developer.apple.com/library/ios/samplecode/CustomHTTPProtocol/Introduction/Intro.html#//apple_ref/doc/uid/DTS40013653
http://eng.42go.com/customizing-uiwebview-requests-with-nsurlprotocol/
I'll post more when I have the thing operational.
Things of note with this implementation.
Initially started using CFNetworking as the "wedge" in my custom
NSURLPRotocol, but quickly found I was rewriting the same code that
was in ASIHTTPRequest. So I just implemented the wedge with
ASIHTTPRequest.
The items that are not documented well (or at all), is the
interaction of UIWebView with NSURLProtocol callbacks, vs
NSURLRequest/Conenction with NSURLProtocol. Some Findings:
a) All dependent page resources are loaded automatically by UIWebView
(which we knew), and they all go through NSURLProtocol, so it is an
excellent place to put in code to modify all requests.
b) The UIWebView sets the Referer header. On a redirect, the only
way to get the UIWebView to update it's Referer from the original URL
to the new redirect URL is with the [[self client] URLProtocol:self
wasRedirectedToRequest:redirectRequest
redirectResponse:tmpHttpResponse]; callback.
c) when the above redirect callback is received by UIWebView, it
generates a new NSURLRequest (essentially the one you sent back to
it). So if you have a wedge that likes to do the redirect
internally, you have to cancel the Request that it attempts to make,
in favor of the new one from UIWebView.
You have to be careful with which callbacks you implement from ASIHTTPRequestDelegate. e.g. implementing didReceiveData will disable the built in gzip processing. willRedirectToURL disables most of the built in redirect processing (even if you call [request redirectToURL:newURL]; as recommended in the comments).
I'm trying to make a UIWebView application, just like any other (with refresh, go forward, back, google search, etc). It is going to be very simple. One thing I want to do however, is make the data loaded into the UIWebView loaded through a proxy server (like hideMyAss) - so websites like at schools or workplaces become unblocked.
I have been looking for a proxy which enables me to input the website address at the end of a proxy's URL, but I have not found one.
E.g. Hidemyass.com?url=google.com
Does Apple have any documentation as to how I could achieve this. I have no idea where to start looking as I don't know the exact name of what if be looking for. Any suggestions would be really helpful. Thanks!
Create a subclass of NSURLProtocol class that will handle all web protocols such as HTTP, HTTPS, SSL etc. This is a abstract class that provides the basic structure for performing protocol-specific loading of URL data. Guide on NSURLProtocol
Once your created your custom url protocol handler, register it in your appDelegate so your protocol will have priority over any of the built-in protocols.
[NSURLProtocol registerClass:[MyURLProtocol class]];
In terms of proxy, create your own server and implement a ready made solution that will do all your tunneling of client data to outside world. Tinyproxy is a example of a free software that can do your proxy requirements, research others or even create your own solution if you got the time.
While testing my application, i connected to a wifi network which needed an authentication to access the internet.
I would have like [NSString stringWithContentOfUrl:encoding:error:] to fail or return the content of this authentication page even if it is not the page I asked. But it keeps on trying to download, and never returns.
Do you have any solution to detect this kind of issue ?
I would recommend using NSURLConnection. When a redirect happens it will call the delegate method as mentioned here. Also with NSURLConnection you will have greater control in the future when you add additional features and content. Or should the router not do a redirect and just force you to a page, you will be able to use the NSURLConnection to download the content and parse it to determine if it is indeed the page you were looking for.
In that case you'll have to do some coding. Download the contents of the url via NSURLRequest -> NSURLConnection. Then via the NSURLConnection's delegate methods you can respond to the authentication challenge.