I have a code which places a phone call using below code :
// Make a call to given phone number
- (void)callPhoneNumber:(NSString *)phoneNumber
{
if (!self.webview)
self.webview = [[UIWebView alloc] init];
self.webview.delegate = self;
// Remove non-digits from phone number
phoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:#""];
// Make a call
NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:#"tel:%#", phoneNumber]];
[self.webview loadRequest:[NSURLRequest requestWithURL:url]];
[self.view addSubview:self.webview];
}
This makes a call. What I want is, I want to know when user ends a call. I have to perform an operation when user ends a call. Is there any way for it?
What I tried is, I set delegate of webview to current controller. But none of the delegate methods is called.
- (void)webViewDidStartLoad:(UIWebView *)webView
{
DLog(#"Start Loading");
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
DLog(#"Finish Loading");
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
DLog(#"Did fail with error : %#", error);
}
I don't know if you need this info but I use webview so that when phone call is made, flow remains within the app and on call end, app screen is displayed rather than user manually coming to app from native contact app.
CoreTelephony framework has a CTCallCenter Class with callEventHandler property.
#property (nonatomic, copy) void (^callEventHandler)(CTCall*);
You will have to define a handler block in your application and assign it to this property.
If your application is active when a call event takes place, the system dispatches the event to your handler immediately when call state changes. Refer apple documents found here.
I used below code to get notified of call events.
// Create CTCallCenter object
callCenter = [[CTCallCenter alloc] init];
// Assign event handler. This will be called on each call event
self.callCenter.callEventHandler = ^(CTCall* call) {
// If call ended
if (call.callState == CTCallStateDisconnected)
{
NSLog(#"Call ended.");
}
};
For other call states check out Call States. For more info on CTCallCenter look at Apple doc for CTCallCenter.
You should be implementing this delegate method of UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
on end call operation your webview will notify this delegate method about your action perform and you can handle it in there for example
-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if (navigationType == UIWebViewNavigationTypeLinkClicked )
{
NSURL *url = [request URL];
NSLog(#"URL ===== %#",url);
// you can check your end call operation URL and handle it accordingly
if ([[url absoluteString] rangeOfString:#"#"].location == NSNotFound)
{
[[UIApplication sharedApplication] openURL:[request URL]];
return NO;
}
//return NO;
}
return YES;
}
Related
I have an app with UIWebView, which loads a web site https://app.bridallive.com/. Previously it was working well in iPad, but now it does not.
It loads neither in UIWebView (in a simplest possible app) and in iOS Safari. Works well in Mac OS.
I tried to diagnose it with:
-(void) loadWebSite {
NSURL * url = [NSURL URLWithString:#"https://app.bridallive.com/#/dashboard?iPadApp=true"];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest: request];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self loadWebSite];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"Error : %#",error);
}
NSAppTransportSecurity is set to YES and the web site has valid and trusted certificate.
this is what I used in futher experiments:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self loadWebSite];
}
but this does not give any errors.
Can anyone suggest a way to diagnose this in iOS?
Best regards,
Andrey
UIWebview has a protocol, UIWebViewDelegate, it helps you to observe changes in your
It implements a few method using what you can observe the changes in your UIWebview loading lifecyclye:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"shouldStart");
return true;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
NSLog(#"webViewDidStartLoad");
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(#"webViewDidFinishLoad");
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"%#", error);
NSLog(#"didFailLoadWithError");
}
In order to receive the callback on this functions, sign up for the delegate on your classes interface in .h for example.
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIWebViewDelegate>
#end
I have set up a sample project, and upon opening the URL, i receive the following error in the didFailLoadWithError callback:
Error Domain=WebKitErrorDomain Code=101 "(null)"
It seems you are not encoding you request properly. You should encode the string the following way:
NSString *encodedString=[[NSString stringWithFormat:#"https://app.bridallive.com/#/dashboard?iPadApp=true"] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
NSURL *weburl = [NSURL URLWithString:encodedString];
I am trying to build a filter for a UIWebView and I am struggiling to detect when the UIWebView has completely finished loading. I have used the following two methods
– webView:shouldStartLoadWithRequest:navigationType:
– webViewDidFinishLoad:
but the issue is that these will be called multiple times when a page has frames and additional content to load.
What I need is to know when the view has completely loaded and there is no more content to fetch. Then when the content has loaded I can check to URL of the page against a list of approved URLS.
ANy ideas?
Use the UIWebViewDelegate protocol method webViewDidFinishLoad and webView's isLoading property
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//Check here if still webview is loding the content
if (webView.isLoading)
return;
//after code when webview finishes
NSLog(#"Webview loding finished");
}
Swift 3 version:
func webViewDidFinishLoad(_ webView: UIWebView) {
if webView.isLoading{
return
}
print("Done loading")
}
Try use:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)requestURL navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = [requestURL URL];
NSLog(#"##### url = %#",[url absoluteString]);
return YES;
}
don't forget to set your UIWebView Delegate
or add statement,
NSRange range = [[url absoluteString] rangeOfString:#"https://www.google.com"];
if (range.location != NSNotFound)
{}
hope to help you.
It is true that the original question was posted many years ago. Recently I had to find a reliable solution to this issue.
Here is the solution that worked for me:
Replaced UIWebView with WKWebWiew.
Added 'evaluateJavaScript' code while handling the 'didFinishNavigation' delegate method.
The complete code is:
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
[webView evaluateJavaScript:#"document.body.innerHTML" completionHandler:^(id result, NSError *error) {
if (result != nil) {
// Call your method here
}
if(error) {
NSLog(#"evaluateJavaScript error : %#", error.localizedDescription);
}
}];
}
If you aren't an owner of UIWebView and as a result you don't have an access to webViewDidFinishLoad but you have a reference to UIWebView you can use the next code
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (timer) in
guard uiWebView.isLoading else {
return
}
timer.invalidate()
//Add your logic here
}
I'm making an iOS application that allows 2 users interact with each other. When first user navigates to a URL on his app using UIWebView, I will send this url to the second user. The second user receive this link and use UIWebView to load it.
Everything works well until one person goes into a private zone. For example, when the first user goes to www.google.com and logs into his gmail account. Now I capture this link and send to the second user, who tries to load it, but this fails, because it's a private zone of the first user.
How can the UIWebView of second user know that the received url is in private zone or must authenticate to access it?
Here some illustration codes:
On the first user, when navigates an url like www.google.com. I call this function
- (void)loadRequestUrl:(NSString *)url {
NSLog(#"load request");
//Load request
self.websiteUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:websiteUrl]];
[wbWebview loadRequest:request];
//Send url to partner
//I'm using GCDAsyncSocket to send this url string to our server
//And server will send it to my current partner
//Just example code for sending
[asyncSocket send:url];
}
//On second user. I received url string from a delegate of GCDAsyncSocket. In this delegate I call this function
- (void)receiveUrl:(NSString *)url {
//Just Load request
self.websiteUrl = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:websiteUrl]];
[wbWebview loadRequest:request];
}
Here an example of url that on first user after he logins his google account
https://accounts.google.com.vn/accounts/SetSID?ssdc=1&sidt=ALWU2cscH%252BVqOOjXXIRKgtz4Q8qmIcJ5lE0dy7xh7MISa%252Bw75BNSeOrF3cO91IED8Cy6PfREuDjuXLzdOMEPaaP0p6XZpCzJFQi4w2xAZa9VKubLQ5xk5%252FF%252FOj8KR0f6e5PSav%252Fww0mKEAuPoI0Dtnve600Pj6PERFtvlH3kbt2Y0hk4KEBpn6nk7zAXUdt2wc%252FaHK0%252FufzyfIMI2hkLpCFu1W1XaOIS3zwuGttA5tXjyLb3AeBLmPgfBbsd7hwZWp7IRVJGglde8gAJ%252F%252BmIhQD4eMQa1s7LD8tnuoagx%252BmRzQ4EGqtcAlE%252FGE3e8b1itkh2HXZQZYB612X1NpcPgta1XbgO7IHd0g7HsDEnsqodhHtr9F7vGl4fO%252BCcHFYaHjH3dT2mCjnOwBn%252Bbh0%252FykYpxqbx2W8K%252BHcZp3B4KI166qCPCZvgnvq7QACPsPuWGVrll4Nw2yLK%252FE2bdmFVILfgIpVbY9SheA%253D%253D&continue=http%253A%252F%252Fwww.google.com.vn%252F%253Fgfe_rd%253Dcr%2526ei%253Dgf7EU-TLL-zV8ge_rYCIAw
The second user receives it, but cannot open. I can NOT capture error on any delegate
IMPORTANCE: I don't need to force second user open it, I just want to capture the event to tell the second user that first user went into private zone
To receive and handle authentication challenge in uiwebview , here is the code.
- (void)viewDidLoad
{
// Load the web view with your url
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"yoururl"]]];
isauth = NO;
}
Now for the delegates. This one is called before a new request is loaded into the webview.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
{
if (!isauth) {
isauth = NO;
[[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
return NO;
}
return YES;
}
//in nsurlconnections delegate. This one deals with the authentication challenge. this time set isauth to YES
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
if ([challenge previousFailureCount] == 0) {
isauth = YES;
[[challenge sender] useCredential:[NSURLCredential credentialWithUser:#"username" password:#"password" persistence:NSURLCredentialPersistencePermanent] forAuthenticationChallenge:challenge];
}
else
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
// if the authentication is successfully handled than you will get data in this method in which you can reload web view with same request again.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
{
NSLog(#"received response via nsurlconnection");
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"yoururl"]];
[webView loadRequest:urlRequest];
}
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
{
return NO;
}
Hope it helps.
thank you.
to get user name and password , there are multiple ways of doing it , you can create your own mechanism for the same .One simple way is.
-
(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge: (NSURLAuthenticationChallenge *)challenge{
NSLog(#"Need Authentication");
UIAlertView *webLogin = [[UIAlertView alloc] initWithTitle:#"Auth"
message:#"Enter User and Password"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK"
, nil];
self. challenge = challenge;
webLogin.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
[webLogin show];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
user = [[alertView textFieldAtIndex:0]text];
pass = [[alertView textFieldAtIndex:1]text];
if (buttonIndex == [alertView cancelButtonIndex]) {
[self dismissModalViewControllerAnimated:YES];
}
else if (buttonIndex != [alertView cancelButtonIndex]) {
[self handleChallenge:self.challenge withUser:user password:pass];
}
}
- (void)handleChallenge:(NSURLAuthenticationChallenge *)aChallenge withUser:(NSString *)userName password:(NSString *)password {
NSURLCredential *credential = [[NSURLCredential alloc]
initWithUser:userName password:password
persistence:NSURLCredentialPersistenceForSession];
[[aChallenge sender] useCredential:credential forAuthenticationChallenge:aChallenge];
}
I'm trying to get token,but getting warning message like 'WACloudAccessControlClient, may not respond to setToken
- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
NSURL *url = [[NSURL alloc] initWithString:#"URL"];
if(url)
{
/* make the call re-entrant when we re-load the content ourselves */
if([url isEqual:[request URL]])
{
return YES;
}
[url release];
}
url = [[request URL] retain];
NSString* scheme = [url scheme];
if([scheme isEqualToString:#"acs"])
{
// parse the JSON URL parameter into a dictionary
NSDictionary* pairs = [self parsePairs:[url absoluteString]];
if(pairs)
{
WACloudAccessToken* accessToken;
accessToken = [[WACloudAccessToken alloc] initWithDictionary:pairs];
[WACloudAccessControlClient setToken:accessToken];
[self dismissModalViewControllerAnimated:YES];
}
return NO;
}
[NSURLConnection connectionWithRequest:request delegate:self];
return NO;
}
Any ideas?
Seems like WACloudAccessControlClient is a class, not an instance variable in the code you wrote.
When you are calling something like [AClass someMethod], you are actually working with structure of AClass, not with an instance.
Structure cannot save/store in memory any runtime data, because it doesn't have allocated memory.
If WACloudAccessControlClient is an instance variable in this code, then it's not imported with statement:
#import "WACloudAccessControlClient.h"
... or something similar to this. Compiler cannot detect any method declared with such signature.
For more information about class methods read this.
Is it possible to download and add a passbook from within a webview without modifying the app to support the new MIME type or unknown MIME types like Sparrow did?
I have a news ios app with a webview. In the webview I display the news items and a banner. When you click the banner I want to open a url to a .pkpass file and add it to my passbook. Instead I get a FrameLoadInterrupted error and nothing visible happens.
If I open the url from safari this works fine, chrome, since earlier this week (version 23) also opens the url like intended.
Is this some weird strategy from Apple maybe? not allowing this MIME type to properly open from a webview?
My best bet is that the UIWebView is just not capable of handling the Passbook passes. You could however try and catch the downloading in the UIWebViewDelegate method -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType.
What I mean to say is that you have to handle this part your self, since the http://passkit.com/samples/ I used does not return an url which ends pkpass thus it is totally depended on how you request the passbook files.
If you do in include the .pkpass extension you can check for the extension in the request.
If you know what kind of URL the passbook file is at you write your own download code here and pass it to passbook via the passbook api.
There does not seem to be any great on fix for this, you could load the failed ULR in safari:
- (void) webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"Webview: %#", error);
if ([error.domain isEqualToString:#"WebKitErrorDomain"] && error.code == 102) {
NSString *failedURL = [error.userInfo objectForKey:NSURLErrorFailingURLStringErrorKey];
if (failedURL == nil) {
return;
}
NSURL *url = [NSURL URLWithString:failedURL];
[[UIApplication sharedApplication] openURL:url];
}
}
But this is just really bad coding.
Okay, talked to the engineers at WWDC and this is a know bug in UIWebView but Apple probably won't fix it because they're encouraging people to adopt the new SFSafariViewController. We did come up with a hack to fix it should you need to support iOS 6-8:
Add the PassKit framework to the project if it isn't already.
#import <PassKit/PassKit.h>
Set up a delegate for the UIWebView (for example the view controller launching the UIWebView)
<UIWebViewDelegate>
Add a class variable to cache the UIWebView requests
NSURLRequest *_lastRequest;
Set the delegate
self.webView.delegate = self;
Add the callback to grab all requests and cache in case of failure
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
_lastRequest = request;
return YES;
}
Add the failure callback and re-fetch the URL to see if it is a pass and if so, present the pass to the user
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
// try to get headers in case of passbook pass
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:_lastRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// check for PKPass
if ([response.MIMEType isEqualToString:#"application/vnd.apple.pkpass"]) {
NSError *error;
PKPass *pass = [[PKPass alloc] initWithData:data error:&error];
if (error) {
NSLog(#"Error: %#", error);
} else {
PKAddPassesViewController *apvc = [[PKAddPassesViewController alloc] initWithPass:pass];
[self presentViewController:apvc animated:YES completion:nil];
}
}
}];
}
It's a horrible hack for what should be supported, but it works regardless of the extension and should support re-directs. If you want to pile on the bug train, you can reference radar://21314226