JavaScript
console.log
iOS
UIWebView
debugging

Javascript console.log in an iOS UIWebView

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

UIWebView is legacy technology, but many older iOS codebases still contain it. One of the biggest debugging frustrations is that JavaScript console.log output is not surfaced the way it is in a desktop browser. The usual workaround is to override console.log in JavaScript and forward the message to native code.

Why console.log Is Awkward in UIWebView

UIWebView predates the better debugging tools that later arrived with WKWebView. In a legacy app, JavaScript runs inside the embedded web view, but the standard browser console is not readily exposed to you in the app UI.

That means console.log("hello") may execute, but you do not see the output anywhere useful.

Bridge console.log to Objective-C

A classic workaround is:

  1. inject JavaScript that replaces console.log
  2. make the replacement trigger a custom URL scheme
  3. intercept that request in the UIWebViewDelegate
  4. print the message in native code

The injected JavaScript can look like this:

javascript
1(function () {
2  var oldLog = console.log;
3
4  console.log = function (message) {
5    oldLog.apply(console, arguments);
6    var text = encodeURIComponent(String(message));
7    window.location = "js-log:" + text;
8  };
9})();

When console.log runs, it navigates to a fake URL beginning with js-log:. Native code can intercept that navigation and stop it.

Intercept the Log Message in UIWebViewDelegate

In Objective-C, implement webView:shouldStartLoadWithRequest:navigationType:.

objective-c
1- (BOOL)webView:(UIWebView *)webView
2shouldStartLoadWithRequest:(NSURLRequest *)request
3 navigationType:(UIWebViewNavigationType)navigationType
4{
5    NSURL *url = request.URL;
6    if ([[url scheme] isEqualToString:@"js-log"]) {
7        NSString *message = [[url resourceSpecifier]
8            stringByRemovingPercentEncoding];
9        NSLog(@"JS: %@", message);
10        return NO;
11    }
12    return YES;
13}

Now JavaScript log calls appear in the Xcode console through NSLog.

Inject the Override After the Page Loads

Once the page finishes loading, inject the JavaScript override from native code.

objective-c
1- (void)webViewDidFinishLoad:(UIWebView *)webView
2{
3    NSString *script =
4        @"(function(){"
5         "var oldLog = console.log;"
6         "console.log = function(message){"
7         "oldLog.apply(console, arguments);"
8         "var text = encodeURIComponent(String(message));"
9         "window.location = 'js-log:' + text;"
10         "};"
11         "})();";
12
13    [webView stringByEvaluatingJavaScriptFromString:script];
14}

After this, a script in the page such as:

javascript
console.log("page loaded");

will produce native console output.

Handle Multiple Arguments More Carefully

console.log often receives multiple arguments, not just one string. If you want better output, join all arguments before forwarding them.

javascript
1(function () {
2  var oldLog = console.log;
3
4  console.log = function () {
5    oldLog.apply(console, arguments);
6    var parts = Array.prototype.slice.call(arguments).map(function (value) {
7      return typeof value === "string" ? value : JSON.stringify(value);
8    });
9    window.location = "js-log:" + encodeURIComponent(parts.join(" "));
10  };
11})();

That handles calls like console.log("user", userObject) more cleanly.

Limitations of the URL-Scheme Technique

This bridge is useful, but it is still a workaround. The custom URL approach has limits:

  • it is noisy if the page logs frequently
  • it can interfere with navigation if implemented carelessly
  • large messages may be truncated or awkward to encode
  • it is only suitable for debugging, not production telemetry

For serious web-view debugging, WKWebView is the better platform.

Prefer WKWebView for New Work

If you can migrate, do it. WKWebView has better performance, a cleaner JavaScript bridge, and more modern inspection options. For legacy maintenance, the console.log override pattern is still practical, but it should be treated as a compatibility technique rather than a long-term design.

Common Pitfalls

Forgetting to return NO after intercepting the fake js-log: navigation. Then the web view tries to load the fake URL.

Injecting the override too early. Wait until the page has loaded or your script may not attach correctly.

Logging non-string objects without serialization. Convert them explicitly for readable output.

Using this as a production logging mechanism. It is fine for debugging, not for structured telemetry.

Ignoring the larger issue that UIWebView is deprecated. Use WKWebView for active development.

Summary

  • 'UIWebView does not expose JavaScript logs conveniently, so console.log often appears invisible.'
  • A common workaround is to override console.log and forward messages through a custom URL scheme.
  • Intercept that scheme in the UIWebViewDelegate and print with NSLog.
  • This is useful for legacy debugging, but WKWebView is the better long-term solution.
  • Keep the bridge simple and debugging-focused so it does not interfere with navigation.

Course illustration
Course illustration

All Rights Reserved.