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];
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).