I'm trying to load a web page in a WKWebView control without loading it as a subview:
WKPreferences *preferences = [[WKPreferences alloc] init];
preferences.javaScriptEnabled = YES;
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.preferences = preferences;
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
webView.navigationDelegate = self;
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest setValue:#"Safari" forHTTPHeaderField:#"User-agent"];
[webView loadRequest:urlRequest];
The url I'm trying to load is this:
https://www.google.com/search?q=mustang
The following WKNavigationDelegate method is not being called:
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
[webView evaluateJavaScript:#"document.getElementsByTagName('html')[0].outerHTML" completionHandler:^(id result, NSError *error) {
NSLog(#"Result %#", result);
}];
}
My NSObject class conforms to the WKNavigationDelegate protocol.
Do I need to do something else?
Greatly appreciate any help.
I had a WK Scheme Handler in my code and I used it for tracking requests done and completed (amongst other things).
It showed that if one resource load failed, the didFinishNavigation: did not get called.
So, your content needs to load in its entirety for this to ever be called.
Loading the same URL seems to work for me.
Do note:
The didFinishNavigation method is called after main frame load completes (this includes loading stuff from other hosts which are present in the main frame). So if something would be responding slow the didFinishNavigation method won't be invoked until resolved.
Related
my system version is iOS 11.2.6 but i think in higher version have the same appearance.
i post a request in wkwebview like this and it works fine
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
request.HTTPMethod = #"POST";
request.HTTPBody = [postStr dataUsingEncoding:NSUTF8StringEncoding];
WKWebView *webView = [[WKWebView alloc]initWithFrame:rectDown];
webview.frame = self.view.frame;
[webview loadRequest:request];
[self.view addSubview:webview];
but if i implement the decidePolicyForNavigationAction delegate method like down,the nodejs server can't receive any post data
-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:
(WKNavigationAction *)navigationAction decisionHandler:(void (^)
(WKNavigationActionPolicy))decisionHandler{
decisionHandler(WKNavigationActionPolicyAllow);
}
and I want to reuse a wkwebview object anyone have a good idea?
additional i found that when i first load the post request and back then reload and back again reload again it's always works right ,but if I reuse the wkwebview load any other url like google then it can't load the post request anymore.if if don't implement the delegate decidePolicyForNavigationAction method ,it will always works fine.
so ,this is a conflict between decidePolicyForNavigationAction and a reuse wkwebview?
I use this code to format the reuse wkwebview when the wkwebview's controller is remove
-(void)webFormat{
if (#available(iOS 9.0,*)) {
NSSet *websiteDataTypes = [NSSet setWithObjects:WKWebsiteDataTypeDiskCache,
WKWebsiteDataTypeOfflineWebApplicationCache,
WKWebsiteDataTypeMemoryCache,
nil];
// NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
}];
}
[self.configuration.userContentController removeScriptMessageHandlerForName:kScriptHandlerName];
[self.configuration.userContentController removeAllUserScripts];
[self stopLoading];
[self loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"about:blank"]]];
self.scrollView.delegate = nil;
[self setUIDelegate:nil];
}
I fix this by add code
self.navigationDelegate = nil
in webFormat function .Even when the function webFormat run ,self.navigationDelegate is nil already;I thought that when the viewController remove,as the navigationDelegate of wkwebview ,left something in the corner and influence the webview's next load.who knows.
All,
I am displaying a URL in UIWebview and i have to perform some click actions and I am able to using click function in javascript (I am able to get callbacks to objective C code as well).
Now i want to migrate to WKWebview and i have below code written with delegate methods
WKPreferences *preferences = [[WKPreferences alloc] init];
preferences.javaScriptEnabled = YES;
WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
configuration.preferences = preferences;
self.webKitView = [[WKWebView alloc] initWithFrame: [[self view] bounds]configuration:configuration];
self.webKitView.navigationDelegate = self;
[self.webKitView loadHTMLString:htmlString1 baseURL:nil];
[self.view addSubview:self.webKitView];
Delegate method
(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSURLRequest *request = navigationAction.request;
decisionHandler(WKNavigationActionPolicyAllow);
}
Now i am able to display same as in Webview, I am performing Click actions but i am not able to call.
Can any one how can i proceed
My goal with this code is to simply show the html code from the url request on a Label as the app is initialized:
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:#"http://192.168.25.242:8090"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[_webView loadRequest:request];
sleep(5);
self.myLabel.text = [_webView stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML"];
}
I think the url request take some time to end, so I used sleep function trying to wait for it to end and then convert the webview results into string and load on the label... But is shows nothing. When I create a button to "self.myLabel.text = [_webView stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML"];" after any webview load, it does work, and the label gets the html from the webview correctly.
What should I do?
The sleep() function won't work because the webView won't be given time to load. Instead, set _webView.delegate = self; and implement -webViewDidFinishLoad:, where you should set the label text.
I'm testing the WKWebView with local file, which is working in the simulator but it is not working in the device
#interface EDPresentationViewController ()<WKNavigationDelegate,WKScriptMessageHandler>
#property(nonatomic,strong)WKWebView *webView;
#property(nonatomic,strong)EDPresentationController *presentationController;
#end
#implementation EDPresentationViewController
-(void)viewDidLoad
{
[super viewDidLoad];
self.presentationController = [[EDPresentationController alloc]init];
WKWebViewConfiguration *webConfiguration = [[WKWebViewConfiguration alloc]init];
self.webView = [[WKWebView alloc]initWithFrame:self.view.frame configuration:webConfiguration];
NSURL *presentationFolder = [self.presentationController url];
NSURLRequest *request = [NSURLRequest requestWithURL:presentationFolder];
[self.webView loadRequest:request];
}
I grant the url from:
NSURL *presentationFolder = [self.presentationController url];
is ok, because I tested the same code with a UIWebview and works!
I always get the same error:
Could not create a sandbox extension for '/'
This wasn't work, I guess it would work in Objective-C as in swift
iOS Webkit not working on device, but works on simulator at swift
Any idea will be appreciated, thanks
Update 2-12-2014
I've discovered this could be a bug in iOS 8.1 and it may be fixed in 8.2
https://devforums.apple.com/thread/247777?start=25&tstart=0
I've tested moving the files to the temporary folder and I didn't get any error but the webView is just empty.
I've tested the same code (temporary folder) with a UIWebView and works fine!
Also, I've tried this:
https://stackoverflow.com/a/26054170/426180
As I could find out, this works because the css and the javascript is embebed in the html.
Try XWebView which has a very tiny embedded http server. It's much smaller than the GCDWebServer. A loadFileURL:allowingReadAccessToURL method is added through extension, so you are not aware of the server.
This worked like a charm ...
#interface ViewController () <WKScriptMessageHandler, WKNavigationDelegate>
...
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *controller = [[WKUserContentController alloc] init];
configuration.userContentController = controller;
[configuration.preferences setValue:#"TRUE" forKey:#"allowFileAccessFromFileURLs"];
self.webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds configuration:configuration];
self.webView.navigationDelegate = self;
// Also if you'd have bouncing problem
self.webView.scrollView.bounces = false;
self.webView.scrollView.alwaysBounceVertical = false;
[self.view addSubview:self.webView];
NSString* productURL = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"htmlapp/home.html"];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:productURL]];
[self.webView loadRequest:request];
I have a problem I have discovered in my app that has a UIWebView. iOS 7 caches a blank body 304 response, resulting in blank pages being shown when the user refreshes the UIWebView. This is not good user expierience and I'm trying to figure out how to solve this on the iOS side, as I do not have control over how Amazon S3 responds to headers (that's who I use for my resource hosting).
More details of this bug were found by these people: http://tech.vg.no/2013/10/02/ios7-bug-shows-white-page-when-getting-304-not-modified-from-server/
I'd appreciate any help offered to how I can solve this on the app side and not the server side.
Thank you.
Update: fixed this bug using the bounty's suggestion as a guideline:
#property (nonatomic, strong) NSString *lastURL;
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if ([self.webView stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML"].length < 1)
{
NSLog(#"Reconstructing request...");
NSString *uniqueURL = [NSString stringWithFormat:#"%#?t=%#", self.lastURL, [[NSProcessInfo processInfo] globallyUniqueString]];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:uniqueURL] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:5.0]];
}
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
self.lastURL = [request.URL absoluteString];
return YES;
}
You can implement a NSURLProtocol, and then in +canonicalRequestForRequest: modify the request to override the cache policy.
This will work for all requests made, including for static resources in the web view which are not normally consulted with the public API delegate.
This is very powerful, and yet, rather easy to implement.
Here is more information:
http://nshipster.com/nsurlprotocol/
Reference:
https://developer.apple.com/library/ios/documentation/cocoa/reference/foundation/Classes/NSURLProtocol_Class/Reference/Reference.html
Here is an example:
#interface NoCacheProtocol : NSURLProtocol
#end
#implementation NoCacheProtocol
+ (void)load
{
[NSURLProtocol registerClass:[NoCacheProtocol class]];
}
+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
{
if ([NSURLProtocol propertyForKey:#“ProtocolRequest” inRequest:theRequest] == nil) {
return YES;
}
return NO;
}
+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)theRequest
{
NSMutableURLRequest* request = [theRequest mutableCopy];
[request setCachePolicy: NSURLRequestReloadIgnoringLocalCacheData];
//Prevent infinite recursion:
[NSURLProtocol setProperty:#YES forKey:#"ProtocolRequest" inRequest:request];
return request;
}
- (void)startLoading
{
//This is an example and very simple load..
[NSURLConnection sendAsynchronousRequest:self.request queue:[NSOperationQueue currentQueue] completionHandler:^ (NSURLResponse* response, NSData* data, NSError* error) {
[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[[self client] URLProtocol:self didLoadData:data];
[[self client] URLProtocolDidFinishLoading:self];
}];
}
- (void)stopLoading
{
NSLog(#"something went wrong!");
}
#end
As the other questions are to use the NSURLConnection every time, which seems like a bit of an overhead:
Why don't you execute a small javascript after the page was loaded (complete or incomplete) that can tell you if the page is actually showing? Query for a tag that should be there (say your content div) and give a true/false back using the
[UIWebView stringByEvaluatingJavaScriptFromString:#"document.getElementById('adcHeader')!=null"]
And then, should that return false, you can reload the URL manually using the cache-breaker technique you described yourself:
NSString *uniqueURL = [NSString stringWithFormat:#"%#?t=%d", self.url, [[NSDate date] timeIntervalSince1970]];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:uniqueURL] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:5.0]];
[edit]
based on the discussion in the comments and some of the other answers, I think you might have the best solution manually changing the NSURLCache.
From what I gathered, you're mainly trying to solve a reload/reshow scenario. In that case, query the NSURLCache if a correct response is there, and if not delete the storedvalue before reloading the UIWebView.
[edit 2]
based on your new results, try to delete the NSURLCache when it is corrupted:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache]cachedResponseForRequest:request];
if (cachedResponse != nil && [[cachedResponse data] length] > 0)
{
NSLog(#"%#",cachedResponse.response);
} else {
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:request];
}
return YES;
}
We might have to refine the check if the cache is invalid again, but in theory this should do the trick!
NSURL *URL = [NSURL URLWithString:#"http://mywebsite.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:30.0];
[myWebView loadRequest: request];
When creating NSURLRequest instance, you can set Cache Policy.
Hope it works!