Xcode9文件模板——第一篇

之前写过一篇关于iOS如何自定义文件/项目模板。很多同学看了之后并没有理解如何去实现一套自己的模板。为了方便大家理解,我在Xcode9基础之上重新梳理一遍,由浅入深,希望帮到需要的同学。

自定义模板的场景

如果使用自定义文件模板来创建文件相对于使用代码块(Code Snip)相比要重量级一些。

  • 代码块适合于一行或多行代码,比如@property (nonatomic, strong) <#type#> <#name#>;
  • 文件模板更加适合用一个文件的整个内容模板化,比如控制器经常有很多相同的内容、声明周期、注册通知、处理通知、配置视图等等。这样就不用没写一个控制器都还需要重复写代码。

代码块的使用读者自行搜索,很简单,值得一提的就是在需要自定义输入参数的时候是使用<#param#>占位。

下面正式开始介绍Xcode9中如何自定义文件模板。

解析Template

如果想要充分理解Template,建议先看看Customizing the file header comment and other text macros in Xcode 9

大致内容如下:

  • Xcode自带的文件头是完全是多余的,比如文件名、时间、作者等完全可以通过版本控制工具看到。
  • 在Xcode9中,允许开发使用文本宏的Plist自定义文件头。苹果官方链接——Customize text macros,其中所引用到的所有文本宏Text macros reference
    image
  • 可以根据作用范围,在不同的路径定义IDETemplateMacros.plist文件来限制自定义的范围。

Text Macros

在正式开始介绍前,先看看Text Macros 它定义了在创建文件的时候,文件名、创建时间等宏定义。也就是起到一个参数传递的作用

DATE
The current date.

DEFAULTTOOLCHAINSWIFTVERSION
The version of Swift used for the default toolchain.

FILEBASENAME
The name of the current file without any extension.

FILEBASENAMEASIDENTIFIER
The name of the current file encoded as a C identifier.

FILEHEADER
The text placed at the top of every new text file.

FILENAME
The full name of the current file.

FULLUSERNAME
The full name of the current macOS user.

NSHUMANREADABLECOPYRIGHTPLIST
The entry for the human readable copyright string in the Info.plist file of a macOS app target. The value of the macro must include the XML delimiters for the plist. For example, a valid value is:

<key>NSHumanReadableCopyright</key>

<string>Copyright © 2017 Apple, Inc. All rights reserved.</string>

   

Notice that the value includes a newline.

ORGANIZATIONNAME
The company name for the team used for the provisioning profile.

PACKAGENAME
The name of the package built by the current scheme.

PACKAGENAMEASIDENTIFIER
A C-identifier encoded version of the package name built by the current scheme.

PRODUCTNAME
The app name of the product built by the current scheme.

PROJECTNAME
The name of the current project.

RUNNINGMACOSVERSION
The version of macOS that is running Xcode.

TARGETNAME
The name of the current target.

TIME
The current time.

USERNAME
The login name for the current macOS user.

UUID
Returns a unique ID. The first time this macro is used, it generates the ID before returning it. You can use this macro to create multiple unique IDs by using a modifier. Each modifier returns an ID that is unique for that modifier. For example, the first time the UUID:firstPurpose modifier is used, the macro generates and returns a unique ID for that macro and modifier combination. Subsequent uses of the UUID:firstPurpose modifier return the same ID. Adding the UUID:secondPurpose modifier generates and returns a different ID that will be unique to UUID:secondPurpose, and different from the ID for UUID:firstPurpose.

WORKSPACENAME
The name of the current workspace. If there is only one project open, then the name of the current project.

YEAR
The current year as a four-digit number.

模板路径

一般情况下自定义的文件模板在~/Library/Developer/Xcode/Templates/File Templates/<Custom Group Name>。可以有多个自定义的文件模板组。

系统文件模板在~/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File Templates经过测试如果把自定义文件模板放在系统的文件模板目录下同样可以使用。

这里就以XLFile为自定义文件模板组。在该目录下创建XLCocoa Class.xctemplate文件夹,在XLCocoa Class.xctemplate就是包含具体的文件模板。

