Skip to content

Instantly share code, notes, and snippets.

@chrismaddern
Last active December 31, 2015 06:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrismaddern/7950022 to your computer and use it in GitHub Desktop.
Save chrismaddern/7950022 to your computer and use it in GitHub Desktop.
Playing around with what the interface could look like for potential XPC (3rd party remote view controllers) features in iOS 8
/*
Assume that remote view controllers are instantiable inside of any application.
Give developers a header to give the instantiated VC an interface and be able to compile etc..
*/
#import "VENNewTransactionViewControllerXPCStub.h" // Give developers this header to give the transaction view controller an interface
...
// Assume a concrete class is available and instantiate it
if (NSClassFromString(@"VENNewTransactionViewController") {
VENNewTransactionViewController *newTransactionController = [[VENNewTransactionViewController alloc] init];
}
/*
Infer the existence of an NSXPCApp class which represents an installed app and allows you
to interact with the process. The developer would again need a header to add an interface
to the XCPApp with the methods we need for our custom functionality
*/
#import "VENXPCApp.h"
..
// Check for an installed app and do some XPC (totally made up interface)
VENXPCApp *app = (VENXPCApp *)[NSXPCApp appWithIdentifier:@"com.venmo.ios"];
if (app) {
[app showNewTransactionControllerWithRecipient:recipient andAmount:amount];
}
/*
A function exists to fetch a remote view controller by name if it exists.
Remote view controllers cannot provide custom interfaces but can simply be displayed in v1?
*/
#import "VENXPCApp.h"
..
UIRemoteViewController *remoteVC = UIRemoteViewControllerFetch(@"VENNewTransactionViewController");
if (remoteVC) {
[remoteVC present];
}
/*
Let's extend that to allow us to use ObjC_msgsend to run custom methods. This would be possible
without having to give the developer any third party code to include in their app.
*/
UIRemoteViewController *remoteVC = UIRemoteViewControllerFetch(@"VENNewTransactionViewController");
if (remoteVC && [remoteVC respondsToSelector:@selector(configureWithRecipient:andAmount:)]) {
objc_msgsend(remoteVC, @selector(configureWithRecipient:andAmount:), recipient, amount);
[remoteVC present];
}
/*
What if we don't just want a remote ViewController, but instead want another process to do some
work for us in it's context and return us some result. I'm going to run with the 'NSXPCApp' metaphor
for this one.
*/
#import "VENXPCApp.h"
..
VENXPCApp *app = (VENXPCApp *)[NSXPCApp appWithIdentifier:@"com.venmo.ios"];
if (app) {
[app calculateTotalPriceForTransactionWithComponents:components withCompletionHandler:^(BOOL status, NSData *result, NSError *err){
if (status && !err) {
// Extract the result we want from the response
NSString *total = [[NSString alloc] initWithData:result usingEncoding:NSUTF8StringEncoding];
// Update some UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
totalField.text = total;
});
}
else {
// handle the error in some way
}
}];
}
/*
If we're willing to give up the specificity of the call, we can make this much more generic and
remove the need for a developer to import a header at all.
*/
NSXPCApp *app = [NSXPCApp appWithIdentifier:@"com.venmo.ios"];
if (app) {
[app performXPCMethod:@"TotalPriceCalculation" withData:[@{VENComponentsKey: components} dataValue] andCompletionHandler:^(BOOL status, NSData *result, NSError *err){
if (status && !err) {
// Extract the result we want from the response
NSString *total = [[NSString alloc] initWithData:result usingEncoding:NSUTF8StringEncoding];
// Update some UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
totalField.text = total;
});
}
else {
// handle the error in some way
}
}];
}
/*
The receiving app for this request for processing would likely handle it very much like
we currently handle URL requests where we build our own protocol inside the app where
each calling
*/
- (BOOL)receivedXPCCallWithMethodName:(NSString *)methodName
inputData:(NSData *)data
andCompletionHandler:((^)(BOOL, NSData *, NSError *))completionHandler {
if ([methodName isEqualToString:@"TotalPriceCalculation"]) {
// Extract arguments from the passed data
NSDictionary *arguments = [NSDictionary dictionaryFromData:data usingEncoding:NSUTF8StringEncoding]; // Made up method
// Perform our local calculation
NSString *totalPrice = [self totalPriceForComponents:arguments[VENComponentsKey]];
// Call the XPC closure
completionHandler(totalPrice ? YES : NO, [totalPrice dataUsingEncoding:NSUTF8StringEncoding], nil);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment