前言
最近項目中有個需求,在iOS設備上使用iOS系統提供的內容分享功能账忘,從第三方App應用直接分享實體內容到我們的應用中卫旱。其大概的原理是這樣的,首先為我們的iOS應用注冊可以打開document types(文檔類型)骨饿,然后在第三方應用中亏栈,如果它們使用了iOS提供的分享功能,那么就會看到我們的應用程序样刷,點擊進行分享仑扑。
而關于需求的設計和實現的具體思路,我會在下一篇博客中詳細講解置鼻。這篇文章是來講一下在iOS系統中為了更好的進行類型標識镇饮,而提供的一套共用的規(guī)范,也就是標題中提到的“Uniform Type Identifier(UTI)”箕母,我把它翻譯成“統一類型標識符”,下面統一簡稱為“UTI”储藐。
官方教程
網上關于UTI的使用教程少之又少俱济,所以我只是參考了蘋果官方文檔提供的講解,這篇博客權當是我對于官方文檔的一個理解吧8撇蛛碌!自認為很重要的部分,我會貼出來官方文檔原文辖源,以便于大家學習理解蔚携,不至于被我的歪詞所誤導,同時也推薦大家從開發(fā)者中心上搜一些文檔來看克饶,這里推薦幾篇:
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丽涩,打個比方棺滞,它就像是如今世界各國作為官方語言統講得英文。為什么這么比喻呢矢渊,因為中國人講母語漢語继准,法國人講母語法語,但是如果一個中國人到了法國矮男,而又不懂法語移必,碰到的法國人不懂漢語,那么他們如何交流溝通呢毡鉴,這就是英文的用武之地了崔泵。而相對而言,蘋果操作系統相當于整個世界猪瞬,各個不同的程序或者服務相當于各個國家憎瘸,倆個不同的程序想要互通交流,就比如互相發(fā)送文件陈瘦,可是一個使用文件擴展名幌甘,一個使用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”,官方文檔還給我們提出了例子解釋眯勾,對于一個文件或者是字節(jié)流來說枣宫,“type”指的的數據類型;而對于packages和bundles來說吃环,“type”指的就是它們內部的目錄層級結構也颤。
UTI用途
大多數情況下,一個UTI提供的是系統中所有程序和服務都能夠識別并且依賴的一個唯一的標識郁轻,這么講可能有些太抽象翅娶,我們使用一下官方文檔中給出的例子,比如一個JPEG類型的圖片文件好唯,在不同的環(huán)境下竭沫,可以有下面幾種不同的標識方法:
- ‘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的定義和我們開發(fā)iOS程序時填寫organization時一樣,采取的是反域名規(guī)則蓖柔。如下面這幾種:
- com.apple.quicktime-movie
- com.mycompany.myapp.myspecialfiletype
- public.html
- com.apple.pict
- public.jpeg
而UTI中的域名辰企,如‘com’、‘public’這些况鸣,僅僅是用來表示這個UTI字符串在域名層級中的位置牢贸,它不會影響任何相似類型的分組。比如镐捧,‘public’域名就是大部分應用程序用來標識標準類型的潜索,而目前僅僅只有Apple可以創(chuàng)建‘public’域名的UTI。
另外懂酱,我們可能會碰到的是一種‘dyn’域名竹习,是動態(tài)域名,意思就是我們使用中玩焰,不會指定這種類型的UTI為某一個字符串由驹,然后系統運行過程中,會自動識別幫我們處理昔园。針對這種動態(tài)標識蔓榄,我們是看不到的,但是我們可以通過UTI字符串的操作方式轉換成我們的常用類型默刚,比如OSType,MIME類型等甥郑。
官方文檔中,對動態(tài)標識有個比喻:“You can think of a dynamic identifier as a UTI-compatible wrapper around an otherwise unknown filename extension, MIME type, OSType, and so on”荤西,大概意思就是我們可以把這種動態(tài)標識當做是針對普通類型進行了重寫包裝的澜搅,而且是兼容UTI的一種標識伍俘。
最后一種就是可以自定義的域名,代表性的就是‘com’域名勉躺,Apple也給我們提供了一些他們定義的'com'域名的UTI癌瘾。
順應性
UTI相對于其他那些舊標簽的一個關鍵優(yōu)勢就是在于,它可以在一個順應結構中聲明饵溅。而用我們面向對象的方式說妨退,UTI就是可繼承的,而且是多繼承方式蜕企。先上圖:
如上圖所示咬荷,‘public.html’這個UTI就是繼承于‘public.text’這個UTI,因為‘public.html’標識的是HTML文本格式轻掩,也屬于是文本格式的一種幸乒,而文本、圖片等等這些內容又都屬于是數據的一種唇牧,所以他們繼承于'public.data'這個UTI罕扎。
上面這個UTI繼承結構圖,指的是UTI中的內容形式的繼承結構奋构,此外壳影,原則上來說,指定UTI層次的時候弥臼,即可以指定它的功能結構,也可以指定它的物理結構根灯,上圖是就是一個內容形式的功能結構圖.物理結構指的就是這個UTI的物理實質径缅,比如它標識一個目錄,一個文件等烙肺,而功能結構指的就是這個UTI的用圖纳猪,比如同樣是文件,它標識的可以是圖片桃笙、視頻等等氏堤。 而官方文檔也給出了一般指定UTI層次結構的規(guī)則:
1.一個UTI在物理層次上需要繼承‘public.item’
2.一個UTI在功能層次需要繼承非'public.item'之外的UTI。
然而搏明,指定UTI的功能層次并不是強制的鼠锈,但是這樣做是考慮到可以更好地將UTI集成到系統一些特性中,就比如Spotlight應用星著,就可以把我們指定的功能性UTI和命名屬性聯系起來购笆。下面是一個UTI功能順應結構和物理順應結構圖:
這個順應性使得我們的UTI在決定類型上擁有更高的靈活性,不僅避免了大量的條件判斷的使用虚循,而且還可以關聯你想不到的一些類型同欠。
使用UTI
1. 應用場景
在Mac OS中我們開發(fā)應用時我們可以經常使用到UTI样傍,但是在開發(fā)iOS應用程序時,我們應用到UTI的場景不是很多铺遂,這也是現在網上教程偏少得原因衫哥。而在iOS開發(fā)中,一般我們使用UTI來標識剪貼板的類型襟锐。而在具體使用到Apple給我們提供的UTI字符串的時候炕檩,我們必須使用在UTCoreTypes.h文件中定義的常量來代替直接使用字符串。關于UIPasteboard的詳細使用捌斧,大家可以去這篇博客中詳細學習一下:精通UIPasteboard粘貼板笛质。
2. 操作方法
現在我們來看一下蘋果提供的一些直接操作UTI的函數方法,簡單列舉幾個捞蚂。我們可以在MobileCoreServices這個framework中的UIType.h文件中找到妇押,我們也可以仔細的看一下這個framework中的其他文件,都是對UTI的一些定義和聲明姓迅。
1.UITypeEqual
判斷倆個UTI是否完全一樣敲霍,后者是一個動態(tài)標簽說明是否是另外一個UTI標簽說明的子集。
2.UITypeConformsTo
判斷倆個標簽的順應性丁存,用面向對象的角度理解肩杈,就是判斷是否是子類。
3.UTTypeCreatePreferredIdentifierForTag
通過其他類型標識符解寝,如MIME標識符扩然,轉換成UTI,當可以創(chuàng)建多個UTI字符串時聋伦,一般返回'public'域名的UTI夫偶。
4.UTTypeCreateAllIdentifiersForTag
通過其他類型標識符,如MIME標識符觉增,轉換成UTI兵拢,當可以創(chuàng)建多個UTI字符串時,返回所有的UTIs,讓你自己選
5.UTTypeCopyPreferredTagWithClass
交換UTI字符串標識
自定義UTI
1. 用法
蘋果允許Mac開發(fā)者為他們的Mac App中獨有的數據格式自定義新的UTI逾礁。它們一般被聲明在下面幾個文件中
- Info.plist
- Application bundles
- Spotlight Importer bundles
- Automator action bundles
使用官方給我們的一個UTI聲明的例子说铃,Public.jpeg聲明:
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>public.jpeg</string>
<key>UTTypeReferenceURL</key>
<string>http://www.w3.org/Graphics/JPEG/</string>
<key>UTTypeDescription</key>
<string>JPEG image</string>
<key>UTTypeIconFile</key>
<string>public.jpeg.icns</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.image</string>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>com.apple.ostype</key>
<string>JPEG</string>
<key>public.filename-extension</key>
<array>
<string>jpeg</string>
<string>jpg</string>
</array>
<key>public.mime-type</key>
<string>image/jpeg</string>
</dict>
</dict>
</array>
一個UTI聲明的屬性列表:
<section>
<div style="margin-top:1.667em;margin-bottom:1.667em;">
<table border="0" cellspacing="0" cellpadding="5">
<tbody>
<tr>
<th scope="col" style="font-weight:400;background-color:#93A5BB;padding:0.3em 0.667em;font-size:13px;color:#FFFFFF;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:#9BB3CD;border-right-width:1px;border-right-style:solid;border-right-color:#9BB3CD;">
<p style="font-weight:700;line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;margin-bottom:0.33em;">
Key
</p>
</th>
<th scope="col" style="font-weight:400;background-color:#93A5BB;padding:0.3em 0.667em;font-size:13px;color:#FFFFFF;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:#9BB3CD;border-right-width:1px;border-right-style:solid;border-right-color:#9BB3CD;">
<p style="font-weight:700;line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;margin-bottom:0.33em;">
Value type
</p>
</th>
<th scope="col" style="font-weight:400;background-color:#93A5BB;padding:0.3em 0.667em;font-size:13px;color:#FFFFFF;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:#9BB3CD;border-right-width:1px;border-right-style:solid;border-right-color:#9BB3CD;">
<p style="font-weight:700;line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;margin-bottom:0.33em;">
Description
</p>
</th>
</tr>
<tr>
<td scope="row">
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
<code style="font-family:Courier, Consolas, monospace;color:#666666;">UTExportedTypeDeclarations</code>
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
array of dictionaries
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
An array of exported UTI declarations (that is, identifiers owned by the bundle’s publisher).
</p>
</td>
</tr>
<tr>
<td scope="row">
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
<code style="font-family:Courier, Consolas, monospace;color:#666666;">UTImportedTypeDeclarations</code>
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
array of dictionaries
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
An array of imported UTI declarations (that is, identifiers owned by another company or organization).
</p>
</td>
</tr>
<tr>
<td scope="row">
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
<code style="font-family:Courier, Consolas, monospace;color:#666666;">UTTypeIdentifier</code>
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
string
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
The UTI for the declared type. This key is required for UTI declarations.
</p>
</td>
</tr>
<tr>
<td scope="row">
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
<code style="font-family:Courier, Consolas, monospace;color:#666666;">UTTypeTagSpecification</code>
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
dictionary
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
A dictionary defining one or more equivalent type identifiers.
</p>
</td>
</tr>
<tr>
<td scope="row">
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
<code style="font-family:Courier, Consolas, monospace;color:#666666;">UTTypeConformsTo</code>
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
array of strings
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
The UTIs to which this identifier conforms.
</p>
</td>
</tr>
<tr>
<td scope="row">
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
<code style="font-family:Courier, Consolas, monospace;color:#666666;">UTTypeIconFile</code>
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
string
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
The name of the bundle icon resource to associate with this UTI.
</p>
</td>
</tr>
<tr>
<td scope="row">
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
<code style="font-family:Courier, Consolas, monospace;color:#666666;">UTTypeDescription</code>
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
string
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
A user-visible description of this type. You can localize this string by including it in an <code style="font-family:Courier, Consolas, monospace;color:#666666;">InfoPlist.strings</code> file.
</p>
</td>
</tr>
<tr>
<td scope="row">
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
<code style="font-family:Courier, Consolas, monospace;color:#666666;">UTTypeReferenceURL</code>
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
string
</p>
</td>
<td>
<p style="line-height:normal;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;">
The URL of a reference document describing this type.
</p>
</td>
</tr>
</tbody>
</table><br />
</div>
</section>
<section>
<a name="http://apple_ref/doc/uid/TP40001319-CH204-SW4" title="Recommendations for Declaring new Uniform Type Identifiers" style="color:#3366CC;font-family:'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;font-size:13px;background-color:#FFFFFF;"></a>
</section>
<p>
<br />
</p>
而且自定義的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的發(fā)布者蚓峦,也就是你的項目舌剂,而被導入的UTI聲明所有人就是其他人,所以就是說如果對于一個UTI來說暑椰,倆種類型的聲明都存在霍转,則自己定義的UTI聲明優(yōu)先使用。
2. 自定義UTI的建議
- 你的UTI字符串必須是唯一的一汽。以‘com.’開頭的反域名命名方式是確保唯一性的簡單有效的方法避消。
- 如果你的代碼依賴于第三方App,UTI類型或許不會在系統中展示召夹,你應該在bundle中聲明為導入類型
- 如果你的UTI類型是一個或多個已存在的UTI類型的子類岩喷,則必須給它添加順應性,讓它繼承于某個父類监憎。最好是繼承于‘public’類型纱意。