Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Plug-in Architectures in Cocoa

Plug-in Architectures in Cocoa

第51回 Cocoa勉強会関西 『Cocoaのプラグインアーキテクチャ』 by @questbeat

Katsuma Tanaka

August 03, 2013
Tweet

More Decks by Katsuma Tanaka

Other Decks in Programming

Transcript

  1. Plug-in Architectures in Cocoa - Sample program is available on

    GitHub. https://github.com/questbeat/PlugInSample
  2. + (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
  3. 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/ ...
  4. - Application (.app) - Framework (.framework) - Preference Pane (.prefPane)

    - Screen Saver (.saver) - Xcode Plug-in (.xcplugin) Loadable Bundles are used in ...
  5. 1. Locate the bundle 2. Load the bundle’s executable code

    3. Instantiate an object of the principal class Loading Bundles Basic Three Steps
  6. NSBundle *appBundle = [NSBundle mainBundle]; NSString *plugInsPath = [appBundle builtInPlugInsPath];

    “/Applications/MyApp.app/Contents/PlugIns” Standard Location Locating Bundles
  7. 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
  8. 1. Locate the bundle 2. Load the bundle’s executable code

    3. Instantiate an object of the principal class Loading Bundles Basic Three Steps
  9. NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; if

    ([bundle load]) { NSLog(@"Bundle was successfully loaded."); } Loading Code
  10. Loading Code and Get Errors NSString *bundlePath = ...; NSBundle

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

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

    bundleWithPath:bundlePath]; [bundle load]; if ([bundle isLoaded] && [bundle unload]) { NSLog(@"Bundle was successfully unloaded."); }
  13. 1. Locate the bundle 2. Load the bundle’s executable code

    3. Instantiate an object of the principal class Loading Bundles Basic Three Steps
  14. NSString *bundlePath = ...; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; Class

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

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

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

    = [NSBundle bundleWithPath:bundlePath]; Class principalClass = [bundle principalClass]; id instance<MyPlugIn> = [[principalClass alloc] init]; [instance greet]; @protocol MyPlugIn <NSObject> - (void)greet; @end ... - Host application should provide an interface.
  18. - Use a formal protocol - Use an informal protocol

    - Use an abstract base class Creating Plug-in Architectures Three Ways to Provide Interface
  19. #import <Cocoa/Cocoa.h> @protocol MyAppBitmapGraphicsFiltering <NSObject> @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
  20. #import <Cocoa/Cocoa.h> @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
  21. #import <Cocoa/Cocoa.h> @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
  22. #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
  23. - (void)drawRect:(NSRect)rect { NSEraseRect(rect); } - (NSURL *)URL { return

    _URL; } - (void)setURL:(NSURL *)URL { _URL = URL; } @end ... Use an Abstract Base Class
  24. - (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
  25. 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