I've created a React Native project and I need to discover Bluetooth devices in the background. I'm trying to implement the entire background functionality on the native side using Objective-C.
As an initial step, I'm trying to use CBCentralManager to discover the device in the foreground first and then will move to the background case.
I'm able to call the init function of my custom class which handles BLE functionality but after that CBCentralManagerDelegate methods are not being called and I'm not able to proceed further.
// BLEScan.m
#import <Foundation/Foundation.h>
#import "BLEScan.h"
#interface BLEScan() <CBCentralManagerDelegate>
#property (strong, nonatomic) CBCentralManager *manager;
#property CBPeripheral *dot;
#end
#implementation BLEScan
- (instancetype) initModule {
NSLog(#"BLEScan: init");
if (self = [super init]) {
_manager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue() options:nil];
NSLog(#"BLEScan: state %li", (long)_manager.state);
}
return self;
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
// You should test all scenarios
NSLog(#"BLEScan: state %li", (long)central.state);
if (central.state != CBManagerStatePoweredOn) {
return;
}
if (central.state == CBManagerStatePoweredOn) {
// Scan for devices
[_manager scanForPeripheralsWithServices:nil options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES }];
NSLog(#"Scanning started");
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BLEScan *scanner = [[BLEScan alloc] initModule];
return YES
}
Link to the repo - https://github.com/inkloud-infiswift/CoreBluetooth
Related
i've the following situation.
2 identical react-native apps (differs only for bundleId, app icon etc) structured like this:
-> project structure
My goal it's to emit an event from native side to the JS layer through the bridge when a push notification has been received or tapped by the user (assuming that the app is in foreground and app has finished launching).
On the first App the following code works as expected when i trigger a push notification to my simulator with the command xcrun simctl push <device-id> <bundleId> <filename>.apns, the second app crash immediatly with the following error:
Thread 1: "Error when sending event: pushDelivered with body: <the string passed as body>. RCTCallableJSModules is not set. This is probably because you've explicitly synthesized the RCTCallableJSModules in CustomEventsEmitter, even though it's inherited from RCTEventEmitter."
-> xcode view
Here is the code implementation of RCTEventEmitter's sendEventWithName that provoke the assertion.
I don't know if it's a problem with my implementation. In 1 of the 2 apps works like a charm, in the other 💥.
Anyone can help me find the problem in the code ? Probably a problem with the bridge?
i've tried many times to reinstall pods, clean project and rebuild. The code works on the project A and not on the project B.. i cannot figure out the reason
AppDelegate.h
#import <React/RCTBridgeDelegate.h>
#import <React/RCTBridgeModule.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
#import <UserNotifications/UNUserNotificationCenter.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, RCTBridgeModule, UNUserNotificationCenterDelegate>
#property (nonatomic, strong) UIWindow *window;
#property (nonatomic, strong) NSDictionary *receivedNotificationUserInfo;
#end
AppDelegate.mm
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTAppSetupUtils.h>
#import <UserNotifications/UserNotifications.h>
#import "CustomEventsEmitter.h"
#implementation AppDelegate
bool hasFinishedLaunching = false;
CustomEventsEmitter *customEventsEmitter = NULL;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
hasFinishedLaunching = true;
customEventsEmitter = [CustomEventsEmitter allocWithZone: nil];
RCTAppSetupPrepareApp(application);
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = RCTAppSetupDefaultRootView(bridge, #"MyAppName", initProps);
if (#available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
}
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
// Define UNUserNotificationCenter
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
return YES;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:#"index"];
#else
return [[NSBundle mainBundle] URLForResource:#"main" withExtension:#"jsbundle"];
#endif
}
-(void)applicationDidBecomeActive:(UIApplication *)application
{
application.applicationIconBadgeNumber = 0;
}
// The method will be called on the delegate when the user responded to the notification by opening
// the application, dismissing the notification or choosing a UNNotificationAction. The delegate
// must be set before the application returns from applicationDidFinishLaunching:.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler {
NSLog(#"didReceiveNotificationResponse response: %#", response);
NSDictionary *userInfo = response.notification.request.content.userInfo;
if (userInfo[#"_od"]){
// if no listeners has been registered yet, store the value
// this is the case when the notification was clicked from closed app
if(![customEventsEmitter hasListeners]) {
// handle this case ...
}
// if listeners has been registered, emit an event
// this is the case when the notification was clicked from foreground app
else {
[self emitPushTappedEvent:userInfo[#"_od"]];
}
}
if (completionHandler != nil) {
completionHandler();
}
}
//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
NSDictionary *userInfo = notification.request.content.userInfo;
NSLog(#"User Info : %#", userInfo);
[self emitPushDeliveredEvent:userInfo[#"_od"]];
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}
-(void)emitPushDeliveredEvent:(NSString*)value {
NSLog(#"emitPushDeliveredEvent called");
[customEventsEmitter sendEventWithName:#"pushDelivered" body:value];
}
-(void)emitPushTappedEvent:(NSString*)value {
NSLog(#"emitPushTappedEvent called");
[customEventsEmitter sendEventWithName:#"pushTapped" body:value];
}
#end
And this are the CustomEventsEmitter files:
CustomEventsEmitter.h
#ifndef CustomEventsEmitter_h
#define CustomEventsEmitter_h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#interface CustomEventsEmitter : RCTEventEmitter <RCTBridgeModule>
- (void)sendEventName:(NSString *)eventName body:(id)body;
- (bool)hasListeners;
#end
#endif
CustomEventsEmitter.m
#import "CustomEventsEmitter.h"
#implementation CustomEventsEmitter
{
bool hasListeners;
}
RCT_EXPORT_MODULE(CustomEventsEmitter);
+ (id)allocWithZone:(NSZone *)zone {
static CustomEventsEmitter *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
});
return sharedInstance;
}
- (NSArray<NSString *> *)supportedEvents {
return #[#"pushDelivered", #"pushTapped"];
}
// Will be called when this module's first listener is added.
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}
// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}
-(bool)hasListeners {
return hasListeners;
}
- (void)sendEventName:(NSString *)eventName body:(id)body {
if (hasListeners) {
NSLog(#"CustomEventsEmitter sendEventName emitting event: %#", eventName);
[self sendEventWithName:eventName body:body];
} else {
NSLog(#"CustomEventsEmitter sendEventName called without listeners: %#", eventName);
}
}
#end
HELP ME UNDERSTAND PLEASEEEE
Oh i've solved it!
It was a mistake of mine.
The AppModule didn't invoke the CustomEventsEmitter's methods correctly..
changing the code like below makes the events be emitted correctly through the RN bridge
-(void)emitPushDeliveredEvent:(NSString*)value {
NSLog(#"emitPushDeliveredEvent called");
[customEventsEmitter sendEventName:#"pushDelivered" body:value];
//[customEventsEmitter sendEventWithName:#"pushDelivered" body:value];
}
-(void)emitPushTappedEvent:(NSString*)value {
NSLog(#"emitPushTappedEvent called");
[customEventsEmitter sendEventName:#"pushTapped" body:value];
//[customEventsEmitter sendEventWithName:#"pushTapped" body:value];
}
I am working with IOT weather connect display(Use Local Network) that connect with the router using iOS app. connection between display and iOS app is done by local network.popup came up at the time of connection for allowing the local network privacy but i want to check in advance that user has allowed or not local network permission. I refer this iOS 14 How to trigger Local Network dialog and check user answer? and https://developer.apple.com/forums/thread/663858
but i am looking for code in Objecitve-C. please help me with that
This is my original answer that is written in Swift - iOS 14 How to trigger Local Network dialog and check user answer? and you can use it from objc as well without any effort.
But if you have a pure ObjC project and don't want to add swift files this is the similar approach that works the same:
// LocalNetworkPrivacy.h
#interface LocalNetworkPrivacy : NSObject
- (void)checkAccessState:(void (^)(BOOL))completion;
#end
// LocalNetworkPrivacy.m
#import <UIKit/UIKit.h>
#import "LocalNetworkPrivacy.h"
#interface LocalNetworkPrivacy () <NSNetServiceDelegate>
#property (nonatomic) NSNetService *service;
#property (nonatomic) void (^completion)(BOOL);
#property (nonatomic) NSTimer *timer;
#property (nonatomic) BOOL publishing;
#end
#implementation LocalNetworkPrivacy
- (instancetype)init {
if (self = [super init]) {
self.service = [[NSNetService alloc] initWithDomain:#"local." type:#"_lnp._tcp." name:#"LocalNetworkPrivacy" port:1100];
}
return self;
}
- (void)dealloc {
[self.service stop];
}
- (void)checkAccessState:(void (^)(BOOL))completion {
self.completion = completion;
self.timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {
if (UIApplication.sharedApplication.applicationState != UIApplicationStateActive) {
return;
}
if (self.publishing) {
[self.timer invalidate];
self.completion(NO);
}
else {
self.publishing = YES;
self.service.delegate = self;
[self.service publish];
}
}];
}
#pragma mark - NSNetServiceDelegate
- (void)netServiceDidPublish:(NSNetService *)sender {
[self.timer invalidate];
self.completion(YES);
}
#end
How to use:
LocalNetworkPrivacy* localNetworkPrivacy = [LocalNetworkPrivacy new];
[localNetworkPrivacy checkAccessState:^(BOOL granted) {
NSLog(#"Granted: %#", granted ? #"YES" : #"NO");
}];
NOTE: You must set NSLocalNetworkUsageDescription and add "_lnp._tcp." to NSBonjourServices into your Info.plist.
I have an objective c application that implements coreBluetooth framework to connect with bluetooth keychains. All is correct, I connect with bluetooth keychain, if I go to background my device keep connected. If my device is powered off or lost then is reconnect.. And my application detect when I do click in the button bluetooth keychain with service and with the didUpdateValueForCharacteristic. So all this is correct.
But I'm trying use restoration properties with willRestoreState function, and this not work... I send my application to background, but when I reboot my iPhone, this isn't reconnecting and not works didUpdateValueForCharacteristic.
I have added the centralManager and all functions bluetooth delegate to AppDelegate with the functions to restorate application and I use a custom queue to connect centralManager bluetooth. But I dont't know what is the problem and why not reconnect then reboot.
#interface AppDelegate ()
#property (strong,nonatomic) CBCentralManager *central;
#property (copy,nonatomic) NSString *targetPeripheral;
#property (strong,nonatomic) NSMutableArray *discoveredPeripherals;
#property (strong,nonatomic) CBPeripheral *connectedPeripheral;
#property (strong,nonatomic) CBUUID *deviceInfoUUID;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
ViewController *myloginController = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"viewController"];
UINavigationController *navController = [[UINavigationController alloc]initWithRootViewController:myloginController];
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
self.discoveredPeripherals=[NSMutableArray new];
self.deviceInfoUUID=[CBUUID UUIDWithString:#"FFE0"];
//I have added this code to connect then reboot iPhone:
NSArray *centralManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];
if ((centralManagerIdentifiers) && (centralManagerIdentifiers.count > 0)) {
// The system knows about one or more centrals that need to be restored.
for (NSString *identifier in centralManagerIdentifiers) {
dispatch_queue_t queue = dispatch_queue_create("com.myco.cm", DISPATCH_QUEUE_SERIAL);
self.central = [[CBCentralManager alloc] initWithDelegate:self queue:queue options:#{CBPeripheralManagerOptionRestoreIdentifierKey : identifier,CBCentralManagerOptionShowPowerAlertKey:#YES}];
[self.referencesToCentrals addObject:self.central];
}
}
else {
dispatch_queue_t queue = dispatch_queue_create("com.myco.cm", DISPATCH_QUEUE_SERIAL);
self.central = [[CBCentralManager alloc] initWithDelegate:self queue:queue options:#{CBPeripheralManagerOptionRestoreIdentifierKey : #"1C7757658-D187-99D2-9578-7B0976AAAEB4",CBCentralManagerOptionShowPowerAlertKey:#YES }];
[self.referencesToCentrals addObject:self.central];
}
//An this is my willrestoreState function
-(void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary *)dict {
dispatch_async(dispatch_get_main_queue(), ^() {
NSArray *peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey];
if ([peripherals count]>0) { // There are peripherals
for (CBPeripheral* p in peripherals) {
if ([p.identifier isEqual:[CBUUID UUIDWithString:self.UUID]]) { // wich are sensorTags
[self.central connectPeripheral:[peripherals firstObject] options:nil]; // so let's reconnect
}
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
NSString *content = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
NSLog(#"Receive from Peripheral: %#",content);
}
-(void) startScan {
[self.central scanForPeripheralsWithServices:#[[CBUUID UUIDWithString:#"FFE0"]] options:nil];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"We are in background");
}
-(void) centralManagerDidUpdateState:(CBCentralManager *)central {
switch (central.state) {
case CBCentralManagerStatePoweredOn:
[self startScan];
break;
case CBManagerStateUnknown:
break;
case CBManagerStatePoweredOff:
break;
case CBManagerStateResetting:
break;
case CBManagerStateUnsupported:
break;
case CBManagerStateUnauthorized:
break;
}
}
-(void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
if([self.central retrieveConnectedPeripheralsWithServices:#[[CBUUID UUIDWithString:#"FFE0"]]] != 0){
dispatch_async(dispatch_get_main_queue(), ^{
self.nameBT = peripheral.name;
self.UUID = peripheral.identifier.UUIDString;
});
}
if (![self.discoveredPeripherals containsObject:peripheral]){
dispatch_async(dispatch_get_main_queue(), ^{
[self.discoveredPeripherals addObject:peripheral];
});
}
if (self.connectedPeripheral) {
[self.central cancelPeripheralConnection:self.connectedPeripheral];
}
self.targetPeripheral=peripheral.identifier.UUIDString;
[self.central connectPeripheral:peripheral options:nil];
}
-(void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
self.connectedPeripheral=peripheral;
dispatch_async(dispatch_get_main_queue(), ^{
self.nameBT = peripheral.name;
self.UUID = peripheral.identifier.UUIDString;
});
peripheral.delegate=self;
[peripheral discoverServices:#[self.deviceInfoUUID]];
}
And I have added this to try if application reconnect bluetooth:
-(BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder {
return YES;
}
-(void) centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(#"Disconnected from peripheral");
if ([self.targetPeripheral isEqualToString:peripheral.identifier.UUIDString]) {
NSLog(#"Retrying");
[self.central connectPeripheral:peripheral options:nil];
}
}
}
How can I reconnect when iPhone reboot automatically without need open application?
What am I doing wrong?
Edit: Now tested on my actual iphone, and I still get the same crash error
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSCFConstantString stringByAppendingString:]: nil argument'
When I ran the debugger, I ran through the debugger with breakpoints, but it did not reach any other function besides the tLog and the didLoad. The message "Bluetooth LE Device Scanner\r\n\r" went through the tLog a few times without before the app crashed because of the invalid argument
Original -
Info: Mac OSX Yosemite; Xcode 6.4; Using Objective C
Right now, I am following the video tutorial on setting up the bluetooth scanner to detect devices powered by bluetooth.
Video Links:
https://www.youtube.com/watch?v=zjr8qLE0sxk
https://www.youtube.com/watch?v=AjBpB4TcI-U (This part is where crash occurs)
I followed the code and understood the basics of core bluetooth framework. However, when running the test under both the iOS simulator and my iPhone, the application crashes at start-up.
The error I get is:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSCFConstantString stringByAppendingString:]: nil argument' (iOS simulator)
I used the breakpoints that involve tlog and start-up, and that same error is still there because it is stuck printing the same thing.
Is there any fix I should do to get the basic bluetooth scanner running?
EDIT -
The code from the tutorial that I followed:
#import "ViewController.h"
#interface ViewController ()
// Now we need the properties to do various tasks for the device scanner
#property (weak, nonatomic) IBOutlet UITextView *outputTextView;
#property (weak, nonatomic) IBOutlet UISegmentedControl *verbositySelector;
#property (strong, nonatomic) CBCentralManager *centralManager;
#property (strong, nonatomic) CBPeripheral *discoveredPeripheral;
#property BOOL bluetoothOn;
#property (weak, nonatomic) IBOutlet UILabel *valueLabel;
#end
#implementation ViewController
-(bool)verboseMode
{
return (self.verbositySelector.selectedSegmentIndex != 0);
}
-(void)tlog:(NSString*)msg
{
self.outputTextView.text = [#"\r\n\r\n" stringByAppendingString:self.outputTextView.text];
self.outputTextView.text = [msg stringByAppendingString:self.outputTextView.text];
}
-(IBAction)startScan:(id)sender
{
if (!self.bluetoothOn){
[self tlog:#"Bluetooth is OFF"];
return;
}
[self.centralManager scanForPeripheralsWithServices:nil
options:
#{CBCentralManagerScanOptionAllowDuplicatesKey: #NO}];
}
// Standard template items added in by default. DO NOT REMOVE!
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self tlog:#"Bluetooth LE Device Scanner\r\n\r"];
self.bluetoothOn = NO;
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// Core Bluetooth Tasks
-(void) centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
{
[self tlog:[NSString stringWithFormat:#"Discovered %#, RSSI: %#\n",
[advertisementData objectForKey:#"kCBAdvDataLocalName"], RSSI]];
self.discoveredPeripheral = peripheral;
if ([self verboseMode]){
[self.centralManager connectPeripheral:peripheral options:nil];
}
}
-(void) centralManager:(CBCentralManager *)central
didFailToConnectPeripheral:(CBPeripheral *)peripheral
error:(NSError *)error
{
}
-(void) centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral
{
}
-(void) peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error
{
}
-(void) peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
}
-(void) peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error
{
}
-(void) centralManagerDidUpdateState:(CBCentralManager *)central
{
if (central.state != CBCentralManagerStatePoweredOn){
[self tlog:#"Bluetooth OFF"];
self.bluetoothOn = NO;
}
else {
[self tlog:#"Bluetooth ON"];
self.bluetoothOn = YES;
}
}
#end
I'm working with CoreBluetooth, so in my unit tests I'm mocking all the CB objects so they return what I want. In one of my tests, I mock a CBPeripheral, and stub the delegate method like so:
[[[mockPeripheral stub] andReturn:device] delegate];
The device passed in is my wrapper object which holds on to the peripheral. Later in the test, I call a method on device which then checks:
NSAssert(_peripheral.delegate == self, #"Empty device");
This line is being asserted during the test because _peripheral.delegate != self.
I've debugged through, and made sure that _peripheral is an OCMockObject. Why isn't the stubbed method returning device when the assert checks the _peripheral's delegate?
Here's the detailed code:
#interface Manager : NSObject
- (void)connectToDevice:(Device*)device;
#end
#implementation Foo
- (void)connectToDevice:(Device*)device {
if([device checkDevice]) {
/** Do Stuff */
}
}
#end
#interface Device : NSObject {
CBPeripheral _peripheral;
}
- (id)initWithPeripheral:(CBPeripheral*)peripheral;
#end
#implementation Device
- (id)initWithPeripheral:(CBPeripheral*)peripheral {
self = [super init];
if(self) {
_peripheral = peripheral;
_peripheral.delegate = self;
}
return self;
}
- (BOOL)checkDevice {
NSAssert(_peripheral.delegate == self, #"Empty device");
return YES;
}
#end
#implementation Test
__block id peripheralMock;
beforeAll(^{
peripheralMock = [OCMockObject mockForClass:[CBPeripheral class]];
});
//TEST METHOD
it(#"should connect", ^{
Device *device = [[Device alloc] initWithPeripheral:peripheralMock];
[[[peripheralMock stub] andReturn:device] delegate];
[manager connectToDevice:device];
}
#end
I am not able to reproduce this -- is this what you're doing?
#interface Bar : NSObject <CBPeripheralDelegate>
#property (nonatomic, strong) CBPeripheral *peripheral;
- (void)peripheralTest;
#end
- (void)peripheralTest
{
NSAssert(_peripheral.delegate == self, #"Empty device");
}
// In test class:
- (void)testPeripheral
{
Bar *bar = [Bar new];
id peripheralMock = [OCMockObject mockForClass:CBPeripheral.class];
[[[peripheralMock stub] andReturn:bar] delegate];
bar.peripheral = peripheralMock;
[bar peripheralTest];
}
This test passes for me.