Objective-C threading considerations

Multithreading is one of the trickiest parts of interop between Objective-C and Dart. This is due to the relationship between Dart isolates and OS threads, and the way Apple's APIs handle multithreading:

  1. Dart isolates are not the same thing as threads. Isolates run on threads, but aren't guaranteed to run on any particular thread, and the VM might change which thread an isolate is running on without warning. There is an open feature request to enable isolates to be pinned to specific threads.
  2. While FFIgen supports converting Dart functions to Objective-C blocks, most Apple APIs don't make any guarantees about which thread a callback will run on.
  3. Most APIs that involve UI interaction can only be called on the main thread, also called the platform thread in Flutter.
  4. Many Apple APIs are not thread safe.

The first two points mean that a block created in one isolate might be invoked on a thread running a different isolate, or no isolate at all. Depending on the type of block you are using, this could cause your app to crash. When a block is created, the isolate it was created in is its owner. Blocks created using FooBlock.fromFunction must be invoked on the owner isolate's thread, otherwise they will crash. Blocks created using FooBlock.listener or FooBlock.blocking can be safely invoked from any thread, and the function they wrap will (eventually) be invoked inside the owner isolate, though these constructors are only supported for blocks that return void. FooBlock.blocking may add support for non-void return values in future, if there is user demand for it.

The third point means that directly calling some Apple APIs using the generated Dart bindings might be thread unsafe. This could crash your app, or cause other unpredictable behavior. In recent versions of Flutter, the main isolate runs on the platform thread, so this isn't an issue when invoking these thread-locked APIs from the main isolate. If you need to invoke these APIs from other isolates, or you need to support older versions of flutter, you can use the runOnPlatformThread function.

Regarding the last point, although Dart isolates can switch threads, they only ever run on one thread at a time. So, the API you are interacting with doesn‘t necessarily have to be thread safe, as long as it is not thread hostile, and doesn’t have constraints about which thread it's called from.

You can safely interact with Objective-C code as long as you keep these limitations in mind.