Skip to content

Commit

Permalink
Use a single CHIPDeviceController for all the views in the iOS app. (#…
Browse files Browse the repository at this point in the history
…1004)

We don't want to be reinitializing the CHIP stack as the user
navigates through the app.

This also fixes issues where we'd have multiple CHIPDeviceController
instances live at the same time, which would bind to the same
low-level socket and confuse the networking layer into delivering
response notifications to the wrong CHIPDeviceController.

The dealloc block for CHIPDeviceController was removed because now we
always keep it alive in a static, so it would never run anyway.
  • Loading branch information
bzbarsky-apple authored Jun 4, 2020
1 parent 8b7a20b commit e0c9ff6
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 46 deletions.
26 changes: 14 additions & 12 deletions src/darwin/CHIPTool/CHIPTool/CHIPViewControllerBase.m
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,20 @@ - (void)viewDidLoad
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];

self.chipCallbackQueue = dispatch_queue_create("com.zigbee.chip.example.callback", DISPATCH_QUEUE_SERIAL);
// initialize the device controller
self.chipController = [[CHIPDeviceController alloc] initWithCallbackQueue:self.chipCallbackQueue];
__weak typeof(self) weakSelf = self;
dispatch_queue_t callbackQueue = dispatch_queue_create("com.zigbee.chip.example.callback", DISPATCH_QUEUE_SERIAL);
self.chipController = [CHIPDeviceController sharedController];
[self.chipController registerCallbacks:callbackQueue
onMessage:^(NSData * _Nonnull message, NSString * _Nonnull ipAddress, UInt16 port) {
typeof(self) strongSelf = weakSelf;
NSString * strMessage = [[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding];
[strongSelf postResult:[@"Echo Response: " stringByAppendingFormat:@"%@", strMessage]];
}
onError:^(NSError * _Nonnull error) {
typeof(self) strongSelf = weakSelf;
[strongSelf postResult:[@"Error: " stringByAppendingString:error.localizedDescription]];
}];

// need to restart connections on background/foreground transitions otherwise the socket can be closed without CHIP knowing
// about it.
Expand Down Expand Up @@ -107,16 +118,7 @@ - (void)_connect
NSString * inputIPAddress = [[self _getScannedIP] length] > 0 ? [self _getScannedIP] : self.serverIPTextField.text;
UInt16 inputPort = [self _getScannedPort] > 0 ? [self _getScannedPort] : [self.serverPortTextField.text intValue];

BOOL didConnect = [self.chipController connect:inputIPAddress
port:inputPort
error:&error
onMessage:^(NSData * _Nonnull message, NSString * _Nonnull ipAddress, UInt16 port) {
NSString * strMessage = [[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding];
[self postResult:[@"Echo Response: " stringByAppendingFormat:@"%@", strMessage]];
}
onError:^(NSError * _Nonnull error) {
[self postResult:[@"Error: " stringByAppendingString:error.localizedDescription]];
}];
BOOL didConnect = [self.chipController connect:inputIPAddress port:inputPort error:&error];
if (!didConnect) {
[self postResult:[@"Error: " stringByAppendingString:error.localizedDescription]];
}
Expand Down
27 changes: 21 additions & 6 deletions src/darwin/Framework/CHIP/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,7 @@ typedef void (^ControllerOnErrorBlock)(NSError * error);

@interface CHIPDeviceController : NSObject

- (nullable instancetype)initWithCallbackQueue:(dispatch_queue_t)appCallbackQueue;
- (BOOL)connect:(NSString *)ipAddress
port:(UInt16)port
error:(NSError * __autoreleasing *)error
onMessage:(ControllerOnMessageBlock)onMessage
onError:(ControllerOnErrorBlock)onError;
- (BOOL)connect:(NSString *)ipAddress port:(UInt16)port error:(NSError * __autoreleasing *)error;
- (nullable AddressInfo *)getAddressInfo;
- (BOOL)sendMessage:(NSData *)message error:(NSError * __autoreleasing *)error;
// We can't include definitions of ChipZclClusterId_t and ChipZclCommandId_t
Expand All @@ -53,6 +48,26 @@ typedef void (^ControllerOnErrorBlock)(NSError * error);
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

/**
* Return the single CHIPDeviceController we support existing.
*/
+ (CHIPDeviceController *)sharedController;

/**
* Register callbacks for network activity.
*
* @param[in] appCallbackQueue the queue that should be used to deliver the
* message/error callbacks for this consumer.
*
* @param[in] onMessage the block to call when the controller gets a message
* from the network.
*
* @param[in] onError the block to call when there is a network error.
*/
- (void)registerCallbacks:(dispatch_queue_t)appCallbackQueue
onMessage:(ControllerOnMessageBlock)onMessage
onError:(ControllerOnErrorBlock)onError;

@end

NS_ASSUME_NONNULL_END
Expand Down
43 changes: 20 additions & 23 deletions src/darwin/Framework/CHIP/CHIPDeviceController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,29 @@ @interface CHIPDeviceController ()
// primarily used to not block the work queue
@property (atomic, readonly) dispatch_queue_t chipSelectQueue;
// queue used to signal callbacks to the application
@property (readonly) dispatch_queue_t appCallbackQueue;
@property (readonly) ControllerOnMessageBlock onMessageHandler;
@property (readonly) ControllerOnErrorBlock onErrorHandler;
@property (readwrite) dispatch_queue_t appCallbackQueue;
@property (readwrite) ControllerOnMessageBlock onMessageHandler;
@property (readwrite) ControllerOnErrorBlock onErrorHandler;
@property (readonly) chip::DeviceController::ChipDeviceController * cppController;

@end

@implementation CHIPDeviceController

- (instancetype)initWithCallbackQueue:(dispatch_queue_t)appCallbackQueue
+ (CHIPDeviceController *)sharedController
{
static CHIPDeviceController * controller = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// initialize the device controller
controller = [[CHIPDeviceController alloc] init];
});
return controller;
}

