Xcode7 plug-in development: from Development to the Devil Island

Source: Internet
Author: User

Xcode is powerful, but some are closed, and the official does not provide documentation for Xcode plugin development. Meow-God's tutorial is more complete, but also suitable for getting started. The tutorial in this article is just as a summary of my development FKConsole process and will not be comprehensive.

FKConsoleI developed a plugin for displaying Chinese in the Xcode console, very small, very simple. The plug-in was originally developed because a friend had this need, and did not find the appropriate plugin. If you do not use a plugin, you will need to embed the file in the project, he is not happy. So FKConsole in the design will only modify the Xcode console text display, will never go to modify your files, this point you can rest assured.

Template

Because now there are a lot of people do Xcode plug-in development, so plug-in template This kind of thing also came into being.

Xcode-plugin-template is a basic template for Xcode plug-in development and can be installed directly with Alcatraz to support Xcode 6+.

After the installation is complete, a Xcode plugin option will appear when the project is created, which is the plugin project template in Xcode.

The template will generate NSObject_Extension the same two files as your project name (. M).

NSObject_Extension.mThe +(无效)pluginDidLoad:(*一个NSBundle)插件 method is also the entrance of the entire plug-in.

In general, we want our plug-ins to survive the entire Xcode lifecycle, so it's generally a singleton, which is reflected in another file.

Add button

This blog post is a record FKConsole of the development process, naturally with this example.

After Xcode starts, the notification will be sent NSApplicationDidFinishLaunchingNotification , the template has been listened to, we have to add an option on the Header toolbar after the program starts to set the plug-in FKConsole FKConsole switch.

There are some differences between Mac software development and iOS development, and it uses the AppKit UI library instead UIKit , so it may feel a bit awkward.

NSApp表示In the [NSApp表示mainMenu] method can get to the head of the main button, which will contain very much NSMenuItem , we will be in the Xcode Window option before inserting an Plugins option (refer to the practice of breaking the blog), and then add an option in this option FKConsole . (Adding an Plugins option is because some plug-ins will be added to, Edit some will be added to, View I have Window not found the option for half a day, it is better to directly build an Plugins option, the user can know at a glance where the plug-in. )

Nsmenu * MAINMENU = [Nsapp means MAINMENU]; if (! MAINMENU) {return;} Nsmenuitem * Pluginsmenuitem = [MAINMENU itemwithtitle:@"Plugin"]; if (! Pluginsmenuitem) {pluginsmenuitem = [[Nsmenuitem alloc] initialization]; Pluginsmenuitem. title Pseudo = @Plug-in; Pluginsmenuitem. submenu = [[Nsmenu page Header] Initwithtitle:pluginsmenuitem. Title Pseudo]; Nsinteger's Windowindex = [MAINMENU indexofitemwithtitle:@ "window"]  Submenuitem .title pseudo = @  SubMenuItem .action = @selector (Togglemenu:) .boolvalue nsonstate:nsoffstate ; [Pluginsmenuitem .submenu Additem:submenuitem]   

We need a state to represent the plug-in switch, just NSMenuItem on one state can represent the state, and just display the effect is good, we use it.

Layer

When the button is added, we now need to get an instance of the console. Unfortunately, Apple didn't give the document.

I'm sorry, I didn't find a layer view tool similar to reveal on Mac software development. Meow recommended one, the NSView Dumping类 code is as follows:

From the http://onevcat.com/2013/02/xcode-plugin/.

- (Invalid) Dumpwithindent:(NSString *) Indent {The NSString * class = Nsstringfromclass ([Individual business category]);NSString * Information = @“” ;if ([From Respondstoselector:@selector (title)]) {The NSString * title = [From Performselector:@selector (title)];if (title =!0 && [title length]>0) {Information = [information stringbyappendingformat:@"Title =%@", Title]}}if ([From Respondstoselector:@selector (StringValue)]) {The NSString * string = [From Performselector:@selector (StringValue);if (string!) =0 && [string length]>0) {Information = [information stringbyappendingformat:@"StringValue =%@", string]; } }NSString * hint = [ self tool tip];  if (hint =!  0 && [hint length]> 0) {information = [information stringbyappendingformat:@] Span class= "hljs-string" > "hint =%@", prompt]. }  NSLog (@  "%@%@%@", Indentation, class information);  if ([ from child view] Count]> 0) { NSString * Subindent = [ NSString stringwithformat:@ "%@%@ ", indent, ([indent length]/2)%2 = = 0? @  "|" : @  ":"];  from Child view]) {[Child view dumpwithindent:subindent];}}  

The effect is similar to the following:

