I have an UIWebView in my iOS app, which displays a very long web page. If the page fails to load for some reason, I display an alert in my webView:didFailLoadWithError: method. However, it is possible that user would tap some link BEFORE the page is fully loaded, and UIWebView treats that as an error as well, which I want to ignore silently. Is there any way to find out that a page loading error originates from user tapping a link?
As user1135469 suggested, error.code seems to be the key. It's NSURLErrorCancelled when the user taps the link, and it's safe to just ignore this error code.
Related
I have a very specific issue with the wkwebview. When a user opts to use the Settings / Screen Time / Content Restrictions / web content / Allowed Websites Only then the user will be presented with a Native ui component which says Restricted Site and gives the user an option to allow the website via a Allow website button. The problem is that the website exception is indeed added to allowed list in setting but the following action of the button is blocked.
[Process] 0x10781e618 - [pageProxyID=8, webPageID=9, PID=6619] WebPageProxy::Ignoring request to load this main resource because it was handled by content filter
That means the user is stuck on the screen, which of cause is not ideal. To solve this we added an alert with the option to return to the login page (better messaging is required).
I have tried all WKNavigationDelegate and WKUIDelegate hooks which are not trigger upon clicking on the Allow Website button and I could not find any documentation around that feature. I guess the only way is to educate the user by showing a dialog with instruction to add a bunch of urls to the allowed sites section of screen time and then restart the app or reload the webview via the dialog. The whole process seems very clunky.
If anyone has some more information about this it would highly appreciated.
UPDATE:
Turns out that screen time has 2 ways of dealing with restricted content. With or without a passcode. If a passcode is set upon clicking the allow website button you will open a enter passcode dialog, which will reload the webview when entered correctly. If you don't have a passcode set the webview will not reload. In my answer below I was using a javascript to reload the webview after the html button was clicked (the shown view is not native but a local html file.) The problem with that approach is that it will crash the the app if a passcode is set.
I was hoping to find a way to know if the passcode screen has been presented but I was not successful yet. If I had that information then I could stop the webview from reloading and therefor stop the crash.
I have been learning more about this, and as it turns out the screen which shows the Restricted Site message isn't a native component but rather a system html file which gets loaded into the webview.
file:///System/Library/PrivateFrameworks/WebCore.framework/ContentFilterBlockedPage.html
The Allow website link contains a href of x-apple-content-filter://unblock which when clicked will add the blocked site to the allowed websites list (it seems to be handled outside of the WKNavigationDelegate scope). In order to refresh the webview, you need to inject a small javascript which reloads the page after you clicked the link.
(function(){
const element = document.querySelector('#unblock a');
console.log("####unblock####");
console.log("ELEMENTS", element);
if (element) {
element.onclick = function() {
setTimeout(() => {
console.log("called message handler")
callMessageHandler("reloadWebView")
}, 1000);
};
}
})();
You can use the userscript messageHandler functionality to reload the url, my example is incomplete but its not hard to work this out.
I hope this helps someone.
Currently, I have to create automation ui test with XCUI and I have some action to open external browser by default is Safari.
I need to create some uitest's behavior like this.
Go to external browser when click my button.
Check is open correct url in browser (or just open browser) ?
Go back to our app with resume state.
Is it impossible to do that ?.
Absolutely. Safari is an XCUIApplication just like any other with a bundle identifier of com.apple.mobilesafari.
To check the URL you'll tap() the URL field (it's a button when a page loads) and read the value of the URL field (at this point it's a textField). Once you're done asserting that, activate() your app and you'll be back in it (note: my tests finish after asserting so I don't have to do this, but it's the published method - you could always enter debug and find how to tap the button to return to your application in the top-left of the screen if this doesn't work).
I'm happy to provide exact code if you show me you've tried this and can't get it working, but it's pretty straightforward XCUI automation.
Providing the exact code, that worked for me
enum SafariError: Error {
case appLoadTimeout
}
let safari = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari")
app.buttons["MyButtom"].tap() // MyButton launches Safari
guard safari.wait(for: .runningForeground, timeout: 5) else {
throw SafariError.appLoadTimeout
}
safari.otherElements["Address"].tap()
XCTAssertEqual(safari.textFields["Address"].value as! String, "https://check-url.com")
app.activate() //Back to my app
The previous answers work fine for an external browser. If you app happens to launch inline browsers, they won't work as there is no URL bar. You could detect the inline browser and tap the Safari button to launch it externally before checking the URL, but then you'd have to reactivate your app and close the inline browser. Many more steps.
THERE IS AN EASIER WAY THAT WILL WORK WITH BOTH BROWSER TYPES!
What do these browsers share? A share button! How can we get the URL from there? Copy will place the URL in the pasteboard, which we have access to.
app.buttons["ShareButton"].tap()
_ = copyButton.waitForExistence(timeout: Waits.short.rawValue) // a tiny wait is necessary here for the share page to open fully
app.buttons["Copy"].tap()
let URL = UIPasteboard.general.string!
There are two catches here:
When inline, app is your app. When external, it's com.apple.mobilesafari. You'll have to have logic to handle that.
You want to wait for the page to finish loading. By not doing so, you will run into flakiness. When the page is loading, there is a ReloadButton. When that goes away, you should expect to see a StopButton. If the reload button doesn't disappear, the page isn't loading completely so you may need to hit the stop button, but do so in a safe way as I have seen maybe 1% of the time the page load completes in the milliseconds between giving up on it and performing that tap - I do a quick isHittable check before tapping.
I have a webpage that uses location data from the browser that I want to open up inside an ios application.
If I open this page up inside a UIWebview I will see the page ask for location and after I respond the page finishes loading.
However, when I try to open this page up inside of a SFSafariViewController I do not get prompted for location which prevents the page from loading. I definitely want to use SFSafariViewController but I first need to fix this problem.
I've been unable to find any information about using location with SFSafariViewController. Does anyone know how to get this working?
We have an ad partner that is redirecting users to the app store after an ad in our app is tapped. We load an in-app browser which does the redirect. Nothing in the browser ever loads, it is just a white screen. Once the user returns to our app they are looking at that empty browser. Is there anyway to dismiss or close that browser once the redirect is completed?
We don't have any control over the server side.
What kind of in-app browser you are using in your app? Like a library or an UIWebView? If it's an UIWebView, there's a callback method when the webpage is loaded (and probably, redirected the link):
- (void)webViewDidFinishLoad:(UIWebView *)webView;
If you're using a library, I'm pretty sure it also has a method like that.
I fixed this by skipping the in-app browser and used Safari instead. App store re-directs are happening without showing the browser window and all the other ads are working as expected.
Plus I got to delete a bunch of old code.
I make Application with webview to my Application. when i want to Login some pages and i want to login saites the confirm Message is displayed.and it says that do you want the browser to remember this password. I don't want to show this Message and i dont want to click on never
what should I do to stop showing this Message and what should i do to Not displayed this Message
you can see the see screenshot
http://i.stack.imgur.com/1kulj.jpg
You can turn this behavior off with the WebSettings.setSavePassword() API. Please see http://developer.android.com/reference/android/webkit/WebSettings.html#setSavePassword(boolean)