Failed to block main thread with runloop on iOS15 with device iPhone12?

1.1k Views Asked by At

In my project, I use runloop to block main thread to implement getting user's click result of UIAlertController. here are test Viewcontroller.h and Viewcontroller.m codes:

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end


#import "ViewController.h"
@interface ViewController ()    
@end

static NSInteger kButtonIndex = -1;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    BOOL result = [self test];
    NSLog(@"Result: %d",result);
}

- (BOOL)test{
    kButtonIndex = -1;
    
    NSLog(@"000");

    UIAlertController *alerVC = [UIAlertController alertControllerWithTitle:@"Alert" message:@"Message" preferredStyle:UIAlertControllerStyleAlert];
    [alerVC addAction:[UIAlertAction actionWithTitle:@"Ok" style:(UIAlertActionStyleDestructive) handler:^(UIAlertAction * _Nonnull action) {
        kButtonIndex = 1;
    }]];
    [alerVC addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        kButtonIndex = 0;
    }]];

    NSLog(@"111");

    [self presentViewController:alerVC animated:YES completion:^{}];

    NSLog(@"222");

    while (kButtonIndex == -1) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];
    }

    if (kButtonIndex == 1) {
        return YES;
    }
    if (kButtonIndex == 0) {
        return NO;
    }

    return NO;
}
@end

On device iPhone 11 or older devices with any iOS versions, once you tap the screen, UIAlertViewController will show, if you click Ok button, the console logs should be like this:

2021-09-24 16:51:04.146063+0800 TestAlert[1520:46901] 000
2021-09-24 16:51:04.148558+0800 TestAlert[1520:46901] 111
2021-09-24 16:51:04.165442+0800 TestAlert[1520:46901] 222
2021-09-24 16:51:05.774194+0800 TestAlert[1520:46901] Result: 1

But if on device iPhone 12 or later devices with iOS 15, once you tap the screen, UIAlertViewController will not show, and the console logs should be like this:

2021-09-24 16:51:04.146063+0800 TestAlert[1520:46901] 000
2021-09-24 16:51:04.148558+0800 TestAlert[1520:46901] 111
2021-09-24 16:51:04.165442+0800 TestAlert[1520:46901] 222

Seems like that following codes doesnt work any more:

while (kButtonIndex == -1) {
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];
}

Does anybody come through this problem? Thanks!

Extra: Our project use Cordova and UIWebView, some remote h5 pages in this app are from other companies(if we change webview to WKWebView, these company need to update their js files like cordova.js, this is a tough work, so we stay at UIWebView)... In UIWebView, some users said that js confirm() fucntion doesnt show modal view any more on iOS15, I tried a lot, figure out that js confirm() function in UIWebView presented a UIAlertController.. and I tried to rewrite UIWebView confirm implementation like this, it works well in any device except iPhone 12 iOS15:

#import <UIKit/UIKit.h>
@interface UIWebView (Alert)
@end

#import "UIWebView+Alert.h"

@implementation UIWebView (Alert)

static NSInteger kButtonIndex = -1;

- (BOOL)webView:(UIWebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(id)frame{
    kButtonIndex = -1;

    UIAlertController *alerVC = [UIAlertController alertControllerWithTitle:@"Alert" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alerVC addAction:[UIAlertAction actionWithTitle:@"Ok" style:(UIAlertActionStyleDestructive) handler:^(UIAlertAction * _Nonnull action) {
        kButtonIndex = 1;
    }]];
    [alerVC addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        kButtonIndex = 0;
    }]];

    [[self topViewController] presentViewController:alerVC animated:YES completion:^{}];

    while (kButtonIndex == -1) {
        [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];
    }

    if (kButtonIndex == 1) {
        return YES;
    }
    if (kButtonIndex == 0) {
        return NO;
    }

    return NO;
}

@end

Actually, codes above is almost same as the foremost codes in my question, if anyone can offer help, you can run the foremost test codes to reappear the problem.

1

There are 1 best solutions below

2
Starman On

We are experiencing the same issue with animations on iOS 15 on iPhone 12. We were using [[NSRunLoop mainRunLoop] runUntilDate] to allow an animation to complete execution before continuing to execute code in the main thread. However, this doesn't work on iOS 15 on iPhone 12. The animation doesn't complete even when runUntilDate is called with a time exceeding the animation duration. This appears to only be an issue on iPhone 12 devices running iOS 15. Everything works properly in the simulator, on iPhone 11 devices, and even on an iPad Air 4 with the same processor as the iPhone 12. It appears that UI Code that would normally execute on the main thread when runUntilDate is called does not do so on iOS 15 / iPhone 12.