📜 ⬆️ ⬇️

Writing your Xcode plugin

Often there are situations when you want to expand the functionality of the used IDE. Lucky if the developer provided the tools and documentation to do this. Unfortunately, this is not the case with Xcode. Documenting capabilities has stopped at Xcode 3.0 , so no one can guarantee that the plugin you write will work in the next version.

Note: The plugin ColorSense-for-Xcode was taken as the basis for writing this topic.

As I said, officially, Xcode does not provide a public API for writing plugins. When the application starts, Xcode scans the plugins folder (~ / Library / Application Support / Developer / Shared / Xcode / Plug-ins) and loads the found (.xcplugin) files.
')
In fact, a simple plugin is written in a few hours, which you will see later.

Create a new Xcode project

Plugin - just an OS X bundle, we will create a new project with the type of 'bundle'.



When creating a project, you need to make sure that ARC is turned off, since Xcode is running under the garbage collector, the same applies to the plugin.

Open the target of the plugin and set the following settings:





Configuring Build Settings

Go to build settings and set the following settings:


Also, add some user-definded settings:


We have indicated where our plugin should be installed after assembly. Important: debugging plug-ins is all sad, and you will have to restart Xcode after each build.

Writing a plugin

Let's create a new class and name it by the name that was specified in the Principal class setting. When Xcode loads a plugin, the + (void) pluginDidLoad: (NSBundle *) plugin method will be invoked, in which you can make the initial configuration of the plugin (as a rule, the plugin is a singleton).

+ (void) pluginDidLoad: (NSBundle*) plugin { static id sharedPlugin = nil; static dispatch_once_t once; dispatch_once(&once, ^{ sharedPlugin = [[self alloc] init]; }); } - (id)init { if (self = [super init]) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidFinishLaunching:) name:NSApplicationDidFinishLaunchingNotification object:nil]; } return self; } 

in the applicationDidFinishLaunching handler: we can directly execute the logic of the plugin. In our case, we will subscribe to notifications for changing the cursor position in the editor, and also add a new item in the Edit menu.

 - (void)applicationDidFinishLaunching:(NSNotification*)notification { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(selectionDidChange:) name:NSTextViewDidChangeSelectionNotification object:nil]; NSMenuItem* editMenuItem = [[NSApp mainMenu] itemWithTitle:@"Edit"]; if (editMenuItem) { [[editMenuItem submenu] addItem:[NSMenuItem separatorItem]]; NSMenuItem* newMenuItem = [[NSMenuItem alloc] initWithTitle:@"Show autoresizing masks" action:@selector(toggleMasks:) keyEquivalent:@"m"]; [newMenuItem setTarget:self]; [newMenuItem setKeyEquivalentModifierMask:NSAlternateKeyMask]; [[editMenuItem submenu] addItem:newMenuItem]; [newMenuItem release]; } } 

For example, we will output a line by changing the cursor position:

 - (void)selectionDidChange:(NSNotification*)notification { if ([[notification object] isKindOfClass:[NSTextView class]]) { NSTextView* textView = (NSTextView *)[notification object]; if (![[NSUserDefaults standardUserDefaults] boolForKey:kDLShowSizingsPreferencesKey]) { return; } NSArray* selectedRanges = [textView selectedRanges]; if (selectedRanges.count >= 1) { NSRange selectedRange = [[selectedRanges objectAtIndex:0] rangeValue]; NSString *text = textView.textStorage.string; NSRange lineRange = [text lineRangeForRange:selectedRange]; NSString *line = [text substringWithRange:lineRange]; } NSAlert *alert = [[[NSAlert alloc] init] autorelease]; [alert setMessageText:line]; [alert runModal]; } 

The simplest, nothing useful plugin is ready!

Remarks

As I said above, debugging of the plugin is possible only by restarting Xcode (when I wrote the plugin, I put NSAlert everywhere and output the necessary information). Due to the fact that there is no documentation as such, in order to find the desired view in the hierarchy, or to find out which notifications are being sent, it is necessary to perform the same trick: output the information either to a log or to an alert. If the plugin crashes, Xcode will not start, and the plugin must be removed from '~ / Library / Application Support / Developer / Shared / Xcode / Plug-ins'.

More functional example

The prerequisite for writing this article was a small plugin written by me that displays the auto-sizing masks for UIView:



Source code on github .

Thanks for attention!

Source: https://habr.com/ru/post/163795/


All Articles