详解苹果提供的UTI(统一类型标识符)
前言
最近项目中有个需求,在iOS设备上使用iOS系统提供的内容分享功能,从第三方App应用直接分享实体内容到我们的应用中。其大概的原理是这样的,首先为我们的iOS应用注册可以打开document types(文档类型),然后在第三方应用中,如果它们使用了iOS提供的分享功能,那么就会看到我们的应用程序,点击进行分享。
而关于需求的设计和实现的具体思路,我会在下一篇博客中详细讲解。这篇文章是来讲一下在iOS系统中为了更好的进行类型标识,而提供的一套共用的规范,也就是标题中提到的“Uniform Type Identifier(UTI)”,我把它翻译成“统一类型标识符”,下面统一简称为“UTI”。
官方教程
网上关于UTI的使用教程少之又少,所以我只是参考了苹果官方文档提供的讲解,这篇博客权当是我对于官方文档的一个理解吧!!自认为很重要的部分,我会贴出来官方文档原文,以便于大家学习理解,不至于被我的歪词所误导,同时也推荐大家从开发者中心上搜一些文档来看,这里推荐几篇:
1.Cocoa Core Competencies -- Uniform Type Identifier
这篇文档提供了一个视图来说明UTI是什么,怎么工作和被谁使用,是个非常好的新手指南。
2.Uniform Type Identifier介绍和使用
这篇文档详细得描述了UTI的基础概念和属性,还有它们的使用方法,内容非常丰富,本文主要参考的就是这篇
3.System-Declared Uniform Type Identifiers
这篇文档提供了在OS X系统中定义的一个UTI的列表,我们可以查看每一种官方提供的UTI的定义和涵义。
4.UTType Reference
这篇文档提供了对UTI字符串直接操作的函数方法
5.一步一步为iOS应用添加自定义的document type和新的UTI
顾名思义,这篇文档,讲解的是如何在iOS应用中导入新的UTI和添加自定义的document type。
为什么会有UTI
为什么会有UTI,打个比方,它就像是如今世界各国作为官方语言统讲得英文。为什么这么比喻呢,因为中国人讲母语汉语,法国人讲母语法语,但是如果一个中国人到了法国,而又不懂法语,碰到的法国人不懂汉语,那么他们如何交流沟通呢,这就是英文的用武之地了。而相对而言,苹果操作系统相当于整个世界,各个不同的程序或者服务相当于各个国家,俩个不同的程序想要互通交流,就比如互相发送文件,可是一个使用文件扩展名,一个使用MIME类型,俩者的数据类型不同,无法解析,都互相不认识,那么怎么交流沟通呢?在这样的情景下,UTI就有了用武之地啦,它就充当的是现实世界的英文这个角色。
UTI概念
Uniform type identifiers(UTIs)提供了在整个系统里面标识数据的一个统一的方式,比如documents(文档)、pasteboard data(剪贴板数据)和bundles(包)。
而具体到UTI的定义,官方文档是这么说的:“A uniform type identifier is a string that uniquely identifies a class of entities considered to have a ‘type’.”。其大概意思是说,一个统一类型标识符是一个唯一标识一种拥有"类型"属性实体的字符串。而且,针对这个“type”,官方文档还给我们提出了例子解释,对于一个文件或者是字节流来说,“type”指的的数据类型;而对于packages和bundles来说,“type”指的就是它们内部的目录层级结构。
UTI用途
大多数情况下,一个UTI提供的是系统中所有程序和服务都能够识别并且依赖的一个唯一的标识,这么讲可能有些太抽象,我们使用一下官方文档中给出的例子,比如一个JPEG类型的图片文件,在不同的环境下,可以有下面几种不同的标识方法:
- ‘JPEG’, OSType表示,
- '.jpg', 文件扩展名
- '.jpeg', 文件扩展名
- 'image/jpeg', MIME(多用途互联网邮件扩展类型)中的一种类型
而UTI则是用‘public.jpeg’这个字符串标识,完全代替了这些不一致的标签,这个字符串和其他任何一个旧标签都是完全兼容的,而且他们之间可以相互转换。由于UTI可以标识任何类型的实体,所以他们相对于旧标签来说灵活性更强了;使用UTI我们可以表示下面这些实体:
- Pasteboard data
- Folders (directories)
- Bundles
- Frameworks
- Streaming data
- Aliases and symbolic links
用法
1. 简介
Apple给我们提供了在iOS和Mac应用中通用的UTI字符串集合,比如,'public.data'、'public.item'、'public.image'等,这些我们都可以在官方文档中进行查阅他们的涵义。除此之外,我们也可以在应用程序中自定义自己的UTI字符串,比如我们可以定义一个标识特殊文档格式的UTI字符串叫'cc.icoc.shaobozheng',如果其他的应用程序想要支持我们这种格式的文档,他们就可以用'cc.icoc.shaobozheng'来标识我们的文档。
2. 字符集
看到我们上边的举例了,那么我们来说一下定义UTI字符串时所用到的字符集。通常一个UTI字符串是一个包含ASCII字符的Unicode字符串,同时也可以加入罗马字母和阿拉伯数字,如(A-Z),(a-z),(0-9)还有点号(".")和连接符("-")。而任何包含非法字符的字符串,如包含下划线'_',都无法作为UTI来标识内容,而且Apple不会有任何错误反馈。
3. 语法
就像我上面的例子一样,UTI的定义和我们开发iOS程序时填写organization时一样,采取的是反域名规则。如下面这几种:
- com.apple.quicktime-movie
- com.mycompany.myapp.myspecialfiletype
- public.html
- com.apple.pict
- public.jpeg
而UTI中的域名,如‘com’、‘public’这些,仅仅是用来表示这个UTI字符串在域名层级中的位置,它不会影响任何相似类型的分组。比如,‘public’域名就是大部分应用程序用来标识标准类型的,而目前仅仅只有Apple可以创建‘public’域名的UTI。
另外,我们可能会碰到的是一种‘dyn’域名,是动态域名,意思就是我们使用中,不会指定这种类型的UTI为某一个字符串,然后系统运行过程中,会自动识别帮我们处理。针对这种动态标识,我们是看不到的,但是我们可以通过UTI字符串的操作方式转换成我们的常用类型,比如OSType,MIME类型等。
官方文档中,对动态标识有个比喻:“You can think of a dynamic identifier as a UTI-compatible wrapper around an otherwise unknown filename extension, MIME type, OSType, and so on”,大概意思就是我们可以把这种动态标识当做是针对普通类型进行了重写包装的,而且是兼容UTI的一种标识。
最后一种就是可以自定义的域名,代表性的就是‘com’域名,Apple也给我们提供了一些他们定义的'com'域名的UTI。
顺应性
UTI相对于其他那些旧标签的一个关键优势就是在于,它可以在一个顺应结构中声明。而用我们面向对象的方式说,UTI就是可继承的,而且是多继承方式。先上图:

而且自定义的UTI必须指定UTExportedTypeDeclarations或者UTImportedTypeDeclarations,这样如果是你定义的UTI,第三方应用程序和服务都可以使用,而如果是其他人定义的UTI,那么加入到你的项目中后,你就可以看到这种类型的数据。
而官方文档中有一句话:“If both imported and exported declarations for a UTI exist, the exported declaration takes precedence over imported one”,我的理解是,被导出去得UTI声明所有人是定义UTI的发布者,也就是你的项目,而被导入的UTI声明所有人就是其他人,所以就是说如果对于一个UTI来说,俩种类型的声明都存在,则自己定义的UTI声明优先使用。
2. 自定义UTI的建议
- 你的UTI字符串必须是唯一的。以‘com.’开头的反域名命名方式是确保唯一性的简单有效的方法。
- 如果你的代码依赖于第三方App,UTI类型或许不会在系统中展示,你应该在bundle中声明为导入类型
- 如果你的UTI类型是一个或多个已存在的UTI类型的子类,则必须给它添加顺应性,让它继承于某个父类。最好是继承于‘public’类型。