├── 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 | 
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 |
--------------------------------------------------------------------------------