UIWebView not loading https link - iphone

I am trying to load one secure HTTP url into UIWebView, but it throws me an "untrusted certificate error" i.e error -1202. NSURLDomainError.
I searched on stackoverflow and found out this solution and implemented the same:
UIWebView to view self signed websites (No private api, not NSURLConnection) - is it possible?
As per this solution after receiving a callback to :
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
We again load the secure http url to webview, and as we have already done the authentication pass,
it loads the data.Also, the credential the server uses are my personal crendetials, so i am mentioning the NSURL
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:#"username"
password:#"password"
persistence:NSURLCredentialPersistenceForSession];
But my problem here is, it just goes into some kind of loop, and i am not able to get any of the callback of UIWebView. Any solution or any idea what the problem will be?

Related

iOS stringWithContentsOfURL gives HTML error

I am trying to Parse Google Maps api with SBJSon but before parsing when I use the below code gives me the Access Denied errors in the HTML this error comes only when I run on the Device and If I run the same in the simulator ..it gives me the google maps api string..what should be the error for Device running.
NSString *String = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding
error:nil];
NSLog(#"string %#",string);
error
<HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD>....
full Html error is similar of
Setting Proxy Username & password using Objective-C - iPhone
The network your device is connected to does not allow access to the URL. Since you are able to access the URL from your MAC, connect your device to the same network as your MAC. It should work.
Alternate solution, first access some URL from the device Safari browser. If asked for authentication, provide correct username and password. Once connected, do not close the tab or Safari app. Run your application and the URL should now work.
EDIT
If you still face authentication problem, may be you are better off implementing asynchronous downloader which implements NSURLConnectionDelegate. Provide the authentication parameters in method,
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
The API you are using stringWithContentsOfURL:encoding:error: will not give you much flexibility.
Hope that helps!

xcode, nslog weird hitch

The code below gives me an NSLog redirect location on a tinyurl link when clicking on a tinyurl link. However, if I remove the following piece of code [[UIApplication sharedApplication] openURL:[request URL]];, it will give me the actual website in NSLog and not the tinyurl link. How can I make it where I can have the actual website URL in NSLog while the share application code is presented?
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"Redirect Location: %#",[request.URL absoluteString]);
[[UIApplication sharedApplication] openURL:[request URL]];
}
Short answer:
Using UIWebViewDelegate method shouldStartLoadWithRequest is not a great mechanism for figuring out to which site you'll be redirected (especially since, judging from your code, you'll ultimately opening it in an external app, not your UIWebView), because you're getting shouldStartLoadWithRequest before the redirect takes place. You can, though, use NSURLConnection and NSURLConnectionDataDelegate methods to figure out where you'll eventually be redirected.
Long answer:
If you look at shouldStartLoadWithRequest, if you return YES, you'll let the UIWebView follow the redirect requests. Consider the following shouldStartLoadWithRequest:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"%#", request.URL);
return YES;
}
If you use this with the a random bit.ly URL, say, http://nyti.ms/Yi6EAk, you'll see the following log:
2013-03-12 21:23:31.418 webtest[6959:c07] http://nyti.ms/Yi6EAk
2013-03-12 21:23:31.511 webtest[6959:c07] http://bit.ly/Yi6EAk?cc=0d7134b272b1004cb954d0400076e9fa
2013-03-12 21:23:31.560 webtest[6959:c07] http://www.nytimes.com/2013/03/13/us/politics/ryans-plan-aims-to-balance-budget-in-10-years.html?hp&_r=0
That third call to shouldStartLoadWithRequest is the actual URL for which I defined the bit.ly redirect URL, i.e. the final destination of two consecutive redirects. But if your shouldStartLoadWithRequest returned NO, then you'll never figure out to which site it would ultimately be redirected. (And by the way, your shouldStartLoadWithRequest should definitely return YES or NO ... your sample doesn't return either.)
As you can see, because shouldStartLoadWithRequest happens before the redirect has a chance to take place, you'll see each and every redirect taking place. Depending upon what the resulting site does, you may see subsequent shouldStartLoadWithRequest calls as the page retrieves extra content. This makes this an awkward mechanism for finding out what site to which you were ultimately redirected.
If you really need the site you're being redirected to, you might want to use NSURLConnection, instead. While that is generally used for actually retrieving the data from the server, it can also be used for capturing the redirects (but not suffering from the problems of the UIWebViewDelegate method shouldStartLoadWithRequest, where it's tough to differentiate between genuine redirects from just random additional content that the page may subsequently request).
So consider the following:
self.url = [NSURL URLWithString:#"http://nyti.ms/Yi6EAk"];
NSURLRequest *request = [NSURLRequest requestWithURL:self.url];
[NSURLConnection connectionWithRequest:request delegate:self];
You can then implement connection:willSendRequest:redirectResponse:, a NSURLConnectionDataDelegate method, that will track the various redirects:
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
{
self.url = request.URL;
return request;
}
Clearly, because you're using NSURLConnection to track the redirects, but we don't really care about the responseData that we generally use NSURLConnection to retrieve, we can just cancel the connection as soon as you receive a good response (confident that we already know to which site were finally redirected):
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[connection cancel];
NSLog(#"Ok, we now know that the resulting URL is %#", self.url);
}
You might, by the way, also want to capture connection errors, for example:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Ok, we failed trying to retrieve data from %#", self.url);
}
In closing, it's worth pointing out that this technique, of making the HTTP request with NSURLConnection and letting it follow the whole series of redirects, is a pretty inefficient process, so you'll have to decide whether it's worth it. But this is one way to figure out where your HTTP redirects will lead you.
One final caveat, this captures traditional redirects, but if the page is doing any client-side JavaScript redirects, I don't think this technique will work. It works for the vast majority of redirected HTTP requests, though.

