Skip to content

Commit c02aac6

Browse files
committed
[darwin-framework-tool] Add XPC connectivity support for HomeKit on iOS
1 parent 332c444 commit c02aac6

File tree

4 files changed

+192
-12
lines changed

4 files changed

+192
-12
lines changed

examples/darwin-framework-tool/commands/common/xpc/DeviceControllerServer.mm

+16-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020

2121
#import <lib/support/logging/CHIPLogging.h>
2222

23+
#if TARGET_OS_MACCATALYST || TARGET_OS_IOS
24+
#import "HomeKitConnector.h"
25+
#endif
26+
2327
@interface DeviceControllerXPCServerImpl : NSObject <MTRDeviceControllerServerProtocol>
2428
@property (nonatomic, strong) id<MTRDeviceControllerClientProtocol> clientProxy;
2529
@property (nonatomic, strong) NSArray<MTRDeviceController *> * controllers;
@@ -331,19 +335,25 @@ - (void)start
331335

332336
- (void)stop
333337
{
338+
#if TARGET_OS_MACCATALYST || TARGET_OS_IOS
339+
[[HomeKitConnector sharedInstance] stop];
340+
#endif
334341
}
335342

336343
- (MTRDeviceController *)createController:(NSString *)controllerID serviceName:(NSString *)serviceName error:(NSError * __autoreleasing *)error
337344
{
338-
__auto_type connectBlock = ^NSXPCConnection *
339-
{
345+
NSXPCConnection * (^connectBlock)(void) = nil;
340346
#if TARGET_OS_OSX
347+
connectBlock = ^NSXPCConnection *
348+
{
341349
return [[NSXPCConnection alloc] initWithMachServiceName:serviceName options:0];
342-
#else
343-
ChipLogError(chipTool, "NSXPCConnection::initWithMachServiceName is not supported on this platform.");
344-
return nil;
345-
#endif // TARGET_OS_OSX
346350
};
351+
#elif TARGET_OS_MACCATALYST || TARGET_OS_IOS
352+
connectBlock = [[HomeKitConnector sharedInstance] connectBlockFor:controllerID];
353+
controllerID = [[HomeKitConnector sharedInstance] homeControllerIDFor:controllerID];
354+
#endif
355+
356+
VerifyOrReturnValue(nil != connectBlock, nil, ChipLogError(chipTool, "DeviceControllerXPCServerWithServiceName is not supported on this platform."));
347357
return [MTRDeviceController sharedControllerWithID:controllerID xpcConnectBlock:connectBlock];
348358
}
349359
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
#import <Foundation/Foundation.h>
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
23+
@interface HomeKitConnector : NSObject
24+
- (instancetype)init NS_UNAVAILABLE;
25+
+ (instancetype)new NS_UNAVAILABLE;
26+
+ (instancetype)sharedInstance;
27+
28+
- (void)start;
29+
- (void)stop;
30+
- (NSString *)homeControllerIDFor:(NSString *)controllerID;
31+
- (NSXPCConnection * (^)(void) )connectBlockFor:(NSString *)controllerID;
32+
@end
33+
34+
NS_ASSUME_NONNULL_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright (c) 2025 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
#import "HomeKitConnector.h"
20+
#import "../CHIPCommandBridge.h"
21+
#import <lib/support/logging/CHIPLogging.h>
22+
23+
#import <HomeKit/HomeKit.h>
24+
25+
const int64_t kHomeManagerSetupTimeout = 10LL * NSEC_PER_SEC;
26+
NSString * kControllerIdPrefixStr = @(kControllerIdPrefix);
27+
28+
@interface HomeKitConnector () <HMHomeManagerDelegate>
29+
@property (nonatomic, assign) BOOL connectorStarted;
30+
@property (nonatomic, strong) HMHomeManager * homeManager;
31+
@property (nonatomic, assign) BOOL homeManagerReady;
32+
@end
33+
34+
@implementation HomeKitConnector {
35+
dispatch_group_t _homeManagerReadyGroup;
36+
}
37+
38+
+ (instancetype)sharedInstance
39+
{
40+
static HomeKitConnector * sharedInstance = nil;
41+
static dispatch_once_t onceToken;
42+
dispatch_once(&onceToken, ^{
43+
sharedInstance = [[HomeKitConnector alloc] init];
44+
});
45+
return sharedInstance;
46+
}
47+
48+
- (void)start
49+
{
50+
VerifyOrReturn(!_connectorStarted);
51+
_connectorStarted = YES;
52+
53+
_homeManagerReady = NO;
54+
_homeManagerReadyGroup = dispatch_group_create();
55+
dispatch_group_enter(_homeManagerReadyGroup);
56+
57+
_homeManager = [[HMHomeManager alloc] init];
58+
_homeManager.delegate = self;
59+
60+
// Wait until homeManagerDidUpdateHomes is called or timeout
61+
dispatch_group_wait(_homeManagerReadyGroup, dispatch_time(DISPATCH_TIME_NOW, kHomeManagerSetupTimeout));
62+
}
63+
64+
- (void)stop
65+
{
66+
VerifyOrReturn(_connectorStarted);
67+
68+
_homeManager.delegate = nil;
69+
_homeManager = nil;
70+
}
71+
72+
- (void)homeManagerDidUpdateHomes:(HMHomeManager *)manager
73+
{
74+
VerifyOrReturn(!_homeManagerReady);
75+
dispatch_group_leave(_homeManagerReadyGroup);
76+
}
77+
78+
- (HMHome *)homeFor:(NSString *)controllerID
79+
{
80+
[[HomeKitConnector sharedInstance] start];
81+
82+
__auto_type * homes = _homeManager.homes;
83+
VerifyOrReturnValue(0 != homes.count, nil, ChipLogError(chipTool, "HomeKit is not configured with any homes."));
84+
85+
NSNumber * fabricId = nil;
86+
if ([controllerID hasPrefix:kControllerIdPrefixStr]) {
87+
__auto_type * fabricIdString = [controllerID substringFromIndex:kControllerIdPrefixStr.length];
88+
fabricId = @([fabricIdString integerValue]);
89+
} else {
90+
fabricId = CHIPCommandBridge::GetCommissionerFabricId([controllerID UTF8String]);
91+
}
92+
93+
// When multiple homes exist, the first controller corresponds to the first home, the second controller to the second home, etc.
94+
// If there are fewer homes than controllers, controllers beyond the last home will be associated with the final home in the list.
95+
NSUInteger index = [fabricId unsignedShortValue] - 1;
96+
if (index >= homes.count) {
97+
index = homes.count - 1;
98+
}
99+
100+
return homes[index];
101+
}
102+
103+
- (NSString *)homeControllerIDFor:(NSString *)controllerID
104+
{
105+
__auto_type * home = [self homeFor:controllerID];
106+
return home.matterControllerID;
107+
}
108+
109+
- (NSXPCConnection * (^)(void) )connectBlockFor:(NSString *)controllerID;
110+
{
111+
__auto_type * home = [self homeFor:controllerID];
112+
ChipLogProgress(chipTool, "Controller '%s' will be associated with home '%s'.", [controllerID UTF8String], [home.matterControllerID UTF8String]);
113+
114+
if ([controllerID hasPrefix:kControllerIdPrefixStr]) {
115+
if ([home respondsToSelector:NSSelectorFromString(@"matterStartupParametersXPCConnectBlock")]) {
116+
return [home valueForKey:@"matterStartupParametersXPCConnectBlock"];
117+
}
118+
119+
ChipLogError(chipTool, "Error: 'matterStartupParametersXPCConnectBlock' not available for controller '%s'.", [controllerID UTF8String]);
120+
return nil;
121+
} else {
122+
return home.matterControllerXPCConnectBlock;
123+
}
124+
}
125+
@end

examples/darwin-framework-tool/commands/common/xpc/XPCServer.mm

+17-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020

2121
#import <lib/support/logging/CHIPLogging.h>
2222

23+
#if TARGET_OS_MACCATALYST || TARGET_OS_IOS
24+
#import "HomeKitConnector.h"
25+
#endif
26+
2327
@interface XPCServerImpl : NSObject <MTRXPCServerProtocol_MTRDevice>
2428
@property (nonatomic, strong) id<MTRXPCClientProtocol> clientProxy;
2529
@property (nonatomic, strong) NSArray<MTRDeviceController *> * controllers;
@@ -239,19 +243,26 @@ - (void)start
239243

240244
- (void)stop
241245
{
246+
#if TARGET_OS_MACCATALYST || TARGET_OS_IOS
247+
[[HomeKitConnector sharedInstance] stop];
248+
#endif
242249
}
243250

244251
- (MTRDeviceController *)createController:(NSString *)controllerID serviceName:(NSString *)serviceName error:(NSError * __autoreleasing *)error
245252
{
246-
__auto_type connectBlock = ^NSXPCConnection *
247-
{
253+
NSXPCConnection * (^connectBlock)(void) = nil;
248254
#if TARGET_OS_OSX
255+
connectBlock = ^NSXPCConnection *
256+
{
249257
return [[NSXPCConnection alloc] initWithMachServiceName:serviceName options:0];
250-
#else
251-
ChipLogError(chipTool, "NSXPCConnection::initWithMachServiceName is not supported on this platform.");
252-
return nil;
253-
#endif // TARGET_OS_OSX
254258
};
259+
#elif TARGET_OS_MACCATALYST || TARGET_OS_IOS
260+
connectBlock = [[HomeKitConnector sharedInstance] connectBlockFor:controllerID];
261+
controllerID = [[HomeKitConnector sharedInstance] homeControllerIDFor:controllerID];
262+
#endif
263+
264+
VerifyOrReturnValue(nil != connectBlock, nil, ChipLogError(chipTool, "XPCServerWithServiceName is not supported on this platform."));
265+
255266
__auto_type * uniqueIdentifier = [[NSUUID alloc] initWithUUIDString:controllerID];
256267
__auto_type * xpcParams = [[MTRXPCDeviceControllerParameters alloc] initWithXPConnectionBlock:connectBlock uniqueIdentifier:uniqueIdentifier];
257268
return [[MTRDeviceController alloc] initWithParameters:xpcParams error:error];

0 commit comments

Comments
 (0)