In addition to this practice, I use chisel, which is a LLDB command-line auxiliary debugging tool for Facebook open source. It contains a pviews command that can be directly recursive to print the entire key窗口 , the effect is as follows:

Import Private API

We found a class in it, IDEConsoleTextView which is the only one in all the views seen in, and Console we look at its frame and make sure the console is it.

Apple did not give this IDEConsoleTextView to put AppKit in, it is a private class, we now want to modify it, then we need to get its header file.

GitHub has a lot to dump out of Xcode in the header, you can see: Https://github.com/search?utf8=%E2%9C%93&q=xcode+header. We found it in the header IDEConsoleTextView.h , in IDEKit the middle.

As you can see in the header file, IDEConsoleTextView it is inherited from DVTCompletingTextView DVTTextView NSTextView . The content of the NSTextView saved text is used NSTextStorage *textStorage , so we want to modify IDEConsoleTextView it textStorage . But we NSTextStorage did not find the specific text in the header file to save the properties, then we go to find.

Functional development

We loop through all the 的NSView , find IDEConsoleTextView , we look at its information:

We did not find its textStorage properties and we tried to hit it in the console:

It has this attribute, just not seen in the debug area.

textStorageThere are two methods in the representation, namely:

//Send-process insideBefore you edit a fixed property.Delegates can change text or attributes.-(invalid) Textstorage:(Nstextstorage *) Textstorage willprocessediting:(nstextstorageeditactions) Editedmask Range:(nsrange) editedrange changeinlength:  ( nsinteger) increment ns_available  (10 _ 11,7 _0);//send correct-processediting notification layout Manager before,  delegates can change properties. - (invalid) Textstorage:  (nstextstorage *) textstorage didprocessediting:  ( nstextstorageeditactions) Editedmask range:  (nsrange" Editedrange changeinlength:  ( Nsinteger) Increment ns_available  (10 _11, 7 _0)                 

textStorageAfter the character or description is modified, this proxy is triggered, so we implement this proxy method:

自.fkConsoleTextView .textStorage .delegate = 自我 ;- (无效)textStorage:(NSTextStorage *)textStorage willProcessEditing:(NSTextStorageEditActions)editedMask 范围:( NSRange)editedRange changeInLength :( NSInteger的)三角洲{}

OK, this time we found, IDEConsoleTextView there is a _contents property, this is an inherited from NSMutableAttributedString the class, the inside of the mutableString save text, mutableAttributes Save the description of the text. This is the attribute we need to modify mutableString .

We can get to the property using the proxy method, valueForKeyPath: mutableString So now we're going to convert it.

FKConsoleis used to adjust the display of the console in Chinese, in order to change the Unicode encoding similar to this () \U6d4b\U8bd5" to "测试啊" the normal display of ().

I'm in the calculator. Find a workaround code similar to this:

From http://stackoverflow.com/questions/13240620/uilabel-text-with-unicode-nsstring

- (的NSString *)stringByReplaceUnicode :( 的NSString *)的字符串{    的NSMutableString * convertedString = [字符串mutableCopy]    [convertedString replaceOccurrencesOfString:@ “\\ U” withString:@ “\\ U”选项:0范围:NSMakeRange(0,convertedString 。长度)]; CFStringRef变换= CFSTR( “ 任意六角/ Java”的); CFStringTransform((__桥CFMutableStringRef)convertedString,NULL,变换,YES); 返回 convertedString;}

We use 的setValue:forKeyPath: the way to modify mutableString properties.

Run, yes, but there are some problems.

    1. If you use Findview way to find IDEConsoleTextView , and then to set up the agent, then, when to go to Findview, if this time and new open a few pages, this is not sure.
    2. The modified text length is not the same as the original, even if the modification editedRange is not used. In this case, if you enter text or debug commands on the console, it may crash, the main reason for the crash is IDEConsoleTextView _startLocationOfLastLine to use and _lastRemovableTextLocation these two properties to control the text start position and delete position, after the setting mutableString , due to the length of the string value may occur out of bounds, and NSTextStorageThe agent is not getting hold of it IDEConsoleTextView .
Monitoring notifications

For the first question, we can use the notification method to solve.

Refer to the cat's blog, you can listen to all the notifications, and then go to find which is what you need.

- (ID)的init {      如果(自 = [ 超级初始化]){        [ NSNotificationCenter defaultCenter]的addObserver:自我 选择:@选择(的NotificationListener :) 名称:无目标:零 ] } 回归 自我 ;} - (无效)的NotificationListener :( NSNotification *)的NotI { 的NSLog(@ “通知:%@”,[NotI位名]); } 

