2 // MultiDeviceNetworking.m
6 #import "MultiDeviceNetworking.h"
7 #import "MultiDeviceNetworkingProtocol.h"
9 @interface MDNCounters ()
10 @property (assign) unsigned long kvsSyncAndWait;
11 @property (assign) unsigned long kvsFlush;
12 @property (assign) unsigned long kvsSend;
13 @property (assign) unsigned long kvsRecv;
14 @property (assign) unsigned long kvsRecvAll;
15 @property (strong) NSMutableDictionary<NSString *,NSNumber *> *kvsKeys;
17 - (void)addCountToKey:(NSString *)key;
21 @interface MDNConnection : NSObject <MultiDeviceNetworkingProtocol>
22 @property (weak) MultiDeviceNetworking *network;
23 @property NSXPCConnection *inConnection;
24 @property NSXPCConnection *outConnection;
25 @property MDNCounters *counters;
28 @interface MultiDeviceNetworking () <NSXPCListenerDelegate>
29 @property NSXPCListener *networkListener;
30 @property NSMutableDictionary *kvs;
31 @property NSMutableArray<MDNConnection *> *connections;
32 @property dispatch_queue_t serialQueue;
33 @property NSMutableDictionary<NSString *, XCTestExpectation *> *expectations;
36 @implementation MDNCounters
38 - (instancetype)init {
39 if ((self = [super init]) == NULL) {
42 self.kvsKeys = [NSMutableDictionary dictionary];
46 - (NSDictionary *)summary{
47 NSDictionary *kvsKeys = @{};
48 @synchronized(self.kvsKeys) {
49 kvsKeys = [self.kvsKeys copy];
52 @"kvsSyncAndWait" : @(self.kvsSyncAndWait),
53 @"kvsFlush" : @(self.kvsFlush),
54 @"kvsSend" : @(self.kvsSend),
55 @"kvsRecv" : @(self.kvsRecv),
56 @"kvsRecvAll" : @(self.kvsRecvAll),
60 - (NSString *)description
62 return [NSString stringWithFormat:@"<MDNCounters: %@>", [self summary]];
64 - (void)addCountToKey:(NSString *)key
66 @synchronized(self.kvsKeys) {
67 NSNumber *number = self.kvsKeys[key];
68 self.kvsKeys[key] = @([number longValue] + 1);
74 @implementation MultiDeviceNetworking
80 self.networkListener = [NSXPCListener anonymousListener];
81 self.networkListener.delegate = self;
82 [self.networkListener resume];
83 self.kvs = [[NSMutableDictionary alloc] init];
84 self.connections = [NSMutableArray array];
85 self.serialQueue = dispatch_queue_create("MultiDeviceNetworking.flushQueue", NULL);
86 self.expectations = [NSMutableDictionary dictionary];
91 - (NSXPCListenerEndpoint *)endpoint
93 return [self.networkListener endpoint];
98 @synchronized(self.kvs) {
100 [self.kvs enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull __unused stop) {
101 puts([[NSString stringWithFormat:@"%@ - %@", key, obj] UTF8String]);
108 @synchronized(self.connections) {
109 puts("Network counters:");
110 for (MDNConnection *conn in self.connections) {
111 puts([[NSString stringWithFormat:@"%@", conn.counters] UTF8String]);
117 - (void)disconnectAll
119 @synchronized(self.connections) {
120 for (MDNConnection *conn in self.connections) {
121 [conn.inConnection invalidate];
122 [conn.outConnection invalidate];
124 self.connections = [NSMutableArray array];
128 - (void)setTestExpectation:(XCTestExpectation *)expectation forKey:(NSString *)key
130 self.expectations[key] = expectation;
133 - (void)clearTestExpectations
135 self.expectations = [NSMutableDictionary dictionary];
138 - (void)fulfill:(NSString *)key
140 [self.expectations[key] fulfill];
145 //MARK: - setup listener
147 - (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
149 newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MultiDeviceNetworkingProtocol)];
151 MDNConnection *conn = [[MDNConnection alloc] init];
153 conn.inConnection = newConnection;
154 newConnection.exportedObject = conn;
156 [self.connections addObject:conn];
157 [newConnection resume];
167 @implementation MDNConnection
171 if ((self = [super init]) == nil)
173 _counters = [[MDNCounters alloc] init];
177 - (void)MDNRegisterCallback:(NSXPCListenerEndpoint *)callback complete:(MDNComplete)complete
179 self.outConnection = [[NSXPCConnection alloc] initWithListenerEndpoint:callback];
180 self.outConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MultiDeviceNetworkingCallbackProtocol)];
182 __typeof(self) weakSelf = self;
183 self.outConnection.invalidationHandler = ^{
184 __typeof(self) strongSelf = weakSelf;
185 strongSelf.outConnection = nil;
189 [self.outConnection resume];
190 complete(NULL, NULL);
193 - (void)MDNCloudPut:(NSDictionary *)values complete:(MDNComplete)complete {
194 MultiDeviceNetworking *network = self.network;
195 @synchronized(network.kvs) {
196 [network.kvs setValuesForKeysWithDictionary:values];
198 /* interact with test expections so that tests can check that something happned in KVS */
199 [network fulfill:@"Network"];
200 for (NSString *key in values.allKeys) {
201 NSString *dataSummary = @"";
202 id value = values[key];
203 if ([value isKindOfClass:[NSString class]]) {
204 dataSummary = [NSString stringWithFormat:@" = string[%ld]", [(NSString *)value length]];
205 } else if ([value isKindOfClass:[NSData class]]) {
206 NSUInteger length = [(NSData *)value length];
207 NSData *subdata = [(NSData *)value subdataWithRange:NSMakeRange(0, MIN(length, 4))];
208 dataSummary = [NSString stringWithFormat:@" = data[%lu][%@]", (unsigned long)length, subdata];
210 dataSummary = [NSString stringWithFormat:@" = other(%@)", [value description]];
212 NSLog(@"KVS key update: %@%@", key, dataSummary);
213 [network fulfill:key];
214 [self.counters addCountToKey:key];
218 self.counters.kvsSend++;
219 for (MDNConnection *conn in network.connections) {
220 if (conn == self || conn.outConnection == NULL) {
223 conn.counters.kvsRecv++;
224 [[conn.outConnection remoteObjectProxy] MDNCItemsChanged:values complete:^(NSDictionary *returnedValues, NSError *error) {
231 - (void)MDNCloudsynchronizeAndWait:(NSDictionary *)values complete:(MDNComplete)complete {
232 MultiDeviceNetworking *network = self.network;
233 NSDictionary *kvsCopy = NULL;
234 @synchronized(network.kvs) {
235 kvsCopy = [network.kvs copy];
237 self.counters.kvsSyncAndWait++;
238 [[self.outConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
239 NSLog(@"foo: %@", error);
241 }] MDNCItemsChanged:kvsCopy complete:^(NSDictionary *returnedValues, NSError *error) {
243 dispatch_async(network.serialQueue, ^{
248 - (void)MDNCloudGet:(NSArray *)keys complete:(MDNComplete)complete{
249 MultiDeviceNetworking *network = self.network;
250 NSLog(@"asking for: %@", keys);
251 self.counters.kvsRecv++;
252 NSMutableDictionary *reply = [NSMutableDictionary dictionary];
253 @synchronized(network.kvs) {
254 for (id key in keys) {
255 reply[key] = network.kvs[key];
258 complete(reply, NULL);
261 - (void)MDNCloudGetAll:(MDNComplete)complete
263 MultiDeviceNetworking *network = self.network;
264 NSDictionary *kvsCopy = NULL;
265 self.counters.kvsRecvAll++;
266 @synchronized(network.kvs) {
267 kvsCopy = [network.kvs copy];
269 complete(kvsCopy, NULL);
272 - (void)MDNCloudRemoveKeys:(NSArray<NSString *> *)keys complete:(MDNComplete)complete
274 MultiDeviceNetworking *network = self.network;
275 @synchronized(network.kvs) {
277 for (NSString *key in keys) {
278 network.kvs[key] = NULL;
281 network.kvs = [NSMutableDictionary dictionary];
284 complete(NULL, NULL);
287 - (void)MDNCloudFlush:(MDNComplete)complete
289 self.counters.kvsFlush++;
290 dispatch_async(self.network.serialQueue, ^{