Handle wifi issue with stringWithContentOfUrl - ios

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.

Related

Swift Best Practices - how / when to check for internet connection and 404s when working with APIs

There are a wealth of resources for checking for a valid Internet connection with Swift (Reachability) and also ways to check the statusCode of the httpResponse when making an API call, but what is the "right" way to check and handle these errors (Internet not reachable, server 404) when dealing with an API heavy iOS app?
For example, when the app starts in the initial view (or AppDelegate I suppose) one can check for both and redirect to a "ServerProblemsViewController" that displays a message (or show an alert, though those can be dismissed). But what happens is someone is in the middle of using your app and the Internet drops or the server becomes unreachable? How would you handle this?
I am wondering if devs typically check for Reachability before EVERY API call and check the return status of EVERY API call, or somehow encapsulate that logic into a helper function?
How do experienced iOS devs deal with this situation?
I check for reachability on every call and then examine the response status code by type casting the NSURLResponse to an NSHTTPURLResponse (which has a statusCode property). And yes, my requests use the same base class to encapsulate all of the functionality--only creating derived classes when needed. You can use the reachability example code from Apple which has been ported to Swift and can be typically found on Github through the community. Simply reading through this repo will probably answer a lot of your questions about changes in reachability. Notifications FTW!: https://github.com/ashleymills/Reachability.swift

NSURLProtocol fails for video request

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.

NSURLRequest with App Level ProxyHost/ProxyPort Settings

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).

How can I call a https endpoint by NSURLConnection sendSynchronousRequest in case of untrusted certificate?

I have an IOS6 app, that connects to a REST API to fetch some data.
I use NSURLConnection sendSynchronousRequest in my data fetcher class, and I call its methods by GCD async pattern with blocks from my controller classes. So far so good.
My problem, that I change the API endpoint to https, its certificate is self-signed (I know its secure problems etc, but it is out of question for now).
By using sendSynchronousRequest I can't bypass this problem, because to bypass it, I need to set delegate for NSURLConnection, but in case of sendSynchronousRequest I cant' set delegate, delegate methods just called in case of async calls.
I don't like async request calling, I adore this GCD/sync call pattern very much, it works like a charm, it simple and clear.
So how can I make calls to a https api endpoint by GCD and , NSURLConnection sendSynchronousRequest that bypasses untrusted certificate problem?
Thanks to all!
You answered your question already yourself:
By using sendSynchronousRequest I can't bypass this problem, because to bypass it, I need to set delegate for NSURLConnection, but in case of sendSynchronousRequest I cant' set delegate, delegate methods just called in case of async calls.
You should really get used to the asynchronous style. The approach with a synchronous call within a dispatch_async call is suboptimal to say the least.
The method sendSynchronousRequest is for beginners and toy apps. IMHO, Apple should really deprecate this method and remove it in the next iOSs.
The method sendAsynchronousRequest:queue:completionHandler: is for demonstration purposes, sample apps, prove of concept, and very simple requests not using authentication and https.
Finally, using the asynchronous style with implementing the delegates is for "serious" and release apps. Any serious app should use https when talking to a dedicated server, unless the server is public and does not support https.
Once your have switched to asynchronous style, there might be an answer to your actual question. However, there is no need to bypass a certificate - rather you will use the certificate as it is used in the release app.

How to check if UIWebView loads from NSURLCache?

I actually have more than one question regarding this... I'm still new to NSURLCache and UIWebViews but somehow got the basics down I think.
Anyway, I was able to successfully implement Caching for my UIWebView(I think). I initialized the NSURLCache. My UIWebView loads its contents with the loadRequest: method. I assigned the ViewController that owns the UIWebView as it's delegate. I also had the ViewController implement NSURLConnectionDataDelegate. I implemented the connection:willCacheResponse: method just to log a message in the console so I could check that caching was done. I also logged the current disk and memory usage. It increases after the caching so I can see that it did work.
However, when I load the page again, I think the request is sent again to the server because the connection delegate methods like connection:didReceiveResponse: and connection:willCacheResponse are being called.
I also wanted to try getting the cachedResponse from the cache by using cachedResponseForRequest:. I called this method in webViewDidFinishLoad: but it always returns null. Did caching really work?
How can you tell if the WebView loaded from the cache? Am I misusing cachedResponseForRequest:? And on another note, what proper Control-Cache header values should be present in the very least for caching? I'm testing on google's homepage URL and it returns the private value for its Cache Control header but I can see that caching works because the connection:willCacheResponse: is called.
Help please?
If you run a HTTP proxy like Charles or Fiddler you can keep tabs on what network traffic is actually going out over the wire(less).
You can also inspect the request/response headers to see whether the response is indeed destined for the cache.
Note that in some cases you might see a conditional-GET (returns a 304) meaning the client asked for content that it has in its cache, but only wants to download the content if the server has a more recent version.

Resources