之前写过一篇关于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。
- 可以根据作用范围,在不同的路径定义
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至少需要一个
Kind
key,并且是字符串类型,一般情况下的值用Xcode.IDEFoundation.TextSubstitutionFileTemplateKind
就可以了。虽然还有另外的值选择比如CoreData中的Xcode.IDECoreDataModeler.ManagedObjectTemplateKind
,实际中其实根本用不到。
现在新建文件拖到最下面就会看到如下的结果:
创建之后的内容如下:
//
// 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 <Cocoa/Cocoa.h></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得到如下这些文件
多模板
多模板其实就是在前面的基础上再建一层子文件夹。类似于: