iOS UIWebView cannot get web title

Keywords: iOS JQuery less

Recently, I encountered a problem that in the proxy method of UIWebView, the js code executing document.title could not get the title of the page. The code is as follows:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    // Get the title name of the loaded html file
    NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
}

This problem, I first determine whether it is a code problem, after analysis, found that the code has not changed, but this time can not get the title of the page, it is very strange. After searching and analyzing, the problem is that in this edition, the front-end personnel set the title of the web page in asynchronous operation, resulting in UIWebView can not get the title immediately in the proxy method webViewDidFinishLoad: after loading the web page, because the method of getting the title is asynchronous, and the proxy method will be invoked after loading the web page, at that time the title of the web page has no value. So you can't get the title value.

The following is the code for asynchronous Title Acquisition on Web pages, using jQuery and Ajax technology to obtain title asynchronously:

    $.ajax({
        url: remoteur+'/api/innerMessageApi/noticeMessage.htm?callBackFunc=xx',
        type: 'get',
        dataType: 'jsonp',
        jsonpCallback:"xx",
        data: {msgId: msgId},

        success: function(res){
            console.log(res);
            if ( res.successFlag == 'Y' ){
                content = res.content;
                title   = res.title;
            }
        },
        complete:function(res){
            document.title = title;
            $('body').append(content);
        }
    })

When this problem arises, it is on the night when the project is going to go online, when working overtime collectively, we encounter this problem and feel deeply hurt that night...

Well, not much gossip. Here's the solution.

Method 1

If we only consider the solution of iOS, it may be to acquire title by delay, that is to say, document.title is acquired by delay in webViewDidFinishLoad: although it can be solved, there is a risk, because the title is acquired asynchronously, and the asynchronous time is uncertain, so the delay time is uncertain, although it can increase the delay time. But it's not the perfect solution.

   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)),        dispatch_get_main_queue(), ^{
       self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
   });

Method 2

If we consider the web side, we can change the asynchronous operation to synchronous operation. According to the above js code, we can add a synchronous field async: false. The modified web code is as follows:

    $.ajax({
        url: remoteur+'/api/innerMessageApi/noticeMessage.htm?callBackFunc=xx',
        type: 'get',
        dataType: 'jsonp',
        jsonpCallback:"xx",
        data: {msgId: msgId},
        // Setting up synchronization operation
        async: false,

        success: function(res){
            // Synchronize headings
             document.title = res.title;
        complete:function(res){
            ...
        }
    })

Although this can solve this problem, it is still not a very good solution, such as when the page is loaded, the title of the page is obtained synchronously. If the synchronization operation is blocked, then the loading of the page is blocked, which leads to the page can not be displayed, so it is still not the best solution.

Method 3

If we combine the webpage side and iOS side, we can get the title asynchronously on the webpage side. After getting the title, we can set the title by calling the native method through js. This method can not block the loading process of the webpage because of synchronous capturing of the title, nor can it cause the problem that the delay time can not be determined because of delayed capturing of the title. So this method can solve this problem perfectly.

js-side code:

    $.ajax({
        url: remoteur+'/api/innerMessageApi/noticeMessage.htm?callBackFunc=xx',
        type: 'get',
        dataType: 'jsonp',
        jsonpCallback:"xx",
        data: {msgId: msgId},
        // Setting up synchronization operation
        async: false,

        success: function(res){
            // Synchronize headings
            document.title = res.title;
            // js calls native methods to set titles
            setWebViewTitle(title); 
        complete:function(res){
            ...
        }
    })

iOS side code:

    context[@"setWebViewTitle"] = ^(){
       NSArray *args = [JSContext currentArguments];
       if (args.count == 1) {
            // Setting the title requires only one parameter to be passed
            self.title = [args firstObject];
        }
    };

Actually, there are many ways to invoke the native js. Here is just a relatively simple way. It is possible to use any specific way. If you don't know much about this knowledge, you can refer to other materials.

I found a lot of information on the Internet, and found that most of the dynamic modification of web page titles are synchronous operation, less introduction to the use of asynchronous operation to dynamically modify the title, so I summarized the dynamic method of setting the title as above, hoping to give some help to friends in need.

Posted by evanesq on Tue, 11 Jun 2019 13:41:09 -0700