Slide 1

Slide 1 text

Plug-in Architectures in Cocoa Katsuma Tanaka

Slide 2

Slide 2 text

@questbeat

Slide 3

Slide 3 text

Plug-in Architectures in Cocoa - Sample program is available on GitHub. https://github.com/questbeat/PlugInSample

Slide 4

Slide 4 text

Introduction: Creating Xcode Plug-in

Slide 5

Slide 5 text

- github.com/questbeat/Lin Creating Xcode Plug-in

Slide 6

Slide 6 text

Creating Xcode Plug-in - Create a new project.

Slide 7

Slide 7 text

+ (void)pluginDidLoad:(NSBundle *)plugin { [self sharedPlugin]; } + (id)sharedPlugin { static id sharedPlugin = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedPlugin = [[self alloc] init]; }); } Creating Xcode Plug-in - Lin.m

Slide 8

Slide 8 text

- Info.plist Creating Xcode Plug-in - Build settings

Slide 9

Slide 9 text

- Install the plug-in in the appropriate folder. Creating Xcode Plug-in

Slide 10

Slide 10 text

- Installation can be easier by Installation Directory settings. Creating Xcode Plug-in

Slide 11

Slide 11 text

- Plug-in works on the same process as Xcode. Creating Xcode Plug-in

Slide 12

Slide 12 text

What is a plug-in? - A plug-in is a loadable bundle that adds functionality to an application. MyBundle.bundle/ Contents/ Info.plist Frameworks/ MacOS/ Resources/ ...

Slide 13

Slide 13 text

- Application (.app) - Framework (.framework) - Preference Pane (.prefPane) - Screen Saver (.saver) - Xcode Plug-in (.xcplugin) Loadable Bundles are used in ...

Slide 14

Slide 14 text

Loading Bundles

Slide 15

Slide 15 text

1. Locate the bundle 2. Load the bundle’s executable code 3. Instantiate an object of the principal class Loading Bundles Basic Three Steps

Slide 16

Slide 16 text

MyApp.app/ Contents/ PlugIns/ MyPlugin.bundle Resources/ Frameworks/ ... Locating Bundles Standard Location

Slide 17

Slide 17 text

NSBundle *appBundle = [NSBundle mainBundle]; NSString *plugInsPath = [appBundle builtInPlugInsPath]; “/Applications/MyApp.app/Contents/PlugIns” Standard Location Locating Bundles

Slide 18

Slide 18 text

MyApp.app/ Contents/ Resources/ PlugIns/ MyPlugin.bundle Frameworks/ ... Nonstandard Location Locating Bundles

Slide 19

Slide 19 text

NSBundle *appBundle = [NSBundle mainBundle]; NSArray *bundlePaths = [appBundle pathsForResourcesOfType:@"bundle" inDirectory:@"PlugIns"]; Nonstandard Location Locating Bundles

Slide 20

Slide 20 text

/Users/questbeat/Library/Application Support/MyApp/PlugIns User-speci c /Library/Application Support/MyApp/PlugIns /Network/Library/Application Support/MyApp/PlugIns System-wide Network Support Library Directories Locating Bundles

Slide 21

Slide 21 text

NSString *appSupportSubpath = @"Application Support/MyApp/PlugIns"; NSMutableArray *bundleSearchPaths = [NSMutableArray array]; NSArray *librarySearchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask - NSSystemDomainMask, YES); for (NSString *path in librarySearchPaths) { [bundleSearchPaths addObject:[path stringByAppendingPathComponent:appSupportSubpath]]; } Support Library Directories Locating Bundles

Slide 22

Slide 22 text

1. Locate the bundle 2. Load the bundle’s executable code 3. Instantiate an object of the principal class Loading Bundles Basic Three Steps

Slide 23

Slide 23 text

NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; Creating a NSBundle Object

Slide 24

Slide 24 text

NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; if ([bundle load]) { NSLog(@"Bundle was successfully loaded."); } Loading Code

Slide 25

Slide 25 text

Loading Code and Get Errors NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; NSError *error = nil; if (![bundle loadAndReturnError:&error]) { NSLog(@"Error: %@", [error localizedDescription]); }

Slide 26

Slide 26 text

Checking Code without Loading NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; NSError *error = nil; if (![bundle preflightAndReturnError:&error]) { NSLog(@"Error: %@", [error localizedDescription]); }

Slide 27

Slide 27 text

Unloading Code NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; [bundle load]; if ([bundle isLoaded] && [bundle unload]) { NSLog(@"Bundle was successfully unloaded."); }

Slide 28

Slide 28 text

1. Locate the bundle 2. Load the bundle’s executable code 3. Instantiate an object of the principal class Loading Bundles Basic Three Steps

Slide 29

Slide 29 text

Retrieving the Principal Class

Slide 30