Claims Authentication to SharePoint on iPhone

I have made a simple SharePoint client App for iPhone, which require access to some SharePoint web services (mainly /_vti_bin/Lists.asmx). I am having a trouble figuring out how to do this on newer SharePoint environment such as Office365.
With old BPOS environment having forms-based authentication, I was able to authenticate to those services by simply implementing didReceiveAuthenticationChallenge method;
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:username
password:password
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:newCredential
forAuthenticationChallenge:challenge];
}
This obviously didn't work any more with SharePoint sites having claims authentication, so I did some research and found out that I need FedAuth cookies to be attached to the request.
http://msdn.microsoft.com/en-us/library/hh147177.aspx
According to this article, with .NET Apps, it seems possible to retrieve those HTTPOnly FedAuth cookies using WININET.dll, but I guess that's not available on iPhone?
Then, I saw SharePlus App presenting UIWebView and getting user to login to their Office365 account first on the browser screen (which is the same concept as explained in "Enabling User Login for Remote Authentication" section of the article above).
So, I tried to see if I can somehow get access to those FedAuth cookies by logging into Office365 account via UIWebView, but [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] did not let me access HTTPOnly cookies.
Is there a way to achieve claims authentication on iPhone apps without needing designated intermediate .NET service for handling authentications, or requiring user to turn off HTTPOnly property on those cookies?
Sorry, I am very new to SharePoint so I may not even be looking at the right direction, but I would appreciate any advise on getting claims authentication to work on iPhone apps. Thanks in advance!
I've figured this out myself. Had to laugh at my own stupidity and impatience.
First of all, [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] DO let you access HTTPOnly cookies. Though, when user logs into Office 365 on the UIWebView, (void)webViewDidFinishLoad:(UIWebView *)webView delegate method get called several times so you just need to wait until FedAuth appears in the cookies jar.
Here is my (void)webViewDidFinishLoad:(UIWebView *)webView implementation;
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookiesArray = [storage cookies];
for (NSHTTPCookie *cookie in cookiesArray) {
if ([[cookie name] isEqualToString:#"FedAuth"]) {
/*** DO WHATEVER YOU WANT WITH THE COOKIE ***/
break;
}
}
}
Once you have obtained the cookie, you just need to append that to the NSURLRequest using (void)setAllHTTPHeaderFields:(NSDictionary *)headerFields method when you call SharePoint web services.
Hope this helps someone.

webView and 'timeout'

I have question, in my application I need to have a connection and a webview to display the web content.
The problem is that I have a username and a password which need to be right in order to get a connection. Now I want to have an alert that comes up if the username is wrong. The
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
does not work for this, because if the username or the password is wrong it will continue loading (even though it won't display anything).
Is there a possibilty to 'tell' webview to set a time interval and then display an alert?
THANK YOU SO MUCH!
If you're authenticating with your server via HTTP authentication (per your comment above) then I would do this differently. When the user enters their username/pwd, make a request to any page (something lightweight) using NSURLConnection. If the username/password are incorrect you can handle the failures in the NSURLConnectionDelegate methods and prompt your user for a new username/password.
You can also do this with the iOS 5 version of NSURLConnection instead of the delegate methods but the docs aren't very good.
When this first request succeeds then load the web view.
EDIT: added example of using NSURLConnection to check if HTTP auth succeeds
Ok, here's a simple example. You can make this better by allowing NSURLConnection to load your intended request and if it succeeds load the response data into the webView vs initiating a new request. Read the URL Loading System docs to learn more about how to use NSURLConnection.
In your webView delegate, initiate an NSURLConnection with a request to the same domain as the webView request with the user credentials as follows:
- (void)performHTTPAuthRequest
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://%#:%##test.starsightings.com", username, password]]];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
}
// if the request succeeds, credentials must be good so load the webView
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// request completed successfully so username/password must be good
[self.webView loadRequest:theRequest];
}
// ignore the failure
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
// if we get an auth challenge, the credentials are bad so cancel the request and alert the user
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
[[challenge sender] cancelAuthenticationChallenge:challenge];
// alert user that username/pwd is invalid
}

Embedding a Smart URL inside an App

I want to embed a URL inside an iPhone app, that will take the user to a landing page which later on I can redirect that URL to an iTunes App link once it becomes available, all that without resubmitting the App for approval again. Is this possible?
Thanks
Yes. Use the openURL: method of UIApplication to open your landing page URL. When you want to swap it out, have your favourite server-side language perform a 301 or 302 redirect, depending on whether the redirect is permanent or not.
Having said that, if all you want to do is direct people to rate the app or something similar to that, you don't need to swap it out; the application ID is all you need to construct the URL and iTunes Connect gives you that when you register the app, before you upload the binary.
You can use a short URL or any other URL with a redirection, then in your code intercept the redirection to avoid opening Safari if it's not necessary:
Create a NSURLConnection:
NSURL *shortURL = [NSURL URLWithString:#"http://tinyurl.com/xxyy"];
NSURLConnection *conn = [[NSURLConnection alloc]
initWithRequest:[NSURLRequest requestWithURL:shortURL]
delegate:self
startImmediately:YES];
Define some delegate methods. In your class, define an NSURL property finalDestination to hold the final URL.
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)response
{
finalDestination = [response URL];
return request;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (!finalDestination) {
/* redirect failed, use backup url */
finalDestination = /* backup url */
}
[[UIApplication sharedApplication] openURL:finalDestination];
}
Also, check Apple Technical Q&A Launching the App Store from an iPhone application.
(Note: ARC code)