How do I run CoreBluetooth in background thread?

So I managed to get CoreBluetooth functionality working in an objective c++ commandline application, however corebluetooth blocks the main thread:


main.cpp

int main() {
  call_obj_c_corebluetooth_initialization(); // corebluetooth occupies main thread
  std::thread worker(do_cpp_stuff); // c++ code runs in background thread, but I want to run c++ in main loop
}


How can I set up corebluetooth properly when I initialize the CBCentralManager on a background thread?

std::thread worker(call_objective_c_code_to_initialize_cbcentralmanager)


things I know:

  • Must call [[NSRunLoop currentRunLoop] run] after CBCentralManager initialization because CoreBluetooth needs a runloop for delegate callbacks.
  • CoreBluetooth has a background mode (but I don't think background mode is activated with the background thread)


things I tried:

creating a serial dispatch queue and initializing CBCentralManager with it.

_centralQueue = dispatch_queue_create("centralqueue", DISPATCH_QUEUE_SERIAL);
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:_centralQueue options:nil];
[[NSRunLoop currentRunLoop] run];
Answered by pagonda in 421903022

Okay in a sudden fit of clarity, I solved this problem.


main.cpp

void do_corebluetooth stuff() {
*ptr = get_bluetooth_object();
}

int main() {
std::thread worker(do_corebluetooth_stuff);
}

corebluetooth.mm

void get_bluetooth_object() {
// initialize the object
void *ptr = [BlueToothObject alloc] init];
return ptr;
}

@implementation BlueToothObject

// override constructor
-(id)init {
self = [super init];
if (self) {
  std::cout << "init ObjC" << std::endl;
  std::cout << "ObjC running on: " << std::this_thread::get_id() << std::endl;
  if ([NSThread isMainThread]) {
  std::cout << "we're on the main thread" << std::endl;
  } else {
  std::cout << "we're not on the main thread" << std::endl;
  }
_centralQueue = dispatch_queue_create("centralmanager", DISPATCH_QUEUE_SERIAL);

@autoreleasepool {
  dispatch_async(_centralQueue, ^{
  std::cout << "inside here";
  self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:_centralQueue options:nil];


  NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
  while (([runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])) {
  }
  [[NSRunLoop currentRunLoop] run];
  });

};
  return self;
}

}


explanation: in c++ I dispatched a call to create a new CoreBlueToothObject object in a separate thread. Then I created a new serial dispatch queue that will be used to handle corebluetooth callbacks. Next I initialized the Central Manager using the new queue. Because CoreBluetooth needs a runloop to function and that's how callbacks are sent, I explicitly called a runloop on the current thread (which is not the main thread anymore).

Accepted Answer

Okay in a sudden fit of clarity, I solved this problem.


main.cpp

void do_corebluetooth stuff() {
*ptr = get_bluetooth_object();
}

int main() {
std::thread worker(do_corebluetooth_stuff);
}

corebluetooth.mm

void get_bluetooth_object() {
// initialize the object
void *ptr = [BlueToothObject alloc] init];
return ptr;
}

@implementation BlueToothObject

// override constructor
-(id)init {
self = [super init];
if (self) {
  std::cout << "init ObjC" << std::endl;
  std::cout << "ObjC running on: " << std::this_thread::get_id() << std::endl;
  if ([NSThread isMainThread]) {
  std::cout << "we're on the main thread" << std::endl;
  } else {
  std::cout << "we're not on the main thread" << std::endl;
  }
_centralQueue = dispatch_queue_create("centralmanager", DISPATCH_QUEUE_SERIAL);

@autoreleasepool {
  dispatch_async(_centralQueue, ^{
  std::cout << "inside here";
  self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:_centralQueue options:nil];


  NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
  while (([runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])) {
  }
  [[NSRunLoop currentRunLoop] run];
  });

};
  return self;
}

}


explanation: in c++ I dispatched a call to create a new CoreBlueToothObject object in a separate thread. Then I created a new serial dispatch queue that will be used to handle corebluetooth callbacks. Next I initialized the Central Manager using the new queue. Because CoreBluetooth needs a runloop to function and that's how callbacks are sent, I explicitly called a runloop on the current thread (which is not the main thread anymore).

How did you manage to get the CoreBluetooth security confirmation appear for your command-line app? I'm trying to do the same and said alert only appears when I run the app from lldb.
How do I run CoreBluetooth in background thread?
 
 
Q