Wait for some completion in main thread using Catalyst environment

I need to bring an iOS application to macOS using Catalyst. This application contain parts where it waits for a button to be pressed in the main thread, using

[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];

in a while loop to allow some dispatching while waiting. I know that this is not good style, but I need to convert this old source code and mentioned that when using this part of code under Catalyst, the main thread will not dispatch. So the button cannot be clicked, and a beach ball appears after two seconds.

I saw a similar construct for native macOS applications:

NSEvent *event= [[NSApplication sharedApplication] nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate dateWithTimeIntervalSinceNow:0.1] inMode:NSDefaultRunLoopMode dequeue:YES];
if (event) {
    [[NSApplication sharedApplication] sendEvent:event]; }

but I do not have access to NSEvent and NSApplication in a Catalyst environment.

Question: I there any code snippet which I can use to achieve the above? I do not want to completely rewrite old code if there is a solution for this. Any ideas or hints are highly appreciated.

Thank you!

Markus

I need to bring an iOS application to macOS using Catalyst. This application contain parts where it waits for a button to be pressed in the main thread, using

 [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];

in a while loop to allow some dispatching while waiting. I know that this is not good style, but I need to convert this old source code and mentioned that when using this part of code under Catalyst, the main thread will not dispatch. So the button cannot be clicked, and a beach ball appears after two seconds.

So, my main advice here is to rewrite things so that you stop doing this. It might be possible to get this working (see below) but, in my experience:

  1. It's easier to rewrite things to avoid this kind of modal logic than it seems.

  2. The modal loop is harder to get working and tends to keep breaking and/or introducing new failures.

SO, let me start here:

I saw a similar construct for native macOS applications:

 NSEvent *event= [[NSApplication sharedApplication] nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate dateWithTimeIntervalSinceNow:0.1] inMode:NSDefaultRunLoopMode dequeue:YES];
 if (event) {
     [[NSApplication sharedApplication] sendEvent:event]; } 

but I do not have access to NSEvent and NSApplication in a Catalyst environment.

Making things a bit more specific, the issue is actually that you don't have "nextEventMatchingMask" or any real "access" to the direct event queue. NSApplication expose a "getEvent" API because, to be frank, it's very old and it was considered fairly "standard" for an API to provide direct access to the event queue.

That doesn't mean it was ever a good idea and, in fact, the code above is a terrible idea and always has been. It reenters the event dispatch system and, in the general case, opens the door to all sort of brain twisting bugs. In practice it only works at all because it's only used in case where the interface has been specifically configured in a way prevents most event from "arriving" (so the actual entry points are limited) and the actual call is occurring in "simple" event processing (so AppKit isn't being reentered while it's in the middle of a complex state).

Question: I there any code snippet which I can use to achieve the above? I do not want to completely rewrite old code if there is a solution for this. Any ideas or hints are highly appreciated.

Basically, "no", I don't think there is any way to make this work. The reason this works on iOS:

[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];

...is that iOS's runloop configuration is much simple than macOS. Basically, "everything" is in the default mode, so "everything" gets dispatched when you run in that mode. However, Catalyst is actually using AppKit's runloop system (you can see this if you sample an Catalyst app), which means it ends up following the same rules/behaviors.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you for your answer.

So, my main advice here is to rewrite things so that you stop doing this. It might be possible to get this working (see below)

I do not find anything below you referred to. Can you please advise?

Thanks and regards

Markus

I do not find anything below you referred to. Can you please advise?

To be honest, I didn't actually have a specific solution in mind. That was more of a general reference to all the details that followed, particularly if I'd overlooked something that might have worked. Note what I said here later:

Basically, "no", I don't think there is any way to make this work.

Having taken another look at this... I think my answer is basically still "no". It's POSSIBLE that you might find a way to make this work by findling with runloop modes, particularly with the AppKit modes. However, I'm not confident that would work at all and, if it did, I think it's very likely to introduce other problems. I would avoid the problem.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Kevin,

first of all, thank you very much for coming back to my question again. Only one last question: What do you mean by AppKit runloop modes? Are they accessible in a Catalyst environment?

Thanks Markus

Wait for some completion in main thread using Catalyst environment
 
 
Q