XLFile
    └── XLCocoa\ Class.xctemplate

最简单的文件模板

___FILEBASENAME___.h中的内容如下

//___FILEHEADER___

#import <Foundation/Foundation.h>

@interface ___FILEBASENAMEASIDENTIFIER___ : NSObject

@end

___FILEBASENAME___.m文件中的内容

//___FILEHEADER___

//Simple File Template
#import "___FILEBASENAME___.h"

@implementation ___FILEBASENAMEASIDENTIFIER___

@end
  • 注意上面的___FILEBASENAMEASIDENTIFIER______FILEBASENAME___就是Xcode提供的文本宏。

接下来是设置TemplateInfo.plist文件,其内容如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Kind</key>
    <string>Xcode.IDEFoundation.TextSubstitutionFileTemplateKind</string>
</dict>
</plist>
  • TemplateInfo.plist至少需要一个Kindkey,并且是字符串类型,一般情况下的值用Xcode.IDEFoundation.TextSubstitutionFileTemplateKind就可以了。虽然还有另外的值选择比如CoreData中的Xcode.IDECoreDataModeler.ManagedObjectTemplateKind,实际中其实根本用不到。

现在新建文件拖到最下面就会看到如下的结果:


15323344441516.jpg

创建之后的内容如下:

//
//  SimpleTemplate.h
//  NoteForCode
//
//  Created by xxx on 2018/7/23.
//  Copyright © 2018年 wesly. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface SimpleTemplate : NSObject

@end

.m

//
//  SimpleTemplate.m
//  NoteForCode
//
//  Created by xxxx on 2018/7/23.
//  Copyright © 2018年 wesly. All rights reserved.
//

//Simple File Template
#import "SimpleTemplate.h"

@implementation SimpleTemplate

@end

TemplateInfo.plist的Key

TemplateInfo.plist有很多的key来解释模板文件的结构或者内容。在最简单的模板文件中我们只使用了Kind这个Key。下面是我从Xcode自带的文件模板截取出来的plist文件内容。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Kind</key>
    <string>Xcode.IDEFoundation.TextSubstitutionFileTemplateKind</string>
    <key>Description</key>
    <string>A Cocoa class.</string>
    <key>Summary</key>
    <string>A Cocoa class</string>
    <key>SortOrder</key>
    <string>1</string>
    <key>DefaultCompletionName</key>
    <string>MyClass</string>
    <key>Platforms</key>
    <array>
        <string>com.apple.platform.macosx</string>
    </array>
    <key>Options</key>
    <array>
        <dict>
            <key>Identifier</key>
            <string>productName</string>
            <key>Required</key>
            <true/>
            <key>Name</key>
            <string>Class:</string>
            <key>Description</key>
            <string>The name of the class to create</string>
            <key>Type</key>
            <string>text</string>
            <key>NotPersisted</key>
            <true/>
        </dict>
        <dict>
            <key>Identifier</key>
            <string>cocoaSubclass</string>
            <key>Required</key>
            <true/>
            <key>Name</key>
            <string>Subclass of:</string>
            <key>Description</key>
            <string>What class to subclass in the new file</string>
            <key>Type</key>
            <string>class</string>
            <key>Default</key>
            <string>NSObject</string>
            <key>FallbackHeader</key>
            <string>#import &lt;Cocoa/Cocoa.h&gt;</string>
            <key>Values</key>
            <array>
                <string>NSObject</string>
                <string>NSDocument</string>
                <string>NSView</string>
                <string>NSViewController</string>
                <string>NSWindowController</string>
            </array>
        </dict>
        <dict>
            <key>Identifier</key>
            <string>XIB</string>
            <key>Name</key>
            <string>Also create XIB file for user interface</string>
            <key>Description</key>
            <string>Whether to create a XIB file with the same name</string>
            <key>Type</key>
            <string>checkbox</string>
            <key>RequiredOptions</key>
            <dict>
                <key>cocoaSubclass</key>
                <array>
                    <string>NSDocument</string>
                    <string>NSViewController</string>
                    <string>NSWindowController</string>
                </array>
            </dict>
            <key>Default</key>
            <string>true</string>
            <key>NotPersisted</key>
            <true/>
        </dict>
        <dict>
            <key>Identifier</key>
            <string>languageChoice</string>
            <key>Required</key>
            <true/>
            <key>Name</key>
            <string>Language:</string>
            <key>Description</key>
            <string>The implementation language</string>
            <key>Type</key>
            <string>popup</string>
            <key>Default</key>
            <string>Objective-C</string>
            <key>Values</key>
            <array>
                <string>Swift</string>
                <string>Objective-C</string>
            </array>
            <key>MainTemplateFiles</key>
            <dict>
                <key>Swift</key>
                <string>___FILEBASENAME___.swift</string>
                <key>Objective-C</key>
                <string>___FILEBASENAME___.m</string>
            </dict>
            <key>AllowedTypes</key>
            <dict>
                <key>Swift</key>
                <array>
                    <string>public.swift-source</string>
                </array>
                <key>Objective-C</key>
                <array>
                    <string>public.objective-c-source</string>
                    <string>public.objective-c-plus-plus-source</string>
                </array>
            </dict>
        </dict>
    </array>