- (instancetype)init
{
if (self = [super init]) {
_appCallbackQueue = appCallbackQueue;
_chipWorkQueue = dispatch_queue_create(CHIP_WORK_QUEUE, DISPATCH_QUEUE_SERIAL);
if (!_chipWorkQueue) {
return nil;
Expand Down Expand Up @@ -151,11 +161,7 @@ - (void)_dispatchAsyncMessageBlock:(NSData *)data ipAddress:(NSString *)ipAddres
});
}

- (BOOL)connect:(NSString *)ipAddress
port:(UInt16)port
error:(NSError * __autoreleasing *)error
onMessage:(ControllerOnMessageBlock)onMessage
onError:(ControllerOnErrorBlock)onError
- (BOOL)connect:(NSString *)ipAddress port:(UInt16)port error:(NSError * __autoreleasing *)error
{
__block CHIP_ERROR err = CHIP_NO_ERROR;

Expand All @@ -178,14 +184,6 @@ - (BOOL)connect:(NSString *)ipAddress
return NO;
}

// Set the callback handlers
if (onMessage) {
_onMessageHandler = onMessage;
}
if (onError) {
_onErrorHandler = onError;
}

// Start the IO pump
[self _serviceEvents];

Expand Down Expand Up @@ -353,12 +351,11 @@ - (void)_serviceEvents
});
}

- (void)dealloc
- (void)registerCallbacks:appCallbackQueue onMessage:(ControllerOnMessageBlock)onMessage onError:(ControllerOnErrorBlock)onError
{
// no more references to this object so immediately stop the inner controller
_cppController->Shutdown();
delete _cppController;
_cppController = NULL;
self.appCallbackQueue = appCallbackQueue;
self.onMessageHandler = onMessage;
self.onErrorHandler = onError;
}

@end
Expand Down
9 changes: 4 additions & 5 deletions src/darwin/Framework/CHIP/CHIPEchoClient.mm
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,8 @@ - (instancetype)initWithServerAddress:(NSString *)ipAddress port:(UInt16)port
if (self = [super init]) {
NSError * error;
_echoCallbackQueue = dispatch_queue_create("com.zigbee.chip.echo", NULL);
_chipController = [[CHIPDeviceController alloc] initWithCallbackQueue:_echoCallbackQueue];

BOOL didInit = [_chipController connect:ipAddress
port:port
error:&error
_chipController = [CHIPDeviceController sharedController];
[self.chipController registerCallbacks:_echoCallbackQueue
onMessage:^(NSData * _Nonnull message, NSString * _Nonnull ipAddress, UInt16 port) {
NSData * sentMessage = [kEchoString dataUsingEncoding:NSUTF8StringEncoding];
if ([sentMessage isEqualToData:message]) {
Expand All @@ -57,6 +54,8 @@ - (instancetype)initWithServerAddress:(NSString *)ipAddress port:(UInt16)port
[self stop];
}];

BOOL didInit = [_chipController connect:ipAddress port:port error:&error];

if (!didInit) {
os_log(OS_LOG_DEFAULT, "Failed to init with error %@", error);
return nil;
Expand Down

0 comments on commit e0c9ff6

Please sign in to comment.