├── ExtTest.xcodeproj ├── project.xcworkspace │ ├── xcuserdata │ │ └── ian.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── contents.xcworkspacedata ├── xcuserdata │ └── ian.xcuserdatad │ │ ├── xcschemes │ │ ├── xcschememanagement.plist │ │ ├── ExtTest.xcscheme │ │ └── ExtTestExtension.xcscheme │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist └── project.pbxproj ├── ExtTest ├── ViewController.h ├── AppDelegate.h ├── main.m ├── PrivateHeaders.h ├── AppDelegate.m ├── Info.plist ├── Base.lproj │ └── LaunchScreen.storyboard └── ViewController.m ├── ExtTestExtension ├── Extension.h ├── Info.plist └── Extension.m ├── TestObj.h ├── README.md └── TestObj.m /ExtTest.xcodeproj/project.xcworkspace/xcuserdata/ian.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ian-mcdowell/NSExtension-Demo/HEAD/ExtTest.xcodeproj/project.xcworkspace/xcuserdata/ian.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ExtTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ExtTest/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ExtTest 4 | // 5 | // Created by Ian McDowell on 12/18/16. 6 | // Copyright © 2016 Ian McDowell. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ExtTestExtension/Extension.h: -------------------------------------------------------------------------------- 1 | // 2 | // Extension.h 3 | // ExtTest 4 | // 5 | // Created by Ian McDowell on 12/18/16. 6 | // Copyright © 2016 Ian McDowell. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Extension : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TestObj.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestObj.h 3 | // ExtTest 4 | // 5 | // Created by Ian McDowell on 1/23/17. 6 | // Copyright © 2017 Ian McDowell. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TestObj : NSObject 12 | 13 | @property (nonatomic, strong) NSString *name; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ExtTest/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ExtTest 4 | // 5 | // Created by Ian McDowell on 12/18/16. 6 | // Copyright © 2016 Ian McDowell. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ExtTest/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ExtTest 4 | // 5 | // Created by Ian McDowell on 12/18/16. 6 | // Copyright © 2016 Ian McDowell. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NSExtension-Demo 2 | Demo project showing how to use a private NSExtension API to create your own background process on iOS. 3 | 4 | __Read all about how NSExtension works [here](https://ianmcdowell.net/blog/nsextension).__ 5 | 6 | ![Sample gif](https://ianmcdowell.net/uploads/nsextension_demo.gif) 7 | 8 | _Note: In order to do this, you must use a private API in the iOS SDK. This API may change in the future. It is not suggested that you ship an app in the App Store that uses a private API._ 9 | -------------------------------------------------------------------------------- /TestObj.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestObj.m 3 | // ExtTest 4 | // 5 | // Created by Ian McDowell on 1/23/17. 6 | // Copyright © 2017 Ian McDowell. All rights reserved. 7 | // 8 | 9 | #import "TestObj.h" 10 | 11 | @implementation TestObj 12 | 13 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 14 | if (self = [super init]) { 15 | _name = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"name"]; 16 | } 17 | return self; 18 | } 19 | 20 | + (BOOL)supportsSecureCoding { 21 | return YES; 22 | } 23 | 24 | - (void)encodeWithCoder:(NSCoder *)aCoder { 25 | [aCoder encodeObject:_name forKey:@"name"]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ExtTest.xcodeproj/xcuserdata/ian.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ExtTest.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | ExtTestExtension.xcscheme 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 3CB7F1581E07780A00E394A6 21 | 22 | primary 23 | 24 | 25 | 3CB7F1761E07781900E394A6 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ExtTest/PrivateHeaders.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrivateHeaders.h 3 | // ExtTest 4 | // 5 | // Created by Ian McDowell on 12/18/16. 6 | // Copyright © 2016 Ian McDowell. All rights reserved. 7 | // 8 | 9 | #ifndef PrivateHeaders_h 10 | #define PrivateHeaders_h 11 | 12 | @interface NSExtension : NSObject 13 | 14 | + (instancetype)extensionWithIdentifier:(NSString *)identifier error:(NSError **)error; 15 | 16 | - (void)beginExtensionRequestWithInputItems:(NSArray *)inputItems completion:(void (^)(NSUUID *requestIdentifier))completion; 17 | 18 | - (int)pidForRequestIdentifier:(NSUUID *)requestIdentifier; 19 | - (void)cancelExtensionRequestWithIdentifier:(NSUUID *)requestIdentifier; 20 | 21 | - (void)setRequestCancellationBlock:(void (^)(NSUUID *uuid, NSError *error))cancellationBlock; 22 | - (void)setRequestCompletionBlock:(void (^)(NSUUID *uuid, NSArray *extensionItems))completionBlock; 23 | - (void)setRequestInterruptionBlock:(void (^)(NSUUID *uuid))interruptionBlock; 24 | 25 | @end 26 | 27 | #endif /* PrivateHeaders_h */ 28 | -------------------------------------------------------------------------------- /ExtTestExtension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ExtTestExtension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | NSExtensionActivationRule 28 | FALSEPREDICATE 29 | 30 | NSExtensionPrincipalClass 31 | Extension 32 | NSExtensionPointIdentifier 33 | com.apple.app.non-ui-extension 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /ExtTest/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ExtTest 4 | // 5 | // Created by Ian McDowell on 12/18/16. 6 | // Copyright © 2016 Ian McDowell. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @implementation AppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 15 | 16 | // Enable extended extension logging to the console. Hopefully this will help us see more of what is going on. 17 | CFPreferencesSetValue(CFSTR("NSExtensionAssertionLoggingEnabled"), (__bridge CFPropertyListRef _Nullable)([NSNumber numberWithBool:YES]), kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesCurrentHost); 18 | 19 | CFPreferencesSetValue(CFSTR("NSExtensionDiscoveryLoggingEnabled"), (__bridge CFPropertyListRef _Nullable)([NSNumber numberWithBool:YES]), kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesCurrentHost); 20 | 21 | // Set the root view controller, since we aren't using a storyboard. 22 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 23 | [self.window setBackgroundColor:[UIColor whiteColor]]; 24 | ViewController *viewController = [[ViewController alloc] init]; 25 | [self.window setRootViewController:[[UINavigationController alloc] initWithRootViewController:viewController]]; 26 | [self.window makeKeyAndVisible]; 27 | 28 | return YES; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ExtTest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /ExtTestExtension/Extension.m: -------------------------------------------------------------------------------- 1 | // 2 | // Extension.m 3 | // ExtTest 4 | // 5 | // Created by Ian McDowell on 12/18/16. 6 | // Copyright © 2016 Ian McDowell. All rights reserved. 7 | // 8 | 9 | #import "Extension.h" 10 | #import "TestObj.h" 11 | 12 | @implementation Extension 13 | 14 | - (void)beginRequestWithExtensionContext:(NSExtensionContext *)context { 15 | NSLog(@"Beginning request with context: %@", [context description]); 16 | 17 | NSArray *inputItems = [context inputItems]; 18 | NSLog(@"Input items: %@", inputItems); 19 | 20 | // Get the input string from the context. 21 | assert([inputItems count] == 1); 22 | 23 | NSExtensionItem *inputItem = inputItems[0]; 24 | 25 | assert([[inputItem attachments] count] == 1); 26 | 27 | NSString *inputString = [inputItem attachments][0]; 28 | 29 | 30 | // Uppercase the string. 31 | NSString *outputString = [inputString uppercaseString]; 32 | 33 | 34 | // Instantiate a new object to return to the app. We could just put the NSString in the attachments, but this is here to show how to use your own objects. 35 | TestObj *obj = [[TestObj alloc] init]; 36 | [obj setName:outputString]; 37 | 38 | // Archive that object into NSData 39 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:obj]; 40 | 41 | // Create an extension item and attach the data. 42 | NSExtensionItem *item = [[NSExtensionItem alloc] init]; 43 | [item setAttachments:@[data]]; 44 | 45 | // Pass the extension item to the context (sends it back to the host app) 46 | [context completeRequestReturningItems:@[item] completionHandler:^(BOOL expired) { 47 | NSLog(@"Completed request."); 48 | }]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ExtTest/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ExtTest.xcodeproj/xcuserdata/ian.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 40 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /ExtTest.xcodeproj/xcuserdata/ian.xcuserdatad/xcschemes/ExtTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ExtTest.xcodeproj/xcuserdata/ian.xcuserdatad/xcschemes/ExtTestExtension.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 70 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | 92 | 98 | 99 | 100 | 101 | 103 | 104 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /ExtTest/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ExtTest 4 | // 5 | // Created by Ian McDowell on 12/18/16. 6 | // Copyright © 2016 Ian McDowell. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "PrivateHeaders.h" 12 | 13 | #import "TestObj.h" 14 | 15 | @interface ViewController () 16 | 17 | @property (nonatomic, strong) NSExtension *extension; 18 | 19 | @property (nonatomic, strong) UITextField *textField; 20 | @property (nonatomic, strong) UIButton *requestButton; 21 | @property (nonatomic, strong) UILabel *responseLabel; 22 | 23 | @end 24 | 25 | @implementation ViewController 26 | 27 | - (instancetype)init 28 | { 29 | self = [super init]; 30 | if (self) { 31 | self.title = @"Extension Test"; 32 | self.edgesForExtendedLayout = UIRectEdgeNone; 33 | 34 | NSError *error; 35 | self.extension = [NSExtension extensionWithIdentifier:@"net.ianmcdowell.ExtTest.ExtTestExtension" error:&error]; 36 | 37 | NSLog(@"Loaded extension: %@", [self.extension description]); 38 | 39 | // This block will be called if the extension calls 40 | // [context cancelRequestWithError:] 41 | [self.extension setRequestCancellationBlock:^(NSUUID *uuid, NSError *error) { 42 | NSLog(@"Request %@ cancelled. %@", uuid, error); 43 | }]; 44 | 45 | // This block will be called if the extension process crashes or there was an XPC communication issue. 46 | [self.extension setRequestInterruptionBlock:^(NSUUID *uuid) { 47 | NSLog(@"Request %@ interrupted.", uuid); 48 | }]; 49 | 50 | __weak ViewController *weakSelf = self; 51 | // This block will be called if the extension calls 52 | // [context completeRequestReturningItems:completionHandler:] 53 | [self.extension setRequestCompletionBlock:^(NSUUID *uuid, NSArray *extensionItems) { 54 | 55 | NSLog(@"Request %@ completed.", uuid); 56 | 57 | // In this scenario, we are assuming that the extension will always return 1 extension item. 58 | // That extension item will always contain an attachment, which is an NSData serialization 59 | // of a TestObj. Your use case may vary. 60 | 61 | assert([extensionItems count] == 1); 62 | 63 | NSExtensionItem *item = extensionItems[0]; 64 | NSArray *attachments = [item attachments]; 65 | 66 | assert([attachments count] == 1); 67 | 68 | NSData *attachmentData = attachments[0]; 69 | NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:attachmentData]; 70 | [unarchiver setRequiresSecureCoding:YES]; 71 | 72 | TestObj *obj = [unarchiver decodeObjectOfClass:[TestObj class] forKey:NSKeyedArchiveRootObjectKey]; 73 | 74 | NSLog(@"Received response object: %@", obj); 75 | 76 | // This block is called on a background queue, so to modify our UI, we must dispatch back to the main queue. 77 | dispatch_async(dispatch_get_main_queue(), ^{ 78 | // Show the response in the label. 79 | [weakSelf.responseLabel setText:[obj name]]; 80 | }); 81 | 82 | }]; 83 | } 84 | return self; 85 | } 86 | 87 | - (void)viewDidLoad { 88 | [super viewDidLoad]; 89 | 90 | [self.view setBackgroundColor:[UIColor whiteColor]]; 91 | 92 | self.textField = [[UITextField alloc] init]; 93 | [self.textField setPlaceholder:@"Enter text to send to the extension"]; 94 | 95 | self.requestButton = [UIButton buttonWithType:UIButtonTypeSystem]; 96 | [self.requestButton setTitle:@"Send" forState:UIControlStateNormal]; 97 | [self.requestButton addTarget:self action:@selector(sendExtensionRequest) forControlEvents:UIControlEventTouchUpInside]; 98 | 99 | self.responseLabel = [[UILabel alloc] init]; 100 | [self.responseLabel setNumberOfLines:0]; 101 | [self.responseLabel setText:@"Send a request and the response will appear here."]; 102 | 103 | 104 | // Add to view 105 | UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:@[self.textField, self.requestButton, self.responseLabel]]; 106 | [stackView setTranslatesAutoresizingMaskIntoConstraints:NO]; 107 | [stackView setSpacing:25]; 108 | [stackView setAlignment:UIStackViewAlignmentFill]; 109 | [stackView setAxis:UILayoutConstraintAxisVertical]; 110 | [stackView setDistribution:UIStackViewDistributionFillProportionally]; 111 | 112 | [self.view addSubview:stackView]; 113 | [[stackView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:25] setActive:YES]; 114 | [[stackView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant: -25] setActive:YES]; 115 | [[stackView.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:25] setActive:YES]; 116 | } 117 | 118 | -(void)viewDidAppear:(BOOL)animated { 119 | [super viewDidAppear:animated]; 120 | 121 | [self.textField becomeFirstResponder]; 122 | } 123 | 124 | - (void)sendExtensionRequest { 125 | 126 | NSString *input = [self.textField text]; 127 | 128 | NSExtensionItem *item = [[NSExtensionItem alloc] init]; 129 | [item setAttachments:@[input]]; 130 | 131 | // Start a request to the extension with the given input items. 132 | [self.extension beginExtensionRequestWithInputItems:@[item] completion:^void (NSUUID *requestIdentifier){ 133 | 134 | int pid = [self.extension pidForRequestIdentifier:requestIdentifier]; 135 | 136 | NSLog(@"Started extension request: %@. Extension PID is: %i", requestIdentifier, pid); 137 | }]; 138 | } 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /ExtTest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3CB7F15E1E07780A00E394A6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3CB7F15D1E07780A00E394A6 /* main.m */; }; 11 | 3CB7F1611E07780A00E394A6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3CB7F1601E07780A00E394A6 /* AppDelegate.m */; }; 12 | 3CB7F1641E07780A00E394A6 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3CB7F1631E07780A00E394A6 /* ViewController.m */; }; 13 | 3CB7F16C1E07780A00E394A6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3CB7F16A1E07780A00E394A6 /* LaunchScreen.storyboard */; }; 14 | 3CB7F1821E07781900E394A6 /* ExtTestExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 3CB7F1771E07781900E394A6 /* ExtTestExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 15 | 3CB7F1891E07788300E394A6 /* Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 3CB7F1881E07788300E394A6 /* Extension.m */; }; 16 | 3CD9DD341E36646F0095EB53 /* TestObj.m in Sources */ = {isa = PBXBuildFile; fileRef = 3CD9DD331E36646F0095EB53 /* TestObj.m */; }; 17 | 3CD9DD351E3664EF0095EB53 /* TestObj.m in Sources */ = {isa = PBXBuildFile; fileRef = 3CD9DD331E36646F0095EB53 /* TestObj.m */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 3CB7F1801E07781900E394A6 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 3CB7F1511E07780A00E394A6 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 3CB7F1761E07781900E394A6; 26 | remoteInfo = ExtTestExtension; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXCopyFilesBuildPhase section */ 31 | 3CB7F1861E07781900E394A6 /* Embed App Extensions */ = { 32 | isa = PBXCopyFilesBuildPhase; 33 | buildActionMask = 2147483647; 34 | dstPath = ""; 35 | dstSubfolderSpec = 13; 36 | files = ( 37 | 3CB7F1821E07781900E394A6 /* ExtTestExtension.appex in Embed App Extensions */, 38 | ); 39 | name = "Embed App Extensions"; 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXCopyFilesBuildPhase section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 3CB7F1591E07780A00E394A6 /* ExtTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExtTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 3CB7F15D1E07780A00E394A6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 47 | 3CB7F15F1E07780A00E394A6 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 48 | 3CB7F1601E07780A00E394A6 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 49 | 3CB7F1621E07780A00E394A6 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 50 | 3CB7F1631E07780A00E394A6 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 51 | 3CB7F16B1E07780A00E394A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 52 | 3CB7F16D1E07780A00E394A6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 3CB7F1771E07781900E394A6 /* ExtTestExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ExtTestExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 3CB7F17F1E07781900E394A6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | 3CB7F1871E07788300E394A6 /* Extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Extension.h; sourceTree = ""; }; 56 | 3CB7F1881E07788300E394A6 /* Extension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Extension.m; sourceTree = ""; }; 57 | 3CB7F18A1E0778C600E394A6 /* PrivateHeaders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrivateHeaders.h; sourceTree = ""; }; 58 | 3CD9DD321E36646F0095EB53 /* TestObj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestObj.h; sourceTree = ""; }; 59 | 3CD9DD331E36646F0095EB53 /* TestObj.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestObj.m; sourceTree = ""; }; 60 | /* End PBXFileReference section */ 61 | 62 | /* Begin PBXFrameworksBuildPhase section */ 63 | 3CB7F1561E07780A00E394A6 /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | 3CB7F1741E07781900E394A6 /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | /* End PBXFrameworksBuildPhase section */ 78 | 79 | /* Begin PBXGroup section */ 80 | 3CB7F1501E07780A00E394A6 = { 81 | isa = PBXGroup; 82 | children = ( 83 | 3CD9DD321E36646F0095EB53 /* TestObj.h */, 84 | 3CD9DD331E36646F0095EB53 /* TestObj.m */, 85 | 3CB7F15B1E07780A00E394A6 /* ExtTest */, 86 | 3CB7F1781E07781900E394A6 /* ExtTestExtension */, 87 | 3CB7F15A1E07780A00E394A6 /* Products */, 88 | ); 89 | sourceTree = ""; 90 | }; 91 | 3CB7F15A1E07780A00E394A6 /* Products */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 3CB7F1591E07780A00E394A6 /* ExtTest.app */, 95 | 3CB7F1771E07781900E394A6 /* ExtTestExtension.appex */, 96 | ); 97 | name = Products; 98 | sourceTree = ""; 99 | }; 100 | 3CB7F15B1E07780A00E394A6 /* ExtTest */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 3CB7F18A1E0778C600E394A6 /* PrivateHeaders.h */, 104 | 3CB7F15F1E07780A00E394A6 /* AppDelegate.h */, 105 | 3CB7F1601E07780A00E394A6 /* AppDelegate.m */, 106 | 3CB7F1621E07780A00E394A6 /* ViewController.h */, 107 | 3CB7F1631E07780A00E394A6 /* ViewController.m */, 108 | 3CB7F15C1E07780A00E394A6 /* Supporting Files */, 109 | ); 110 | path = ExtTest; 111 | sourceTree = ""; 112 | }; 113 | 3CB7F15C1E07780A00E394A6 /* Supporting Files */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 3CB7F16A1E07780A00E394A6 /* LaunchScreen.storyboard */, 117 | 3CB7F16D1E07780A00E394A6 /* Info.plist */, 118 | 3CB7F15D1E07780A00E394A6 /* main.m */, 119 | ); 120 | name = "Supporting Files"; 121 | sourceTree = ""; 122 | }; 123 | 3CB7F1781E07781900E394A6 /* ExtTestExtension */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 3CB7F17F1E07781900E394A6 /* Info.plist */, 127 | 3CB7F1871E07788300E394A6 /* Extension.h */, 128 | 3CB7F1881E07788300E394A6 /* Extension.m */, 129 | ); 130 | path = ExtTestExtension; 131 | sourceTree = ""; 132 | }; 133 | /* End PBXGroup section */ 134 | 135 | /* Begin PBXNativeTarget section */ 136 | 3CB7F1581E07780A00E394A6 /* ExtTest */ = { 137 | isa = PBXNativeTarget; 138 | buildConfigurationList = 3CB7F1701E07780A00E394A6 /* Build configuration list for PBXNativeTarget "ExtTest" */; 139 | buildPhases = ( 140 | 3CB7F1551E07780A00E394A6 /* Sources */, 141 | 3CB7F1561E07780A00E394A6 /* Frameworks */, 142 | 3CB7F1571E07780A00E394A6 /* Resources */, 143 | 3CB7F1861E07781900E394A6 /* Embed App Extensions */, 144 | ); 145 | buildRules = ( 146 | ); 147 | dependencies = ( 148 | 3CB7F1811E07781900E394A6 /* PBXTargetDependency */, 149 | ); 150 | name = ExtTest; 151 | productName = ExtTest; 152 | productReference = 3CB7F1591E07780A00E394A6 /* ExtTest.app */; 153 | productType = "com.apple.product-type.application"; 154 | }; 155 | 3CB7F1761E07781900E394A6 /* ExtTestExtension */ = { 156 | isa = PBXNativeTarget; 157 | buildConfigurationList = 3CB7F1831E07781900E394A6 /* Build configuration list for PBXNativeTarget "ExtTestExtension" */; 158 | buildPhases = ( 159 | 3CB7F1731E07781900E394A6 /* Sources */, 160 | 3CB7F1741E07781900E394A6 /* Frameworks */, 161 | 3CB7F1751E07781900E394A6 /* Resources */, 162 | ); 163 | buildRules = ( 164 | ); 165 | dependencies = ( 166 | ); 167 | name = ExtTestExtension; 168 | productName = ExtTestExtension; 169 | productReference = 3CB7F1771E07781900E394A6 /* ExtTestExtension.appex */; 170 | productType = "com.apple.product-type.app-extension"; 171 | }; 172 | /* End PBXNativeTarget section */ 173 | 174 | /* Begin PBXProject section */ 175 | 3CB7F1511E07780A00E394A6 /* Project object */ = { 176 | isa = PBXProject; 177 | attributes = { 178 | LastUpgradeCheck = 0820; 179 | ORGANIZATIONNAME = "Ian McDowell"; 180 | TargetAttributes = { 181 | 3CB7F1581E07780A00E394A6 = { 182 | CreatedOnToolsVersion = 8.2; 183 | DevelopmentTeam = A8DZBD3K9C; 184 | ProvisioningStyle = Automatic; 185 | }; 186 | 3CB7F1761E07781900E394A6 = { 187 | CreatedOnToolsVersion = 8.2; 188 | DevelopmentTeam = A8DZBD3K9C; 189 | ProvisioningStyle = Automatic; 190 | }; 191 | }; 192 | }; 193 | buildConfigurationList = 3CB7F1541E07780A00E394A6 /* Build configuration list for PBXProject "ExtTest" */; 194 | compatibilityVersion = "Xcode 3.2"; 195 | developmentRegion = English; 196 | hasScannedForEncodings = 0; 197 | knownRegions = ( 198 | en, 199 | Base, 200 | ); 201 | mainGroup = 3CB7F1501E07780A00E394A6; 202 | productRefGroup = 3CB7F15A1E07780A00E394A6 /* Products */; 203 | projectDirPath = ""; 204 | projectRoot = ""; 205 | targets = ( 206 | 3CB7F1581E07780A00E394A6 /* ExtTest */, 207 | 3CB7F1761E07781900E394A6 /* ExtTestExtension */, 208 | ); 209 | }; 210 | /* End PBXProject section */ 211 | 212 | /* Begin PBXResourcesBuildPhase section */ 213 | 3CB7F1571E07780A00E394A6 /* Resources */ = { 214 | isa = PBXResourcesBuildPhase; 215 | buildActionMask = 2147483647; 216 | files = ( 217 | 3CB7F16C1E07780A00E394A6 /* LaunchScreen.storyboard in Resources */, 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | 3CB7F1751E07781900E394A6 /* Resources */ = { 222 | isa = PBXResourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | }; 228 | /* End PBXResourcesBuildPhase section */ 229 | 230 | /* Begin PBXSourcesBuildPhase section */ 231 | 3CB7F1551E07780A00E394A6 /* Sources */ = { 232 | isa = PBXSourcesBuildPhase; 233 | buildActionMask = 2147483647; 234 | files = ( 235 | 3CB7F1641E07780A00E394A6 /* ViewController.m in Sources */, 236 | 3CB7F1611E07780A00E394A6 /* AppDelegate.m in Sources */, 237 | 3CB7F15E1E07780A00E394A6 /* main.m in Sources */, 238 | 3CD9DD341E36646F0095EB53 /* TestObj.m in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | 3CB7F1731E07781900E394A6 /* Sources */ = { 243 | isa = PBXSourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | 3CD9DD351E3664EF0095EB53 /* TestObj.m in Sources */, 247 | 3CB7F1891E07788300E394A6 /* Extension.m in Sources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | /* End PBXSourcesBuildPhase section */ 252 | 253 | /* Begin PBXTargetDependency section */ 254 | 3CB7F1811E07781900E394A6 /* PBXTargetDependency */ = { 255 | isa = PBXTargetDependency; 256 | target = 3CB7F1761E07781900E394A6 /* ExtTestExtension */; 257 | targetProxy = 3CB7F1801E07781900E394A6 /* PBXContainerItemProxy */; 258 | }; 259 | /* End PBXTargetDependency section */ 260 | 261 | /* Begin PBXVariantGroup section */ 262 | 3CB7F16A1E07780A00E394A6 /* LaunchScreen.storyboard */ = { 263 | isa = PBXVariantGroup; 264 | children = ( 265 | 3CB7F16B1E07780A00E394A6 /* Base */, 266 | ); 267 | name = LaunchScreen.storyboard; 268 | sourceTree = ""; 269 | }; 270 | /* End PBXVariantGroup section */ 271 | 272 | /* Begin XCBuildConfiguration section */ 273 | 3CB7F16E1E07780A00E394A6 /* Debug */ = { 274 | isa = XCBuildConfiguration; 275 | buildSettings = { 276 | ALWAYS_SEARCH_USER_PATHS = NO; 277 | CLANG_ANALYZER_NONNULL = YES; 278 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 279 | CLANG_CXX_LIBRARY = "libc++"; 280 | CLANG_ENABLE_MODULES = YES; 281 | CLANG_ENABLE_OBJC_ARC = YES; 282 | CLANG_WARN_BOOL_CONVERSION = YES; 283 | CLANG_WARN_CONSTANT_CONVERSION = YES; 284 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 285 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 286 | CLANG_WARN_EMPTY_BODY = YES; 287 | CLANG_WARN_ENUM_CONVERSION = YES; 288 | CLANG_WARN_INFINITE_RECURSION = YES; 289 | CLANG_WARN_INT_CONVERSION = YES; 290 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 291 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 292 | CLANG_WARN_UNREACHABLE_CODE = YES; 293 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 294 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 295 | COPY_PHASE_STRIP = NO; 296 | DEBUG_INFORMATION_FORMAT = dwarf; 297 | ENABLE_STRICT_OBJC_MSGSEND = YES; 298 | ENABLE_TESTABILITY = YES; 299 | GCC_C_LANGUAGE_STANDARD = gnu99; 300 | GCC_DYNAMIC_NO_PIC = NO; 301 | GCC_NO_COMMON_BLOCKS = YES; 302 | GCC_OPTIMIZATION_LEVEL = 0; 303 | GCC_PREPROCESSOR_DEFINITIONS = ( 304 | "DEBUG=1", 305 | "$(inherited)", 306 | ); 307 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 308 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 309 | GCC_WARN_UNDECLARED_SELECTOR = YES; 310 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 311 | GCC_WARN_UNUSED_FUNCTION = YES; 312 | GCC_WARN_UNUSED_VARIABLE = YES; 313 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 314 | MTL_ENABLE_DEBUG_INFO = YES; 315 | ONLY_ACTIVE_ARCH = YES; 316 | SDKROOT = iphoneos; 317 | TARGETED_DEVICE_FAMILY = "1,2"; 318 | }; 319 | name = Debug; 320 | }; 321 | 3CB7F16F1E07780A00E394A6 /* Release */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | ALWAYS_SEARCH_USER_PATHS = NO; 325 | CLANG_ANALYZER_NONNULL = YES; 326 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 327 | CLANG_CXX_LIBRARY = "libc++"; 328 | CLANG_ENABLE_MODULES = YES; 329 | CLANG_ENABLE_OBJC_ARC = YES; 330 | CLANG_WARN_BOOL_CONVERSION = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INFINITE_RECURSION = YES; 337 | CLANG_WARN_INT_CONVERSION = YES; 338 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 339 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 340 | CLANG_WARN_UNREACHABLE_CODE = YES; 341 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 342 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 343 | COPY_PHASE_STRIP = NO; 344 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 345 | ENABLE_NS_ASSERTIONS = NO; 346 | ENABLE_STRICT_OBJC_MSGSEND = YES; 347 | GCC_C_LANGUAGE_STANDARD = gnu99; 348 | GCC_NO_COMMON_BLOCKS = YES; 349 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 350 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 351 | GCC_WARN_UNDECLARED_SELECTOR = YES; 352 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 353 | GCC_WARN_UNUSED_FUNCTION = YES; 354 | GCC_WARN_UNUSED_VARIABLE = YES; 355 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 356 | MTL_ENABLE_DEBUG_INFO = NO; 357 | SDKROOT = iphoneos; 358 | TARGETED_DEVICE_FAMILY = "1,2"; 359 | VALIDATE_PRODUCT = YES; 360 | }; 361 | name = Release; 362 | }; 363 | 3CB7F1711E07780A00E394A6 /* Debug */ = { 364 | isa = XCBuildConfiguration; 365 | buildSettings = { 366 | DEVELOPMENT_TEAM = A8DZBD3K9C; 367 | INFOPLIST_FILE = ExtTest/Info.plist; 368 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 369 | PRODUCT_BUNDLE_IDENTIFIER = net.ianmcdowell.ExtTest; 370 | PRODUCT_NAME = "$(TARGET_NAME)"; 371 | }; 372 | name = Debug; 373 | }; 374 | 3CB7F1721E07780A00E394A6 /* Release */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | DEVELOPMENT_TEAM = A8DZBD3K9C; 378 | INFOPLIST_FILE = ExtTest/Info.plist; 379 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 380 | PRODUCT_BUNDLE_IDENTIFIER = net.ianmcdowell.ExtTest; 381 | PRODUCT_NAME = "$(TARGET_NAME)"; 382 | }; 383 | name = Release; 384 | }; 385 | 3CB7F1841E07781900E394A6 /* Debug */ = { 386 | isa = XCBuildConfiguration; 387 | buildSettings = { 388 | DEVELOPMENT_TEAM = A8DZBD3K9C; 389 | INFOPLIST_FILE = ExtTestExtension/Info.plist; 390 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 391 | PRODUCT_BUNDLE_IDENTIFIER = net.ianmcdowell.ExtTest.ExtTestExtension; 392 | PRODUCT_NAME = "$(TARGET_NAME)"; 393 | SKIP_INSTALL = YES; 394 | }; 395 | name = Debug; 396 | }; 397 | 3CB7F1851E07781900E394A6 /* Release */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | DEVELOPMENT_TEAM = A8DZBD3K9C; 401 | INFOPLIST_FILE = ExtTestExtension/Info.plist; 402 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 403 | PRODUCT_BUNDLE_IDENTIFIER = net.ianmcdowell.ExtTest.ExtTestExtension; 404 | PRODUCT_NAME = "$(TARGET_NAME)"; 405 | SKIP_INSTALL = YES; 406 | }; 407 | name = Release; 408 | }; 409 | /* End XCBuildConfiguration section */ 410 | 411 | /* Begin XCConfigurationList section */ 412 | 3CB7F1541E07780A00E394A6 /* Build configuration list for PBXProject "ExtTest" */ = { 413 | isa = XCConfigurationList; 414 | buildConfigurations = ( 415 | 3CB7F16E1E07780A00E394A6 /* Debug */, 416 | 3CB7F16F1E07780A00E394A6 /* Release */, 417 | ); 418 | defaultConfigurationIsVisible = 0; 419 | defaultConfigurationName = Release; 420 | }; 421 | 3CB7F1701E07780A00E394A6 /* Build configuration list for PBXNativeTarget "ExtTest" */ = { 422 | isa = XCConfigurationList; 423 | buildConfigurations = ( 424 | 3CB7F1711E07780A00E394A6 /* Debug */, 425 | 3CB7F1721E07780A00E394A6 /* Release */, 426 | ); 427 | defaultConfigurationIsVisible = 0; 428 | defaultConfigurationName = Release; 429 | }; 430 | 3CB7F1831E07781900E394A6 /* Build configuration list for PBXNativeTarget "ExtTestExtension" */ = { 431 | isa = XCConfigurationList; 432 | buildConfigurations = ( 433 | 3CB7F1841E07781900E394A6 /* Debug */, 434 | 3CB7F1851E07781900E394A6 /* Release */, 435 | ); 436 | defaultConfigurationIsVisible = 0; 437 | defaultConfigurationName = Release; 438 | }; 439 | /* End XCConfigurationList section */ 440 | }; 441 | rootObject = 3CB7F1511E07780A00E394A6 /* Project object */; 442 | } 443 | --------------------------------------------------------------------------------