</dict>
</plist>

下面对其中的某些key进行说明。其实完全可以根据Xcode自带的模板来猜测相应key的作用。

  • Description、Summary:用于提示。但是现在的Xcode没有使用这些两个key了。
  • DefaultCompletionName:默认文件名(字符串类型),用于显示在保存对话框中的默认文件名。
  • AllowedTypes:文件可用类型(字符串数据),用于确定哪些文件类型可以被保存,其值是一个UTI类型。关于UTI得详细内容可以看看这里System-Declared Uniform Type Identifiers。下面是一些常用的UTI
    • public.swift-source
    • public.c-header
    • public.c-source
    • public.c-plus-plus-source
    • public.objective-c-source
    • public.objective-c-plus-plus-source
  • Platforms(字符串数组):用于确定在某些平台可以使用该模板。该模板只会设置了该值的面板中显示。如果不设置那么每个平台下都会有该模板。值有如下几种:
    • com.apple.platform.macosx
    • com.apple.platform.iphoneos
    • com.apple.platform.watchos
    • com.apple.platform.appletvos

图标

最后还可以设置一下模板的图标,给人看起来逼格更高。具体来讲图标名称:

  • TemplateIcon.png (48 ⨯ 48)
  • TemplateIcon@3x.png (96 ⨯ 96)

比如用了这个图标:



最终在创建文件的时候就会长这样子


不同的形式

单类文件

多类文件

上面的例子是创建了单个的类文件。其实还可以一次创建多个文件,根据输入的文件名称。比如一次性根据输入的Model名,创建好对应头文件、View、ViewModel、Xib文件等
比如在刚才的目录下创建如下文件:


然后创建文件,输入Person得到如下这些文件

多模板

多模板其实就是在前面的基础上再建一层子文件夹。类似于:


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

推荐阅读更多精彩内容

  • 鉴于自己平时写demo比较多,每次都要新建一个项目,然后把一些常用的文件拖到项目中。稍微大一点的项目还需添加预编译...
    纸简书生阅读 4,681评论 11 30
  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,592评论 1 180
  • 这是篇连载追着看完的。贺一络,乔。追的时候迫不及待,追完不知道如果评价。女主活的很恣意,不受气,挺好。
    悠然逛南山阅读 204评论 0 0
  • 在烈日炎炎的夏季,强烈的太阳辐射到屋顶,养猪场的猪舍内不可避免的出现高温闷热情况,为了给猪营造良好的生长环境,需要...
    自然是美的表现阅读 673评论 0 0
  • 天蒙蒙亮,雄赳赳的鸡叫声就已经此起彼伏地响起来。男人轻咳了几声,悉悉嗦嗦穿戴整齐,轻轻攀着床沿下了床,生怕惊...
    蜉蝣哥阅读 280评论 0 0