Search code examples
iosswiftwatchkitapple-watchwatchos-2

WatchOS: Should UI updates from the extension be called on the main thread?


For iOS apps, UI updating is done from the main thread exclusively - not doing so is never recommended and can lead to unexpected behaviour.

In watchOS, the OS is structured with a watch extension, and an app - as different 'containers'. Usually, UI updates are called from the extension, and these update something in the app's container.

Does the same main thread logic apply to updating UI from the watch extension, or can UI updates be called from the background?


Edit - to bring some clarity. From the app's container, UI updates should probably happen on the main thread (as happens in most systems/OSs, as pointed out below). The question is really whether watchOS handles that for us, i.e. whether calling a UI update on the background thread of the extension will automatically be posted to the main thread of the app's container for us.


Solution

  • After contacting Apple through a Technical Support Incident, the received answer and explanation is below.

    TLDR: use the main thread.

    All updates should be done from the main thread. This has always been the general recommendation for UIKit and that recommendation extends to watchOS.

    It might be helpful to understand the underlying reason for this requirement. Keep in mind that, even with a centralized communication channel to serialize changes, many problems arise when you attempt to manipulate UI state from background threads. For example, while the serialization channel can prevent multiple UI commands from attempting to simultaneously execute, it can’t control the order in which unrelated commands will execute. Consider the following 2 blocks:

    block 1 {     
      DoUIChange1     
      DoUIChange2     
    }
    
    block 2 {     
      DoUIChange3     
      DoUIChange4     
    }
    

    If both blocks are executed on the main thread, then the actual command stream is either:

    DoUIChange1   
    DoUIChange2   
    DoUIChange3   
    DoUIChange4
    

    or…

    DoUIChange3   
    DoUIChange4   
    DoUIChange1   
    DoUIChange2
    

    However, if both blocks are executed on their own threads, even more possibilities open up:

    DoUIChange3   
    DoUIChange1   
    DoUIChange2   
    DoUIChange4
    

    or..

    DoUIChange1   
    DoUIChange3   
    DoUIChange2   
    DoUIChange4
    

    or..

    DoUIChange1   
    DoUIChange3   
    DoUIChange4   
    DoUIChange2
    

    etc…

    Needless to say, if the UI code is at all complex the number of combinations quickly becomes enormous, making unexpected UI bugs basically unavoidable.