Browse files

Initial XPCProxy Object.

  • Loading branch information...
1 parent 5a79d76 commit a18c9d15c2bf439da9414fdc6f536542678fe6ce @KingOfBrian committed Feb 25, 2012
View
6 TestApp/TestAppAppDelegate.m
@@ -80,6 +80,12 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
[readConnection sendMessage:readData];
[mathConnection sendMessage:[NSDictionary dictionaryWithObject:@"whatTimeIsIt" forKey:@"operation"]];
+
+ id fm = [XPCProxy proxyClass:@"NSFileManager" selector:@selector(defaultManager) onConnection:mathConnection];
+
+ [(NSFileManager *)fm copyItemAtPath:@"/tmp/foo"
+ toPath:@"/tmp/bar"
+ error:nil];
}
@end
View
1 TestService/main.m
@@ -27,6 +27,7 @@ int main(int argc, const char *argv[])
[connection _sendLog:@"Multiply received a connection"];
[connection setEventHandler:^(NSDictionary *message, XPCConnection *connection){
[connection _sendLog:[NSString stringWithFormat:@"Multiply received a message! %@", message]];
+ [XPCProxy handleInvocationOfProxyMessage:message fromConnection:connection];
if([[message objectForKey:@"operation"] isEqual:@"multiply"]){
NSArray *values = [message objectForKey:@"values"];
View
29 XPCKit.xcodeproj/project.pbxproj
@@ -84,11 +84,10 @@
1EF4D9E3141C38D0007BEEC0 /* NSFileHandle+XPCParse.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EF4D9E0141C38D0007BEEC0 /* NSFileHandle+XPCParse.m */; };
1EF4D9E4141C38D0007BEEC0 /* NSFileHandle+XPCParse.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EF4D9E0141C38D0007BEEC0 /* NSFileHandle+XPCParse.m */; };
1EF4D9E7141C3EEE007BEEC0 /* TestService.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = 1EF4D9E6141C3EEE007BEEC0 /* TestService.entitlements */; };
- 9CB818BA14BE64A500A2D29C /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EEDD03D13DD485400D5AEC3 /* SenTestingKit.framework */; };
- 9CB818BB14BE64A500A2D29C /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EEDD0EB13DEA0A400D5AEC3 /* Cocoa.framework */; };
- 9CB818C514BE64A500A2D29C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9CB818C314BE64A500A2D29C /* InfoPlist.strings */; };
- 9CB818C814BE64A500A2D29C /* XPCKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CB818C714BE64A500A2D29C /* XPCKitTests.m */; };
- 9CB818CD14BE65BE00A2D29C /* libXPCKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EEDD02813DD485400D5AEC3 /* libXPCKit.a */; };
+ 86281F6714F96FC800F44FBD /* XPCProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 86281F6514F96FC800F44FBD /* XPCProxy.h */; };
+ 86281F6814F96FC800F44FBD /* XPCProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 86281F6514F96FC800F44FBD /* XPCProxy.h */; };
+ 86281F6914F96FC800F44FBD /* XPCProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 86281F6614F96FC800F44FBD /* XPCProxy.m */; };
+ 86281F6A14F96FC800F44FBD /* XPCProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 86281F6614F96FC800F44FBD /* XPCProxy.m */; };
9CB818D214BE685400A2D29C /* NSNull+XPCParse.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CB818D014BE685400A2D29C /* NSNull+XPCParse.h */; };
9CB818D314BE685400A2D29C /* NSNull+XPCParse.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CB818D014BE685400A2D29C /* NSNull+XPCParse.h */; };
9CB818D414BE685400A2D29C /* NSNull+XPCParse.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CB818D114BE685400A2D29C /* NSNull+XPCParse.m */; };
@@ -208,15 +207,8 @@
1EF4D9E0141C38D0007BEEC0 /* NSFileHandle+XPCParse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileHandle+XPCParse.m"; sourceTree = "<group>"; };
1EF4D9E5141C3EA2007BEEC0 /* TestApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = TestApp.entitlements; sourceTree = "<group>"; };
1EF4D9E6141C3EEE007BEEC0 /* TestService.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = TestService.entitlements; sourceTree = "<group>"; };
- 9CB818B914BE64A500A2D29C /* XPCKitTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XPCKitTests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
- 9CB818BD14BE64A500A2D29C /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
- 9CB818BE14BE64A500A2D29C /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
- 9CB818BF14BE64A500A2D29C /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
- 9CB818C214BE64A500A2D29C /* XPCKitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "XPCKitTests-Info.plist"; sourceTree = "<group>"; };
- 9CB818C414BE64A500A2D29C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
- 9CB818C614BE64A500A2D29C /* XPCKitTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPCKitTests.h; sourceTree = "<group>"; };
- 9CB818C714BE64A500A2D29C /* XPCKitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPCKitTests.m; sourceTree = "<group>"; };
- 9CB818C914BE64A500A2D29C /* XPCKitTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XPCKitTests-Prefix.pch"; sourceTree = "<group>"; };
+ 86281F6514F96FC800F44FBD /* XPCProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XPCProxy.h; sourceTree = "<group>"; };
+ 86281F6614F96FC800F44FBD /* XPCProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XPCProxy.m; sourceTree = "<group>"; };
9CB818D014BE685400A2D29C /* NSNull+XPCParse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNull+XPCParse.h"; sourceTree = "<group>"; };
9CB818D114BE685400A2D29C /* NSNull+XPCParse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNull+XPCParse.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -317,6 +309,8 @@
1EEDD08713DD508C00D5AEC3 /* XPCConnection.m */,
1E5F84A613E10DB700234F31 /* XPCService.h */,
1E5F84A713E10DB700234F31 /* XPCService.m */,
+ 86281F6514F96FC800F44FBD /* XPCProxy.h */,
+ 86281F6614F96FC800F44FBD /* XPCProxy.m */,
1EEDD0EF13DEB96F00D5AEC3 /* XPCUUID.h */,
1EEDD0F013DEB97000D5AEC3 /* XPCUUID.m */,
);
@@ -443,6 +437,7 @@
1EF4D9E2141C38D0007BEEC0 /* NSFileHandle+XPCParse.h in Headers */,
1EB022A6141D9F3700BCC1FB /* NSDate+XPCParse.h in Headers */,
9CB818D314BE685400A2D29C /* NSNull+XPCParse.h in Headers */,
+ 86281F6814F96FC800F44FBD /* XPCProxy.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -465,6 +460,7 @@
1EEDD0F113DEB97000D5AEC3 /* XPCUUID.h in Headers */,
1EB022A5141D9F3700BCC1FB /* NSDate+XPCParse.h in Headers */,
9CB818D214BE685400A2D29C /* NSNull+XPCParse.h in Headers */,
+ 86281F6714F96FC800F44FBD /* XPCProxy.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -570,6 +566,7 @@
1EEDD01F13DD485400D5AEC3 /* Project object */ = {
isa = PBXProject;
attributes = {
+ LastUpgradeCheck = 0420;
ORGANIZATIONNAME = Mustacheware;
};
buildConfigurationList = 1EEDD02213DD485400D5AEC3 /* Build configuration list for PBXProject "XPCKit" */;
@@ -666,6 +663,7 @@
1EF4D9E4141C38D0007BEEC0 /* NSFileHandle+XPCParse.m in Sources */,
1EB022A8141D9F3700BCC1FB /* NSDate+XPCParse.m in Sources */,
9CB818D514BE685400A2D29C /* NSNull+XPCParse.m in Sources */,
+ 86281F6A14F96FC800F44FBD /* XPCProxy.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -713,6 +711,7 @@
1EF4D9E3141C38D0007BEEC0 /* NSFileHandle+XPCParse.m in Sources */,
1EB022A7141D9F3700BCC1FB /* NSDate+XPCParse.m in Sources */,
9CB818D414BE685400A2D29C /* NSNull+XPCParse.m in Sources */,
+ 86281F6914F96FC800F44FBD /* XPCProxy.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -892,7 +891,6 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = TestApp/TestApp.entitlements;
- CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application";
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "TestApp/TestApp-Prefix.pch";
INFOPLIST_FILE = "TestApp/TestApp-Info.plist";
@@ -906,7 +904,6 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = TestApp/TestApp.entitlements;
- CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application";
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "TestApp/TestApp-Prefix.pch";
INFOPLIST_FILE = "TestApp/TestApp-Info.plist";
View
1 XPCKit/NSObject+XPCParse.m
@@ -45,6 +45,7 @@ +(id)objectWithXPCObject:(xpc_object_t)xpcObject{
}else if(type == XPC_TYPE_NULL){
object = [NSNull null];
}
+
return object;
}
View
1 XPCKit/XPCKit.h
@@ -25,3 +25,4 @@
#import "XPCConnection.h"
#import "XPCService.h"
+#import "XPCProxy.h"
View
51 XPCKit/XPCProxy.h
@@ -0,0 +1,51 @@
+//
+// XPCProxy.h
+// XPCKit
+//
+// Created by Brian King on 2/25/12.
+//
+
+#import <Foundation/Foundation.h>
+#import "XPCConnection.h"
+
+
+
+@interface XPCProxy : NSObject
+{
+ Class _proxyClass;
+ XPCConnection *_connection;
+ NSMutableDictionary *_definition;
+}
+/*
+ * Name an object in the service to expose objects to those connecting.
+ */
++ (void)registerProxy:(NSObject *)object named:(NSString *)named;
+
+/*
+ * Return a proxy for the class represented by Class with the selector method. IE:
+ *
+ * p = [XPCProxy proxyClass:@"ClassName"
+ * selector:@selector(singletonSelector)
+ * onConnection:_connection];
+ *
+ * BOOL b = [p methodWithDate:date integer:i andArray:a];
+ *
+ * This will serialize the NSInvocation into dictionaries, and send the dictionary over the connection.
+ * On the service, it will deserialize into an NSInvocation, call [ClassName singletonSelector], and then
+ * invoke the invocation on the result.
+ *
+ */
++ (id)proxyClass:(NSString *)className selector:(SEL)method onConnection:(XPCConnection *)connection;
+
+
+/*
+ * Same as above but with named lookup
+ */
++ (id)proxyClass:(NSString *)className named:(NSString *)name onConnection:(XPCConnection *)connection;
+
+
+
+// Call this method in the event handler of the service
++ (void)handleInvocationOfProxyMessage:(NSDictionary *)proxy fromConnection:(XPCConnection *)connection;
+
+@end
View
281 XPCKit/XPCProxy.m
@@ -0,0 +1,281 @@
+//
+// XPCProxy.m
+// XPCKit
+//
+// Created by Brian King on 2/25/12.
+//
+
+#import "XPCProxy.h"
+#import "XPCConnection.h"
+
+#pragma mark - Helper Object Categories.
+
+
+@implementation NSNumber(XPCProxy)
+
+
++ (NSNumber *)numberFromBuffer:(void *)buffer ofType:(const char *)objCType
+{
+
+ if (!strcmp(objCType, @encode(BOOL)))
+ return [NSNumber numberWithBool:*((BOOL*)buffer)];
+ else if (!strcmp(objCType, @encode(NSInteger)))
+ return [NSNumber numberWithInteger:*((NSInteger*)buffer)];
+ else
+ NSLog(@"Ignoring Unknown Return Type");
+ return nil;
+}
+
+- (void)castType:(const char *)objcType intoBuffer:(void *)buffer
+{
+ if (!strcmp(objcType, @encode(int)))
+ *((int *)buffer) = [self intValue];
+
+ else if (!strcmp(objcType, @encode(uint)))
+ *((uint *)buffer) = [self unsignedIntValue];
+
+ else if (!strcmp(objcType, @encode(double)))
+ *((double *)buffer) = [self doubleValue];
+
+ else
+ NSAssert(FALSE, @"Do not know how to decode type");
+}
+
+- (void)castIntoBuffer:(void *)buffer
+{
+ // Not sure if I can count on the type of the NSNumber, or if I should
+ // get it from the method info
+ [self castType:[self objCType] intoBuffer:buffer];
+}
+
+@end
+
+@implementation NSInvocation(XPCProxy)
+
+- (NSDictionary *)dictionaryRepresentation
+{
+ NSUInteger argumentCount = [self methodSignature].numberOfArguments;
+ NSMutableArray *arguments = [NSMutableArray array];
+ char invocationBuffer[300];
+ NSAssert(argumentCount - 2 < 28, @"Can not handle over 28 arguments - increase invocationBuffer");
+
+
+ for (NSInteger index = 2; index < argumentCount; index++)
+ {
+ const char *objCType = [[self methodSignature] getArgumentTypeAtIndex:index];
+ BOOL isObject = !strcmp(objCType, @encode(id));
+
+ id argument = nil;
+ if (isObject)
+ [self getArgument:&argument atIndex:index];
+ else
+ {
+ void *buffer = &(invocationBuffer[index*10]);
+ [self getArgument:buffer atIndex:index];
+ argument = [NSNumber numberFromBuffer:buffer ofType:objCType];
+ }
+ if (argument == nil)
+ argument = [NSNull null];
+
+ [arguments addObject:argument];
+ }
+
+ return [NSDictionary dictionaryWithObjectsAndKeys:
+ NSStringFromSelector([self selector]), @"selector",
+ arguments, @"arguments",
+ nil];
+}
+
+@end
+
+#pragma mark - XPCProxy
+
+@interface XPCProxy()
+
+@property (nonatomic, assign) Class proxyClass;
+@property (nonatomic, retain) XPCConnection *connection;
+@property (nonatomic, retain) NSMutableDictionary *definition;
+
++ (NSObject *)proxyObjectNamed:(NSString *)name;
+
+@end
+
+
+@implementation XPCProxy
+
+- (id)initForClass:(Class)proxyClass onConnection:(XPCConnection *)connection;
+{
+ self = [super init];
+ if (self) {
+ self.proxyClass = proxyClass;
+ self.connection = connection;
+ self.definition = [NSMutableDictionary dictionary];
+
+ [self.definition setObject:@"XPCProxy" forKey:@"operation"];
+ }
+ return self;
+}
+- (void)dealloc
+{
+ [_connection release];
+ [_definition release];
+ [super dealloc];
+}
+
+@synthesize definition = _definition;
+@synthesize connection = _connection;
+@synthesize proxyClass = _proxyClass;
+
+#pragma mark - Global Proxy Lookup
+static NSMutableDictionary *__XPCRegisteredProxies = nil;
++ (void)registerProxy:(NSObject *)object named:(NSString *)named
+{
+ if (__XPCRegisteredProxies == nil)
+ __XPCRegisteredProxies = [[NSMutableDictionary alloc] init];
+
+ [__XPCRegisteredProxies setValue:object forKey:named];
+}
+
++ (NSObject *)proxyObjectNamed:(NSString *)name
+{
+ return [__XPCRegisteredProxies objectForKey:name];
+}
+
+
++ (id)proxyClass:(NSString *)className named:(NSString *)name onConnection:(XPCConnection *)connection
+{
+ XPCProxy *proxy = [[[XPCProxy alloc] initForClass:NSClassFromString(className)
+ onConnection:connection] autorelease];
+ [proxy.definition setObject:name forKey:@"lookupByName"];
+
+ return proxy;
+}
+
++ (id)proxyClass:(NSString *)className selector:(SEL)method onConnection:(XPCConnection *)connection
+{
+ XPCProxy *proxy = [[[XPCProxy alloc] initForClass:NSClassFromString(className)
+ onConnection:connection] autorelease];
+
+ NSArray *keypath = [NSArray arrayWithObjects:className, NSStringFromSelector(method), nil];
+
+ [proxy.definition setObject:keypath forKey:@"lookupByKeypath"];
+
+ return proxy;
+}
+
+#pragma mark - NSInvocation to NSDictionary
+
+- (void)forwardInvocation:(NSInvocation *)invocation
+{
+ [self.definition setObject:[invocation dictionaryRepresentation] forKey:@"invocation"];
+ [self.connection sendMessage:self.definition];
+}
+
+- (NSMethodSignature *) methodSignatureForSelector:(SEL)selector {
+ return [self.proxyClass instanceMethodSignatureForSelector:selector];
+}
+
+
+#pragma mark - NSDictionary to NSInvocation
++ (NSObject *)lookupProxyFromDictionary:(NSDictionary *)proxy
+{
+ NSString *byName = [proxy objectForKey:@"lookupByName"];
+ NSArray *byKeypath = [proxy objectForKey:@"lookupByKeypath"];
+ NSAssert(byName != nil || byKeypath != nil, @"Must specify a lookup key");
+
+ if (byName)
+ return [self proxyObjectNamed:byName];
+
+ else if (byKeypath)
+ {
+ NSUInteger count = [byKeypath count];
+ NSString *class = [byKeypath objectAtIndex:0];
+ NSString *keypath = [[byKeypath subarrayWithRange:NSMakeRange(1, count - 1)] componentsJoinedByString:@"."];
+ return [NSClassFromString(class) valueForKeyPath:keypath];
+ }
+ return nil;
+}
+
+
++ (id)invokeProxyDictionary:(NSDictionary *)proxy
+{
+ NSObject *target = [self lookupProxyFromDictionary:proxy];
+ SEL selector = NSSelectorFromString([proxy valueForKeyPath:@"invocation.selector"]);
+ NSArray *arguments = [proxy valueForKeyPath:@"invocation.arguments"];
+
+ NSMethodSignature *signature = [target methodSignatureForSelector:selector];
+ char invocationBuffer[300];
+
+ NSAssert(signature.numberOfArguments - 2 < 28,
+ @"Can not handle over 28 arguments - increase invocationBuffer");
+ NSAssert(signature.numberOfArguments - 2 == [arguments count],
+ @"Wrong Argument Mapping (%d, %d)", signature.numberOfArguments - 2, [arguments count]);
+
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
+ [invocation setSelector:selector];
+
+ //
+ // Create the argument list. Indices 0 and 1 indicate the hidden arguments self and _cmd.
+ // Unbox primitive arguments represented by NSNumber, otherwise fail.
+ //
+ const char *objCType;
+ NSInteger index = 2; //
+ for( id arg in arguments ) {
+ objCType = [signature getArgumentTypeAtIndex:index];
+ BOOL isObject = !strcmp(objCType, @encode(id));
+
+ if (isObject == NO && [arg isKindOfClass:[NSNumber class]])
+ {
+ void *buffer = &(invocationBuffer[index*10]);
+ [(NSNumber *)arg castType:objCType intoBuffer:buffer];
+ [invocation setArgument:buffer atIndex:index];
+ }
+ else if (isObject)
+ [invocation setArgument:&arg atIndex:index];
+
+ else
+ NSLog(@"Ignoring Unknown Argument Type");
+
+ index++;
+ }
+
+ [invocation invokeWithTarget:target];
+
+ //
+ // Create the return object - pass (id) and box a few primitive types
+ //
+ objCType = signature.methodReturnType;
+ BOOL isVoid = !strcmp(objCType, @encode(void));
+
+ id returnValue = nil;
+ if (!strcmp(objCType, @encode(id)))
+ [invocation getReturnValue:&returnValue];
+
+ else if (isVoid == NO)
+ {
+ NSUInteger length = [signature methodReturnLength];
+ void *buffer = (void *)malloc(length);
+ [invocation getReturnValue:buffer];
+
+ returnValue = [NSNumber numberFromBuffer:buffer ofType:objCType];
+
+ free(buffer);
+ }
+ return returnValue;
+}
+
+
++ (void)handleInvocationOfProxyMessage:(NSDictionary *)proxy fromConnection:(XPCConnection *)connection
+{
+ NSLog(@"Handle %@", proxy);
+ if ([[proxy objectForKey:@"operation"] isEqualToString:@"XPCProxy"] == NO)
+ return;
+
+ id result = [self invokeProxyDictionary:proxy];
+
+ [connection sendMessage:result];
+}
+
+
+
+@end
View
14 XPCKitTests/XPCKitTests.m
@@ -92,6 +92,20 @@ -(void)testUUID{
[self testEqualityOfXPCRoundtripForObject:[XPCUUID uuid]];
}
+-(void)testProxyInvocation
+{
+ NSDictionary *methodDefinition = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSArray arrayWithObjects:@"NSFileManager", @"defaultManager", nil], @"lookupByKeypath",
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSArray arrayWithObjects:@"/tmp/foo", @"/tmp/foo1", [NSNull null], nil], @"arguments",
+ @"copyItemAtPath:toPath:error:", @"selector",
+ nil], @"invocation",
+ nil];
+
+ id value = [XPCProxy invokeProxyDictionary:methodDefinition];
+
+}
+
-(void)testEqualityOfXPCRoundtripForObject:(id)object{
STAssertNotNil(object, @"Source object is nil");

0 comments on commit a18c9d1

Please sign in to comment.