We only need to listen NSTextDidChangeNotification on the line, and then in the method to judge, then set up the agent.

- (无效)textStorageDidChange:(NSNotification *)的NotI{    如果([NotI位。对象 isKindOfClass:NSClassFromString(@“IDEConsoleTextView”)&&        ((IDEConsoleTextView *)的NotI。对象).textStorage。委派!=个体经营) { (。(IDEConsoleTextView *)的NotI 对象)。.textStorage 委托 =自我; }}

This solves the first problem.

Adding cross-blending of methods and means

If you are interested, you can refer to my other blog: objective-c runs common usage, which explains common run-time usage in an illustrative way.

For the second question, the approach I'm using is to modify IDEConsoleTextView the _startLocationOfLastLine attributes and properties at the right time _lastRemovableTextLocation . After experimentation, the methods of collapse are mainly IDEConsoleTextView these methods:

(void) insertText: (id)arg1; - (void) insertNewline: (id)arg1; - (void)clearConsoleItems; - ( BOOL ) shouldChangeTextInRanges: (id)arg1 replacementStrings: (id)arg2;

I IDEConsoleTextView have added the following methods to the runtime:

(void) fk_insertText: (id)arg1; - (void) fk_insertNewline: (id)arg1; - (void)fk_clearConsoleItems; - ( BOOL ) fk_shouldChangeTextInRanges: (id)arg1 replacementStrings: (id)arg2;

After that, use Jrswizzle to swap, blending methods, similar to this:

-(invalid) Addmethodwithnewmethod:(SEL) Newmethod Originmethod:(SEL) originmethod{Method Targetmethod = Class_getinstancemethod(Nsclassfromstring (@"Ideconsoletextview"), Newmethod); Method Consolemethod = Class_getinstancemethod (console  (! Target  ( "Ideconsoletextview"), newmethod,consoleimp,method_gettypeencoding  (Origin  method" {Nserror * ERROR; [Nsclassfromstring  (@  "Ideconsoletextview") jr_ Swizzlemethod:newmethod Withmethod:originmethod Error: Error] nslog  (@  "error =%@", error);} }} 

In the first fk_ series of methods, a pair of checks has been added IDEConsoleTextView :

-(invalid) Fk_checktextview:(Ideconsoletextview *) If the textview{(TextView the. TextStore. Length <[textView valuesForkeypath: GStartlocationoflastlinekey] LongLongvalue]) {[setvalue:@ in TextView(Text view of the. Text store.) length) Forkeypath:kstartlocationoflastlinekey]; If (text is viewed by the. Text store. Length <[text view value Forkeypath: Gram Lastremovabletextlocationkey] Long Longvalue]) {[ TextView setvalue:@ (Text view of the. Text store.) length) Forkeypath:klastremovabletextlocationkey]; }}- (invalid) Fk_inserttext:(ID) arg1{[self-employed Fk_checktextview: (Ideconsoletextview *) self-employed]; [self-employed fk_inserttext:arg1];}                

In this way, the second problem is solved.

OK, FKConsole This is the basic development done.

Alcatraz Island

As mentioned above, Alcatraz it is an open source Xcode package Manager. In fact, Alcatraz it has become the most important tool for our current installation of the Xcode plugin.

Now we will FKConsole submit it to the 恶魔 top.

Fill

Alcatraz-packages is a Alcatraz list of package warehouses that packages.json have all the Alcatraz supported plugins, color themes, and templates saved.

We fork the alcatraz-packages into our code warehouse. After that, follow this format to add our project.

 {       名 ”:“FKConsole” ,       “ url可 ”:“https://github.com/Forkong/FKConsole” , “ 说明 ”:“FKConsole是一个插件的Xcode调整控制台显示器(对中国)。” , “ 屏幕 ”:“https://raw.githubusercontent.com/Forkong/FKConsole/master/Screenshots/demo.gif” }
Respec

rspecIt's a test framework written in Ruby, where the author writes a script to test if you've modified it to be packages.json legitimate. Cut directly into the alcatraz-packages directory and run the rspec command. If you pass, you will see this:

RSpec的Gemstones that use rubies can be mounted directly.

Pull

After verifying that there is no problem, our 拉入请求 submission appears on the Demon's package 拉请求 :

https://github.com/alcatraz/alcatraz-packages/pull/461

(You must not like me, did not see clear, directly added to the last side.) It is three categories, be sure to see clearly, to add to the category of plug-ins. )

http://blog.csdn.net/djl4104804/article/details/51075722

Xcode7 plug-in development: from Development to the Devil Island

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.