26

I am trying to learn about creating react-native modules for iOS and there is one aspect that came up

Official documentation on threading mentions this block of code alongside its variations

- (dispatch_queue_t)methodQueue
{
  return dispatch_get_main_queue();
}

There is another undocumented peace I saw a lot in the third party libraries which is this

+ (BOOL)requiresMainQueueSetup
{
    return NO;
}

To me, these look kinda similar yet different, hence I wanted to ask for an explanation of following questions

  1. When should dispatch_get_main_queue be added to the module and what happens if it is omitted?

  2. When should requiresMainQueueSetup be added to the module and what happens if it is omitted?

  3. Can dispatch_get_main_queue and requiresMainQueueSetup be used together, if so why and when?

  4. What is the difference between returning YES and NO from requiresMainQueueSetup?

Ilja
  • 44,142
  • 92
  • 275
  • 498

3 Answers3

36
  1. You need dispatch_get_main_queue() whenever you are processing events on a secondary thread which will influence the main thread. Typically this involves UI changes. If you are creating a react-native module which does not involve native rendering you will probably not need the main queue. Async stuff should be invoked on a secondary thread, and this is where you would implement dispatch_get_main_queue() to make sure you UI gets updated when you async actions are completed.

  2. I asked this same question on SO a few weeks ago without success, and after some research I now know this is related to bullet number 1. React-native expects you to implement this method (is not in any way related to iOS), and you will need return YES in you want to do native iOS rendering. This will ensure that your native module is run on the main thread which is relevant in case of UI interactions. You don't want the application to freeze your UI in case of heavy duty processing.

  3. If you do not provide requiresMainQueueSetup() react-native will throw a warning in your face, but will set it to YES at this point. This default value will change in an upcoming release to NO. So to answer your question: they can be used together, but not every combination makes sense. Also in this case, if you are not creating a new native iOS UI component you will probably not need to access the main thread through dispatch_get_main_queue(). The react-native bridge will ensure that native events and methods are always communicated from iOS to JS and visa versa, regardless of which thread they are running on.

  4. This has been addressed in the previous bullets

Edit: Some additional information just to make sure everything is clear. To summarise: requiresMainQueueSetup() has nothing to do with iOS, and is only created by react-native to know what the intentions of your native module are (UI or other). dispatch_get_main_queue() has nothing to do with react-native and is only relevant to your native code. It is basically a callback for secondary threads to inform the main thread that some async actions are completed.

onmyway133
  • 45,645
  • 31
  • 257
  • 263
dentemm
  • 6,231
  • 3
  • 31
  • 43
  • 2
    This is an amazing explanation! To set this in stone for me, I am working on a module for gyroscope that returns me x, y, z values every 50ms or so, thinking about this it doesn't do any native UI rendering, but in react-native (js) when I receive new values I am performing ui changes. I'm a little unsure if this needs `dispatch_get_main_queue `? – Ilja Jun 09 '18 at 15:59
  • 2
    No you would not need dispatch_get_main_queue in this case. The react-native bridge will send new values to your JS code where the react-native JS thread will take control and you don’t have to worry about threading anymore – dentemm Jun 09 '18 at 16:34
2
  1. dispatch_get_main_queue should be added when your native module’s methods need access to UI (primarily) at runtime. It can be placed in instant methodQueue or other solution is to wrap a block of code such as *dispatch_async( dispatch_get_main_queue(), ^ {

    some code here })*

  2. requiresMainQueueSetup is a class method (indicated by + sign) and it works only at initialization time. So it is needed if your init method is calling UI or you override constantToExport method.

  3. It is addressed above.

  4. It is addressed above.

MariaG
  • 31
  • 1
0

There is official documentation about requiresMainQueueSetup and methodQueue in 'RCTBridgeModule.h' file which is provided with React Native library and it also contains other documented features for the react bridge module.

RCTBridgeModule.h

/**
 * The queue that will be used to call all exported methods. If omitted, this
 * will call on a default background queue, which is avoids blocking the main
 * thread.
 *
 * If the methods in your module need to interact with UIKit methods, they will
 * probably need to call those on the main thread, as most of UIKit is main-
 * thread-only. You can tell React Native to call your module methods on the
 * main thread by returning a reference to the main queue, like this:
 *
 * - (dispatch_queue_t)methodQueue
 * {
 *   return dispatch_get_main_queue();
 * }
 *
 * If you don't want to specify the queue yourself, but you need to use it
 * inside your class (e.g. if you have internal methods that need to dispatch
 * onto that queue), you can just add `@synthesize methodQueue = _methodQueue;`
 * and the bridge will populate the methodQueue property for you automatically
 * when it initializes the module.
 */
@property (nonatomic, strong, readonly) dispatch_queue_t methodQueue;

/**
 * Most modules can be used from any thread. All of the modules exported non-sync method will be called on its
 * methodQueue, and the module will be constructed lazily when its first invoked. Some modules have main need to access
 * information that's main queue only (e.g. most UIKit classes). Since we don't want to dispatch synchronously to the
 * main thread to this safely, we construct these modules and export their constants ahead-of-time.
 *
 * Note that when set to false, the module constructor will be called from any thread.
 *
 * This requirement is currently inferred by checking if the module has a custom initializer or if there's exported
 * constants. In the future, we'll stop automatically inferring this and instead only rely on this method.
 */
+ (BOOL)requiresMainQueueSetup;
iUrii
  • 11,742
  • 1
  • 33
  • 48