WKWebView - didFailProvisionalNavigation not called when a component fail - ios

I m trying to log an error when a page component loaded in a WKWebView fail, but didFailProvisionalNavigation is not called.
I removed a css file from my page to test with it, but no result.
When I load the page on Google Chrome I can find the missing file
The WKWebView is initialised as:
#interface MyWebView : UIViewController <WKNavigationDelegate>
#property(strong,nonatomic) WKWebView *loginWebView;
#end
The .m file
#implementation MyWebView
- (void) initWebView {
// Set and Load the WebView
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame];
self.webView.navigationDelegate = self;
[self.webView loadRequest:request];
[self.view addSubview:self.webView];
}
Then I implemented the methods bellow:
webView:decidePolicyForNavigationAction:decisionHandler:
webView:didStartProvisionalNavigation:
webView:didFinishNavigation:
webView:didFailNavigation:withError:
webView:didFailProvisionalNavigation:withError:
The first three methods get called, but neither didFailNavigation nor didFailProvisionalNavigation are called
By looking at the documentation for didFailProvisionalNavigation we have
Called when an error occurs while the web view is loading content.
Well, a content has failed (css file) and the method is not called, how can I do this?
PS : I also tried using UIWebView!

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSInteger statusCode = ((NSHTTPURLResponse *)navigationResponse.response).statusCode;
NSLog(#"statusCode:%ld", statusCode);
if (statusCode/100 == 4 || statusCode/100 == 5) {
NSLog(#"webview error:%#", navigationResponse.response);
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
this works!

Related

To implement the following swift code in objective-c?

I am implementing the web view through react-native. Therefore, I use the react-native-webview library. However, "window.open" and "window.close" are not implemented in the react-native-webview.
I need to apply that part of the code for social login. So I found the swift code document. However, I don't know how to change this document to an objective-c code.
object-c partial code of react-native-webview
swift document
// webView list management
var webViews = [WKWebView]()
...
func webView(_ webView: WKWebView,
createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures
) -> WKWebView? {
guard let frame = self.webViews.last?.frame else {
return nil
}
//Creating and returning a web view creates a parent relationship with the current web view.
return createWebView(frame: frame, configuration: configuration)
}
/// ---------- popup close ----------
func webViewDidClose(_ webView: WKWebView) {
destroyCurrentWebView()
}
// Examples of Web View Generation Methods
func createWebView(frame: CGRect, configuration: WKWebViewConfiguration) -> WKWebView {
let webView = WKWebView(frame: frame, configuration: configuration)
// set delegate
webView.uiDelegate = self
webView.navigationDelegate = self
// add view
self.view.addSubview(webView)
self.webViews.append(webView)
return webView
}
// Examples of webview deletion methods
func destroyCurrentWebView() {
// remove from webview lists and screens
self.webViews.popLast()?.removeFromSuperview()
}
How can I apply this code to suit the react-native-webview?
EDIT
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
WKWebView *popUpWebView = [[WKWebView alloc] initWithFrame: navigationAction.targetFrame configuration: configuration];
popUpWebView.uiDelegate = self;
popUpWebView.navigationDelegate = self;
[_webView addSubview:popUpWebView];
return nil;
}
- (void)webViewDidClose:(WKWebView *)webView {
[_webView removeFromSuperview];
}
I looked at the document and changed it as follows. However, an error occurs when building. I don't know what the problem is.
As I mentioned in my comments, I am not sure if this will work for React Native but this Obj-C code is the same as your swift code and should compile
In your .h file
Your .h file will probably need to be the same as RNCWebView.h and you might need to remove anything unwanted / unused
In your .m file
Similarly, your .m will be similar to RNCWebView.m and remove what you don't use.
Then as per your swift code, these are the updated Obj C versions of those functions
- (WKWebView *)webView:(WKWebView *)webView
createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
forNavigationAction:(WKNavigationAction *)navigationAction
windowFeatures:(WKWindowFeatures *)windowFeatures
{
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
if ([webViews count] == 0) {
return nil;
}
WKWebView *currentWebView = [webViews lastObject];
WKWebView *popUpWebView = [[WKWebView alloc] initWithFrame: currentWebView.frame
configuration: configuration];
popUpWebView.UIDelegate = self;
popUpWebView.navigationDelegate = self;
[webView addSubview:popUpWebView];
return popUpWebView;
}
- (void)webViewDidClose:(WKWebView *)webView
{
[webView removeFromSuperview];
}
Update
If the webViews variable from the original swift code is unused / not needed, you probably need to update the webView createWebViewWithConfiguration as follows:
- (WKWebView *)webView:(WKWebView *)webView
createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
forNavigationAction:(WKNavigationAction *)navigationAction
windowFeatures:(WKWindowFeatures *)windowFeatures
{
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
WKWebView *popUpWebView = [[WKWebView alloc] initWithFrame: webView.bounds
configuration: configuration];
popUpWebView.UIDelegate = self;
popUpWebView.navigationDelegate = self;
[webView addSubview:popUpWebView];
return popUpWebView;
}
Finally, just to clarify:
The header does not need to be the same as mine, I just gave you an example if you were subclassing a UIViewController. You probably need to follow the header and implementation file defined here
My goal was to convert your swift code into Obj C code that would compile, I cannot say if it is right for React Native however.

WKWebView button not showing

I'm making a WKWebView app, and I'd like to display a button that when clicked will take me to the homepage of my website. The WebView takes up the whole screen, and when I place a button in the Main.Storyboard file it doesn't show up (I think it's because the webview overlaps it or something).
Here's my ViewController.m
//
// ViewController.m
// FLWebView
//
// Created by Steve Richey on 11/21/14.
// Copyright (c) 2014 Float Mobile Learning. Shared under an MIT license. See license.md for details.
//
#import "ViewController.h"
// Required for calls to UIWebView and WKWebView to "see" our categories
#import "UIWebView+FLUIWebView.h"
#import "WKWebView+FLWKWebView.h"
#interface ViewController ()
#end
#implementation ViewController
/*
* Called when the view has completed loading. Time to set up our WebView!
*/
- (void) viewDidLoad {
[super viewDidLoad];
// Check if WKWebView is available
// If it is present, create a WKWebView. If not, create a UIWebView.
if (NSClassFromString(#"WKWebView")) {
_webView = [[WKWebView alloc] initWithFrame: [[self view] bounds]];
} else {
_webView = [[UIWebView alloc] initWithFrame: [[self view] bounds]];
}
// Add the webView to the current view.
[[self view] addSubview: [self webView]];
// Assign this view controller as the delegate view.
// The delegate methods are below, and include methods for UIWebViewDelegate, WKNavigationDelegate, and WKUIDelegate
[[self webView] setDelegateViews: self];
// Ensure that everything will resize on device rotate.
[[self webView] setAutoresizingMask: UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[[self view] setAutoresizingMask: UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
// Just to show *something* on load, we go to our favorite site.
[[self webView] loadRequestFromString:#"http://LiftedFeed.com/"];
}
/*
* Enable rotating the view when the device rotates.
*/
- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation
{
return YES;
}
/*
* This more or less ensures that the status bar is hidden for this view.
* We also set UIStatusBarHidden to true in the Info.plist file.
* We hide the status bar so we can use the full screen height without worrying about an offset for the status bar.
*/
- (BOOL) prefersStatusBarHidden
{
return YES;
}
#pragma mark - UIWebView Delegate Methods
/*
* Called on iOS devices that do not have WKWebView when the UIWebView requests to start loading a URL request.
* Note that it just calls shouldStartDecidePolicy, which is a shared delegate method.
* Returning YES here would allow the request to complete, returning NO would stop it.
*/
- (BOOL) webView: (UIWebView *) webView shouldStartLoadWithRequest: (NSURLRequest *) request navigationType: (UIWebViewNavigationType) navigationType
{
return [self shouldStartDecidePolicy: request];
}
/*
* Called on iOS devices that do not have WKWebView when the UIWebView starts loading a URL request.
* Note that it just calls didStartNavigation, which is a shared delegate method.
*/
- (void) webViewDidStartLoad: (UIWebView *) webView
{
[self didStartNavigation];
}
/*
* Called on iOS devices that do not have WKWebView when a URL request load failed.
* Note that it just calls failLoadOrNavigation, which is a shared delegate method.
*/
- (void) webView: (UIWebView *) webView didFailLoadWithError: (NSError *) error
{
[self failLoadOrNavigation: [webView request] withError: error];
}
/*
* Called on iOS devices that do not have WKWebView when the UIWebView finishes loading a URL request.
* Note that it just calls finishLoadOrNavigation, which is a shared delegate method.
*/
- (void) webViewDidFinishLoad: (UIWebView *) webView
{
[self finishLoadOrNavigation: [webView request]];
}
#pragma mark - WKWebView Delegate Methods
/*
* Called on iOS devices that have WKWebView when the web view wants to start navigation.
* Note that it calls shouldStartDecidePolicy, which is a shared delegate method,
* but it's essentially passing the result of that method into decisionHandler, which is a block.
*/
- (void) webView: (WKWebView *) webView decidePolicyForNavigationAction: (WKNavigationAction *) navigationAction decisionHandler: (void (^)(WKNavigationActionPolicy)) decisionHandler
{
decisionHandler([self shouldStartDecidePolicy: [navigationAction request]]);
}
/*
* Called on iOS devices that have WKWebView when the web view starts loading a URL request.
* Note that it just calls didStartNavigation, which is a shared delegate method.
*/
- (void) webView: (WKWebView *) webView didStartProvisionalNavigation: (WKNavigation *) navigation
{
[self didStartNavigation];
}
/*
* Called on iOS devices that have WKWebView when the web view fails to load a URL request.
* Note that it just calls failLoadOrNavigation, which is a shared delegate method,
* but it has to retrieve the active request from the web view as WKNavigation doesn't contain a reference to it.
*/
- (void) webView:(WKWebView *) webView didFailProvisionalNavigation: (WKNavigation *) navigation withError: (NSError *) error
{
[self failLoadOrNavigation: [webView request] withError: error];
}
/*
* Called on iOS devices that have WKWebView when the web view begins loading a URL request.
* This could call some sort of shared delegate method, but is unused currently.
*/
- (void) webView: (WKWebView *) webView didCommitNavigation: (WKNavigation *) navigation
{
// do nothing
}
/*
* Called on iOS devices that have WKWebView when the web view fails to load a URL request.
* Note that it just calls failLoadOrNavigation, which is a shared delegate method.
*/
- (void) webView: (WKWebView *) webView didFailNavigation: (WKNavigation *) navigation withError: (NSError *) error
{
[self failLoadOrNavigation: [webView request] withError: error];
}
/*
* Called on iOS devices that have WKWebView when the web view finishes loading a URL request.
* Note that it just calls finishLoadOrNavigation, which is a shared delegate method.
*/
- (void) webView: (WKWebView *) webView didFinishNavigation: (WKNavigation *) navigation
{
[self finishLoadOrNavigation: [webView request]];
}
#pragma mark - Shared Delegate Methods
/*
* This is called whenever the web view wants to navigate.
*/
- (BOOL) shouldStartDecidePolicy: (NSURLRequest *) request
{
// Determine whether or not navigation should be allowed.
// Return YES if it should, NO if not.
return YES;
}
/*
* This is called whenever the web view has started navigating.
*/
- (void) didStartNavigation
{
// Update things like loading indicators here.
}
/*
* This is called when navigation failed.
*/
- (void) failLoadOrNavigation: (NSURLRequest *) request withError: (NSError *) error
{
// Notify the user that navigation failed, provide information on the error, and so on.
}
/*
* This is called when navigation succeeds and is complete.
*/
- (void) finishLoadOrNavigation: (NSURLRequest *) request
{
// Remove the loading indicator, maybe update the navigation bar's title if you have one.
}
#end

webViewDidFinishLoad not working?

In my app, I am trying to make a splash image appear as my UIWebView loads so it is not just a blank screen. However my webViewDidFinishLoad method will not work. This means that the splash image appears but does not disappear from the screen once the UIWebView has loaded.
My code for the method is:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(#"content loading finished");
[loadingImageView removeFromSuperview];
}
Any help on why the method will not work would be appreciated greatly.
My .h:
#interface ViewController : UIViewController
-(IBAction)makePhoneCall:(id)sender;
#property (nonatomic, strong) IBOutlet UIWebView *webView;
#property(nonatomic, strong) UIImageView *loadingImageView;
#end
My ViewDidLoad and webViewDidFinishLoading:
- (void)viewDidLoad {
UIWebView *mWebView = [[UIWebView alloc] init];
mWebView.delegate = self;
mWebView.scalesPageToFit = YES;
[super viewDidLoad];
}
//**************** Set website URL for UIWebView
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.sleafordpizza.com/food"]]];
//**************** Add Static loading image to prevent white "flash" ****************/
UIImage *loadingImage = [UIImage imageNamed:#"LittleItalyLogo.png"];
loadingImageView = [[UIImageView alloc] initWithImage:loadingImage];
loadingImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"LittleItalyLogo.png"],
nil];
[self.view addSubview:loadingImageView];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(#"content loading finished");
// Remove loading image from view
[loadingImageView removeFromSuperview];
}
Hi probably you do not set proper delegate.
This is small code tip for you.
-(void)viewDidLoad {
mWebView = [[UIWebView alloc] init];
mWebView.delegate = self;
mWebView.scalesPageToFit = YES;
}
-(void)webViewDidFinishLoad:(UIWebView *)webView {
[loadingImageView removeFromSuperview];
NSLog(#"finish");
}
In you're .h file add.
#interface MyView: UIViewController <UIWebViewDelegate> {
UIWebView *webView;
}
Code fixes.
For .h file
#interface ViewController : UIViewController<UIWebViewDelegate>
-(IBAction)makePhoneCall:(id)sender;
#property (nonatomic, strong) IBOutlet UIWebView *webView;
#property(nonatomic, strong) UIImageView *loadingImageView;
#end
For .m file
- (void)viewDidLoad
{
[super viewDidLoad];
webView.delegate = self;
//**************** Set website URL for UIWebView
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.sleafordpizza.com/food"]]];
//**************** Add Static loading image to prevent white "flash" ****************/
UIImage *loadingImage = [UIImage imageNamed:#"LittleItalyLogo.png"];
loadingImageView = [[UIImageView alloc] initWithImage:loadingImage];
loadingImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"LittleItalyLogo.png"],
nil];
[self.view addSubview:loadingImageView];
}
At certain times, this delegate method actually never gets fired. I have had severe problems with the same thing in some of my projects.
At one occasion, I actually had to solve it with a timer, checking the state of the web view every second or so to see if I could proceed.
In that particular case, I just needed a certain element to be present. Still, the view did not trigger the finish loading event, due to external script errors being injected.
So, I just started a trigger when the web view begun loading, then called a method every now and then to see if the web view contained the element in question.
- (void)methodCalledByTimer {
if (<I still do not have what I need>) {
//The web view has not yet finished loading; keep checking
} else {
//The web view has finished loading; stop the timer, hide spinners and proceed
}
}
You could also check if the web view is actually loading, if that is absolutely necessary:
- (void)methodCalledByTimer {
if (self.webView.isLoading) {
//The web view has not yet finished loading; keep checking
} else {
//The web view has finished loading; stop the timer, hide spinners and proceed
}
}
Then, naturally, I'd check for the finishedLoading event as well, just to be sure. Remember to also implement the webView:didFailLoadWithError: method as well.
When waiting for a web page to finish loading, there are some things to keep in mind.
For instance, do you really need it to stop loading, or is there anything else you can do? In my case, I needed an element. Being able to properly execute a script is another thing that may be required.
Second, is the loading page using any external resources? I once had external script errors causing the webViewDidFinishLoad: method to not being called at all. If I removed the external scripts, it worked.
Third, if the page is using external resources, you are exposed not only to the loading capacity of your own resources, but that of the external resources as well. Tracking scripts, ads etc...if one resource provider is delivering content sloooowly (or not at all), you could page could be stuck in loading state forever.
So, I'd go with checking for something else. :)
I see you aren't handling errors. If there is an error, all subsequent delegate calls will not happen. I was surprised to find that this is true when the webview uses a plugin too. It calls this error method telling you that the webview handed off to the delegate, in my case the movie player.
implement this and see if that is it.
-(void) webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
if (error.code == 204) {
//request was handled by a plugin instead of by the webview directly
...
}
else
{
NSLog(#"didFailLoadWithError. ERROR: %#", error);
}
}
I was able to do all the remaining loading work in this method instead of the webviewdidfinishLoad

Common webViewDidStartLoad by category

I think implement completely same method to every viewController with UIWebView (and delegate=self)is not smart. So tried to setup common loading method with all UIWebView. But it did not work.
Is it wrong to achieve with category?
UIWebView+Loading.m
-(void)webViewDidStartLoad:(UIWebView*)webView
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
ViewController.h
#interface ViewController : UIViewController <UIWebViewDelegate>
ViewController.m
#import "UIWebView+Loading.h"
//abbr...
-(void)viewWillAppear:(BOOL)animated
{
UIWebView *someWebView = [[UIWebView alloc] init];
someWebView.delegate = self;
//and HTTP request
NSURLRequest *req = (abbr);
[someWebView loadRequest:req];
}
The method webViewDidStartLoad: is not being called because it is a part of UIWebViewDelegate protocol, not a method of UIWebView class itself. You have to implement it in your ViewController.m file.
Also, you may want to read about delegation pattern in iOS.
Dont need a category like UIWebView+Loading.m for this purpose.
The webview delegate methods will get executed when the loading stats and the delegate method
- (void)webViewDidStartLoad:(UIWebView *)webView {
NSLog(#"Started loading");
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(#"Finshed loading");
}
will get executed.The code you written is enough.Since the delegate is set to self .In the viewcontroller define these methods and thats it .You will have it working

webView shouldStartLoadWithRequest works in one program but can't get it to work in another

I've using shouldStartLoadWithRequest very successfully in one of my programs, but the whole project was a proof of concept and scruffy and I'm starting afresh with a new project.
However shouldStartLoadWithReqest is no longer being invoked for me but I can't see where the important difference between the two projects is (however one difference is the first is using .nibs, in the 2nd I'm not using them).
To get things started I'm using a controller with the UIWebView as its view:
#interface IMSRootController : UIViewController <UIWebViewDelegate> {
UIWebView* webView;
}
(webView is declared as a #property and #synthesized)
- (void)loadView {
[super loadView];
webView = [[UIWebView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view = webView;
[webView release]; }
- (void)viewDidLoad {
[super viewDidLoad];
[[self navigationController] setNavigationBarHidden:YES animated:NO];
[self displayPage]; }
-(void) displayPage { ... [webView loadHTMLString:self.htmlString baseURL:baseURL]; }
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
...
What's wrong?
Thanks
Your object is not being set as a delegate of the UIWebView object, hence you will not receive any delegate messages. At some point, either in loadView or even displayPage (but before the call to loadHTMLString:baseURL:), do:
webView.delegate = self;

Resources