从零开始 编写一个 XCode Source Extension

从零开始 编写一个 XCode Source Extension

先看最终效果图:

demo-tuya.gif

简书图片大小限制,不清楚可以点击查看原图

就像你看到的,这个插件包括两个模块,一个是设置页面我们使用Mac APP 完成(不需要Mac开发基础),另一部分是Xcode source Editor插件。这两个模块我们均用swift进行开发。

第一步:开始创建项目,创建一个Mac APP

001.png

然后为项目取名为DKJSONExtension开发语言为swift使用storyboard。
这时候我们就创建好了主项目了,运行以后是一个空白的窗口。我们先实现设置功能的页面,在进行xcode source editer插件的开发。首先我们打开storyboard
修改一下主程序窗口的标题
002.png

接着在viewcontroller中拖出设置页面,为了方便布局我使用两个stackview去横向排列3个选项
003.png

设置界面完成以后就可以进行事件处理了,设置页面主要是为了显示和修改当前的模型类型和命名规则。这里我们使用UserDefaults存储数据
为了和storyboard中的控件交互这里我们使用两种常用的方式获取控件第一种使用@IBOutlet 引用,第二章通过设置tag取subview
004.png

我们要实现单选按钮需要在按钮的点击事件中自己处理.为同一行的按钮做一个事件处理,并且存储当前的值

@IBAction func modeTypeChange(_ sender: NSButton) {
        [codeableBtn,handyJSONBtn,swiftyJSONBtn].forEach {
            $0?.state = sender.title == $0?.title ? .on : .off
        }
        UserDefaults.modelType = sender.title 
    }

    @IBAction func modeNameStyleChange(_ sender: NSButton) {
        for i in 0..<3 {
            let btn = self.view.viewWithTag(100 + i) as? NSButton
            btn?.state = sender.title == btn?.title ?.on:.off
        }
        UserDefaults.modelStyle = sender.title
    }

这样我们做好了事件处理和数据存储,这里我们存储数据需要和extension共享为此我们使用APP group进行userdefault 存取。如果使用UserDefaults.standard进行存取我们将无法在extension中获取到存取的值。
具体步骤如下:
到开发者中心创建APP group

005.png

选择 APPgroup 填写名称和标识
创建完成打开
如图复制identifier
006.png

在项目中添加APPgroup,天上刚才复制的identifier
007.png

这时我们就能按group存取数据了,这些数据将会被同一group的应用共享。我们为userdefault 添加扩展.注意要使用groupID创建userdefault


extension UserDefaults{
    static var grouped:UserDefaults{
        return UserDefaults(suiteName: "group.com.dkjone.DKJsonExtension") ?? standard
    }
    /// 模型类型
    static var  modelType: String {
        get { return grouped.string(forKey: #function) ?? "HandyJSON" }
        set { grouped.setValue(newValue, forKey: #function) }
    }
    // 命名规则
    static var modelStyle: String {
        get { return grouped.string(forKey: #function) ?? "驼峰法" }
        set { grouped.setValue(newValue, forKey: #function) }
    }
}

最后一步,设置页面加载时,显示当前存储的设置数据

 override func viewDidLoad() {
        super.viewDidLoad()
        [codeableBtn,handyJSONBtn,swiftyJSONBtn].forEach {
            $0?.state = UserDefaults.modelType == $0?.title ? .on : .off
        }
        for i in 0..<3 {
            let btn = self.view.viewWithTag(100 + i) as? NSButton
            btn?.state = UserDefaults.modelStyle == btn?.title ?.on:.off
        }
    }

到此我们完成了设置应用的全部功能
接下来我们实现source editor extension部分
这部分我们首先创建extension如图:


008.png

创建完成以后我们项目中多呢以下文件

-- JSONExtension
---- SourceEditorExtension.swift
---- SourceEditorCommand.swift
---- Info.plist

这几个文件是我们编写extension的关键。我们的 项目 target也会多一个选项,我们使用如下选项运行就会出现一个灰色的xcode 可以让我们调试,用灰色xcode 打开文件便会在editor菜单中出现我们添加的插件名称。


009.png

如果你出现报错

dyld: Library not loaded: @rpath/XcodeKit.framework/Versions/A/XcodeKit
  Referenced from: 

说明库连接有问题,找到这个库链接删除后按图添加即可


010.png

出现以下错误是账号问题需要到xcoed->preferencces->account 中,删除之前的账号,重新登陆账号就可以了

"Your session has expired. Please log in."

如果成功运行会出现灰色Xcode

011.png

012.png

用灰色的xcode打开文件或者项目在editor 菜单最下面就会有我们新建的插件了,在此之前我们修改下插件的菜单名称
可以在info.plist中修改也可以在代码中自定义,我们直接修改代码中的返回值,修改返回值优先级高于info.plist,
修改SourceEditorExtension.swift文件为以下内容

class SourceEditorExtension: NSObject, XCSourceEditorExtension {
    var commandDefinitions: [[XCSourceEditorCommandDefinitionKey: Any]] {
        return [[.nameKey: "将JSON文件转化模型", .identifierKey: "paraseFile", .classNameKey: SourceEditorCommand.className()],
                [.nameKey: "从剪贴板获取JSON转化为模型", .identifierKey: "parasePasteboard", .classNameKey: SourceEditorCommand.className()]]
    }
}

要注意的地方:

nameKey: 菜单的名称
identifierKey: 菜单的标识,可以在处理类中用于判断点击了哪一个菜单
classNameKey: 处理此菜单的类名,这里我们直接使用className()获取,如果你直接写字符串记得在类名前加上模块名。

到此我们直接运行后就是以下的效果了


013.png

最后我们需要处理如何生成代码了:
打开处理类

class SourceEditorCommand: NSObject, XCSourceEditorCommand {
// 点击菜单时调用的方法
 func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void) {
 }
}

这里方法的参数invocation就是当前编辑的上下文可以获取当前菜单的ID和当前文档类型,文档内容,所在行号等等。
如果需要修改文件内容直接修改invocation.buffer.lines就可以。这是个[Sting]类型的可变数组,可以在后面直接加,也可以替换某一行内容。
为了获取主程序保存的配置我们也要为这个extension配置相同的APP group,方法同上。也使用相同的userdefault就可以取出主程序存储的配置了。

这里具体解析json的逻辑就不仔细说明了,有兴趣的可查看完整的源码github或者码云

最后一步发布我们制作的插件。选择主项目,然后xcode->product->archive 接下来和发布IOS应用一样可以选择开发者签名或者提交到APP store 或者内部测试。导出内容如图:

014.png

如果想制作图中的dmg文件,打开磁盘工具->文件->新建映像->基于文件夹新建->选择插件所在文件夹即可。使用时将其中的APP文件(上图圈中的)拖入application文件夹后打开。记得一定要打开一次,然后到系统偏好设置->扩展中勾选这个插件就可以了
015.png

这样我们就可以正常使用这个extension了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容