Slide 30 text

NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; Class principalClass = [bundle principalClass]; Retrieving the Principal Class

Slide 31

Slide 31 text

NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; Class someClass = [bundle classNamed:@"MyAppController"]; Retrieving the Principal Class

Slide 32

Slide 32 text

NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; Class principalClass = [bundle principalClass]; id instance = [[principalClass alloc] init]; Instantiating the Principal Class

Slide 33

Slide 33 text

Instantiating the Principal Class NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; Class principalClass = [bundle principalClass]; id instance = [[principalClass alloc] init]; [instance greet]; @protocol MyPlugIn - (void)greet; @end ... - Host application should provide an interface.

Slide 34

Slide 34 text

Creating Plug-in Architectures

Slide 35

Slide 35 text

- Use a formal protocol - Use an informal protocol - Use an abstract base class Creating Plug-in Architectures Three Ways to Provide Interface

Slide 36

Slide 36 text

#import @protocol MyAppBitmapGraphicsFiltering @required // Returns the version of the interface you're implementing. - (unsigned)interfaceVersion; // Filters the bitmap and returns a modified version. - (NSBitmapImageRep *)filteredImageRep:(NSBitmapImageRep *)imageRep; @optional // Returns what to display in the Filter menu. - (NSString *)menuItemString; // Returns the window controller for the settings configuration window. - (NSWindowController *)configurationWindowController; @end Use a Formal Protocol

Slide 37

Slide 37 text

#import @interface NSObject (MyAppBitmapGraphicsFiltering) // REQUIRED // Returns the version of the interface you're implementing. - (unsigned)interfaceVersion; // REQUIRED // Filters the bitmap and returns a modified version. - (NSBitmapImageRep *)filteredImageRep:(NSBitmapImageRep *)imageRep; // OPTIONAL // Returns what to display in the Filter menu. - (NSString *)menuItemString; // OPTIONAL // Returns the window controller for the settings configuration window. - (NSWindowController *)configurationWindowController; @end Use an Informal Protocol

Slide 38

Slide 38 text

#import @interface MyAppEmbeddingView : NSView { @private NSURL *_URL; void *_reserved1; void *_reserved2; void *_reserved3; } - (id)initWithFrame:(NSRect)frameRect URL:(NSURL)URL; - (unsigned)interfaceVersion; - (NSURL *)URL; - (void)setURL:(NSURL *)URL; @end Use an Abstract Base Class

Slide 39

Slide 39 text

#import "MyAppEmbeddingView.h" @implementation MyAppEmbeddingView - (id)initWithFrame:(NSRect)frameRect URL:(NSURL)URL { self = [self initWithFrame:frameRect]; if (self) { self.URL = URL; } return self; } - (unsigned)interfaceVersion { return 0; } ... Use an Abstract Base Class

Slide 40

Slide 40 text

- (void)drawRect:(NSRect)rect { NSEraseRect(rect); } - (NSURL *)URL { return _URL; } - (void)setURL:(NSURL *)URL { _URL = URL; } @end ... Use an Abstract Base Class

Slide 41

Slide 41 text

- (BOOL)plugInClassIsValid:(Class)plugInClass { if ([plugInClass conformsToProtocol:@protocol(MyAppBitmapGraphicsFiltering)]) { if ([plugInClass instancesRespondToSelector:@selector(interfaceVersion)] && [plugInClass instancesRespondToSelector:@selector(filteredImageRep)]) { return YES; } } return NO; } Formal Protocol Validating Plug-ins

Slide 42

Slide 42 text

- (BOOL)plugInClassIsValid:(Class)plugInClass { if ([plugInClass instancesRespondToSelector:@selector(interfaceVersion)] && [plugInClass instancesRespondToSelector:@selector(filteredImageRep)]) { return YES; } return NO; } Validating Plug-ins Informal Protocol

Slide 43

Slide 43 text

- (BOOL)plugInClassIsValid:(Class)plugInClass { if ([plugInClass isSubclassOfClass:[MyAppEmbeddingView class]]) { return YES; } return NO; } Abstract Base Class Validating Plug-ins

Slide 44

Slide 44 text

Sample Program

Slide 45

Slide 45 text

Summary - Plug-in is a loadable bundle. - Bundle is easily accessible through NSBundle. - There are three choices for plug-in architecture in Cocoa: - Formal protocol - Informal protocol - Abstract base class

Slide 46

Slide 46 text

References - Code Loading Programming Topics https://developer.apple.com/library/mac/ #documentation/Cocoa/Conceptual/LoadingCode/ LoadingCode.html https://developer.apple.com/library/mac/ #documentation/Cocoa/Reference/Foundation/Classes/ NSBundle_Class/Reference/Reference.html - NSBundle Class Reference

Slide 47

Slide 47 text

Thank you