Working through dynamic actor isolation enforcement practically

Our project is a bit over a decade old with over a hundred targets and 18k+ types, about 40% of which are still Objective-C. With that we've managed to still stay fairly cutting-edge, we have complete concurrency checking turned on, nearly all upcoming feature flags available turned on so our Swift 5 looks a heck of a lot like Swift 6 (only Implicitly Open Existentials and Dynamic Actor Isolation remain).

That's been up until those last two flags. In particular the latter one as designed in SE-0423. Turning on that flag (or just running in Swift 6 mode without the disable flag in place) immediately results in runtime crashes. Obviously these are from assertions of areas where concurrency is not properly being used (main actors being initialized from operations, stuff like that), clearly the language is trying to help. However with such a large portion of our code being the "non-isolated" or at least "non-isolation-checking" Objective-C code, I can't help but wonder what sort of hell I'm about to endure trying to audit all of this for really complete concurrency.

We've so far done this in stages, a shared .xcconfig for the main app target and overriding ones at each sub-project layer where we would incrementally turn on complete concurrency checking and work our way up the stack. It worked, but it was grueling. I fear I'm headed for that direction again unless I can find more clever or powerful ways to "check" what is going to crash at runtime before runtime because we can't run the risk of shipping something that has such runtime "instability".

I guess I'm just wondering what sort of tools exist out there to check for these types of areas. It seems like something maybe the static analyzer would do since this type of checking isn't built into the Objective-C compiler (nor would I expect it to be) but so far nothing on that front. Are there any techniques anyone out there reading this has used that proved useful? How long can we expect to be able to go on Swift 6 (if we finally feel safe enough to make the jump) with the "disable" flag turned on (mentioned in the proposal and in the migration docs).

Honestly with some of the back-and-forth on functionality (especially regarding concurrency) I'm afraid to make any effort to adapt to that and just stay planted where we are from a language version perspective until some of the dust settles, but wanting to maybe spur some discussion surrounding how to approach this since I know there have got to be other large apps with tons of legacy code like we have.

1 Like