How do you view errors in UIWebView loadHTMLString:? - iphone

If Apple encounters errors during loadHTMLString, they throw them on a separate thread, with no stacktrace (in Xcode4), and no output to the console.
How do you listen to these errors, debug them, and - ultimately - react to them?
(FYI - I'm using loadHTMLString because I need to load a mix of local and remote resources, and this method provides the only simple way to do it, AFAIAA)
EDIT: ...sorry, to be clear: There are different errors that Apple may encounter. For instance, if it gets an error trying to load an embedded resource (e.g. a CSS file), it won't count that as a "page failed to load", in fact it will report a successful page load.
IMHO ... that is the correct behaviour: if the HTML-parser is able to recover from the error, I don't want "page failed to load". But the errors are still important - they tell us why the page is rendering e.g. without a background image, or with broken images.

For debugging purposes you can attach Safari Web Inspector. To do so:
Make sure you have a version of Safari installed that supports SWI - so at least Safari 6 on Mac OS X 10.7.4.
On the device (or simulator) you're using to test the app, enable Settings > Safari > Advanced > "Web Inspector".
Launch Safari.
Enable Safari > Preferences > Advanced > "Show Develop menu in menu bar".
Run the app and, when the webview you're working with displays, go to Safari > Develop > [device name or "iPhone simulator"] > [web address - it will be listed under the app name]
It will show you what static files have been loaded by the page. (If you want to see Javascript errors, you'll need to attach as normal, set a breakpoint on all exceptions - in SWI, not just in Xcode! - then select the root-level page and CMD+R to reload it.)
As far as I can tell, the only way to do this programmatically (for instance, if you want to retry failed page loads) is with an NSURLProtocol subclass.

Although I haven't used loadHTMLString:, you should be able to register your controller as a UIWebViewDelegate and implement the – webView:didFailLoadWithError: method which should call whenever it fails.

Assign a delegate to your webview and use this method in the delegate.
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
NSLog(#"Error is %#",error);
}

Related

How to open url in Safari and the get back to the app under UITests in Xcode 7?

This is my custom view where "LondonStreet" is a button.
When I tap that button I get url and open it in Safari (it works). Then I can go back, using "Back to Wishlist" button (it also works).
The problem is when I try to test this under UITests.
itemsTable.cells.elementBoundByIndex(0).buttons["addressButton"].tap() //press the button to open link in Safari
Along with this line:
app.statusBars.buttons["Back to Wishlist"].tap() //go back, doesn't work, although it was recorded by Xcode itself.
is an error:
UI Testing Failure - Failed to get screenshot within 5s.
And also in issue Navigator
UI Testing failure - Unable to update application state promptly.
Starting in iOS 11 you can interact with other applications using the XCUIApplication(bundleIdentifier:) initializer.
To get back to your app you'd do something like:
let myApp = XCUIApplication(bundleIdentifier: "my.app.bundle.id")
let safari = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari")
// Perform action in your app that opens Safari
safari.wait(for: .runningForeground, timeout: 30)
myApp.activate() // <--- Go back to your app
UI Testing cannot interact with anything outside of your application. In your scenario, the framework can no longer do anything once your app opens Safari.
To verify this, try printing out the app's hierarchy once Safari opens. You will notice that nothing in Safari nor the navigation bar will show up - you will only see your app's information.
print(XCUIApplication().debugDescription)
To open specific url in Safari on iOS 15:
safari.textFields["Address"].tap()
safari.textFields["Address"].typeText("www.urlToOpen.com")
safari.keyboards.buttons["Go"].tap()

Opening tel: links from UIWebView

I've searched and searched, but I can't seem to find a fix for this problem.
For some reason, I can not get 'tel:' links to work in a UIWebView. When the links are clicked, the message "The URL can't be shown" appears. Clicking on the same link in Safari works perfectly and dials the number.
This problem started with iOS 5. These links worked perfectly in iOS 4.2 and 4.3.
I'm not sure what other information might be useful, so please let me know if I need to clarify.
Thanks!
EDIT:
Here is the actual code in use...
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = request.URL;
if ([url.scheme isEqualToString:#"tel"]) {
return YES;
}
if (![url.scheme isEqualToString:#"http"] && ![url.scheme isEqualToString:#"https"]) {
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
return NO; // Let OS handle this url
}
}
[NSThread detachNewThreadSelector:#selector(startBusy) toTarget:self withObject:nil];
return YES;
}
If I take out the first if statement, the number gets dialed immediately with no confirmation. I'd really like it to function the way it used to by giving an alert, giving you the option to hit either 'Call' or 'Cancel' before dialing the number.
If launching as an HTML link, the tel URL scheme will be opened if they appear as:
1-408-555-5555
If you are launching from a native URL string (meaning you coded this in Objective-C and are not serving it via a WebView), your URL string should look like this:
tel:1-408-555-5555
Note: This only works with iOS devices that have the Phone app installed (that means iPhone only). iPad & iPod Touch devices will display a warning message.
Note 2: Ensure the phone numbers you are passing do not contain spaces or other special characters (such as * and #).
Code Feedback
Based on your code, things are a bit clearer now. You comment about how nothing happens when you leave the first if statement in the shouldStartLoadWithRequest method (where you return YES). This is exactly the behavior you should see because your app is not the Phone app. Only the Phone app can handle the tel: URL scheme. By returning YES, you are telling the OS that your app will handle the phone call, but it cannot. You get the call when that conditional is removed because the next block, which checks if ([[UIApplication sharedApplication] canOpenURL:url]) allows the sharedApplication (which, in this case, is the Phone app) to launch the call.
How Things Work & What You Want
The OS is not going to handle showing the Call/Cancel alert dialog for you. That is up to you. It shows up in Safari because the Safari app's shouldStartLoadWithRequest method undoubtedly responds to the tel: scheme by showing a UIAlertView. Your conditional for if ([url.scheme isEqualToString:#"tel"]) should, when YES, trigger a UIAlertView with a Call and Cancel button. On Call, you will tell the sharedApplication to openURL; on Cancel, you will not issue the call & you will also want to return NO so your app does not attempt to loadWithRequest.
Self-Correcting Edit
To be fair about errors in my own thought process, I'm leaving my responses above.
I believe the Call/Cancel dialog is, in fact, a feature of the OS. Apologies for the inaccuracy.
I'd also erroneously glanced over your code's passing off URL handling to sharedApplication only occurring when the scheme was http or https.
After another look at the code, I wonder if, by any chance you have debug options on in Safari? I believe this prevents the alert from popping up. Also--just to double-check the obvious--you aren't trying this inside the simulator, correct? What happens if you remove the conditional check for http/https and just use the canOpenURL check?
However, aside from the error in my comments on the conditional & dialog itself, you still should not be returning YES. To make a phone call, you should only be able to pull that off by passing it to sharedApplication:openURL and ensuring you return NO because your app is not the Phone app. The only reason you'd want to return YES in this method is if your app is going to handle a tel: link in a special way that doesn't involve sending it to the Phone app.
If you created the UIWebView in a .xib, select the UIWebView and check its attributes in the Attribute Inspector. The first heading should be 'Web View', and under that it provides a list of checkboxes marked 'Detection'. Ensure that 'Phone Numbers' is checked.

Return Back To Application From Browser

I'm implementing a website call on button touch(in iPhone), so my browser get called in that case and website get opened.
I'm using following code:
- (IBAction) websiteButtonTouched{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://www.google.com"]];
}
Now what I want is to COME BACK TO MY APPLICATION FROM BROWSER as In iPhone4.0 and Up the application remain running at background we only have to call that when browser quite...
Thanks In Advance.. :P
Why not create your own in-app browser? Just put a UIWebView inside a navigation controller then put an address bar and a search bar.
Please see this question:
iPhone - Open Application from Web Page
As for returning to application where you left off -- if the device supports backgrounding (iOS4+) and your backgrounded app wasn't unloaded (due to memory shortage), the app will return the point you left it.
However, you also have to handle the returning to correct point in app yourself, in the cases that the device doesn't support backgrounding, or app was unloaded due to memory shortage: you can do this by storing information about current state of the app before it exits.
This web page has some very nice flow charts which describe app backgrounding and foregrounding etc.:
http://www.cocoanetics.com/2010/07/understanding-ios-4-backgrounding-and-delegate-messaging

My links in a WebView fire custom methods, but the iPhone doesn't respond like simulator

I have a WebView that loads a simple html page in my resources folder. The page contains links. When the user touches on a link to a Web site, the iPhone will close my app and open Safari. Since the user may not want to leave my app right now, I use:
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
to trap the link touch and show him an alert box, asking him to confirm that he wants to leave my app and go to Safari right now.
Same for an email link, which would close my app and open the user's email program.
However, if I want to similarly trap when the user touches a "tel:402-555-1212" phone call link, it works on the simulator, but on the real device I just get a simple alert box that says "call 4025551212": it clearly is ignoring my custom code for the telephone number link.
Is that a bug? It happens the same way (success on simulator, not on iphone) whether or not I select "detects phone numbers" for the webview in IB. How do I get this to work on the iPhone?
2) And very closely related question: if I click to allow the phone call to happen, when the phone call ends my app returns to its opening page, closing all other views. Is this normal function? There was no memory warning. Did the phone close and then re-open my app? It doesn't do that if click on a link and go to safari: when I close safari my app doesn't open again.
Thanks in advance for any insight.
--Steve
maybe you should set the detectsPhoneNumbers property to NO

What's the best way to log debug info in an iphone app?

Is there some standard way or has anyone written something that allows you to log a message and have it be displayed either in a small scrolling section on the iphone screen or in a separate window in the iphone simulator?
Update:
For noobs like me and don't know, use the NSLog methods as decribed below and make sure you select Run->Console to display the console.
Would still like to know if anyone has written a simple logger that displays on the iphone itself....
I don't have enough 'reputation' to add a direct comment for your posting but: don't forget to go to XCode->Preferences->Debugging->On Start: Choose Show Console & Debugger
You can of course choose just the Console or whatever, but you'll probably want the Debugger to. To use that, just click to the left of the line you want to break at. You can also toggle from 'Activate' to 'Deactivate' so you if you know that there are a bunch of breakpoints you don't need to hit in the beginning of your application set the debugging to Deactive (in the debugging window) and then, before you hit the UI element in your app you want to debug, toggle that same button to Activate so your breakpoints become active. Otherwise, you could of course just click Continue until you got to your section.
Also, on the NSLog, if you start to accumulate a bunch of log statements, and you need to 'find' one in particular, it helps to do: NSLog(#"\n\n\nMy statement\n\n\n); which will give a bunch of line breaks. Also, for the uninitiated:
NSLog(#"My int: %d my BOOL: %d", myInt, myBOOL);
NSLog(#"My object of any NSObject: %#", anObjectOfAnyKind);
NSLog(#"My float: %f",myFloat);
Hope all of this is helpful and sorry if I got off track with the debugging bit ;)
The Objective-C (more correct, really) method is
NSLog(#"message");
But the standard C method will work also
printf("message");
Use NSLog(#"Log message");
If your have an application that is crashing then your can ask the users you the crash log. The crash log contains information about what the application was doing when it crashed and the stack trace.
iPhone app log files are also stored on your users computer, and are copied across everytime they sync their iPhone. ( Note that DEVICE_NAME will be the same name of your iPhone in iTunes, and each log file will begin with the name of the app. )
Mac OS X : /Library/Logs/CrashReporter/MobileDevice//
Windows XP: C:\Documents and Settings\Application Data\Apple computer\Logs\CrashReporter\
Windows Vista: C:\Users\AppData\Roaming\Apple computer\Logs\CrashReporter\MobileDevice\
For Swift, it's simply
print("log msg")