Last active
December 31, 2015 06:49
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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