前言
最近項目中有個需求,在iOS設備上使用iOS系統提供的內容分享功能,從第三方App應用直接分享實體內容到我們的應用中。其大概的原理是這樣的,首先為我們的iOS應用注冊可以打開document types(文檔類型),然後在第三方應用中,如果它們使用了iOS提供的分享功能,那麼就會看到我們的應用程序,點擊進行分享。而關於需求的設計和實現的具體思路,我會在下一篇博客中詳細講解。這篇文章是來講一下蘋果在iOS和OS X系統中為了更好的進行類型識別,而提供的一套統一的規范,也就是標題中提到的“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類型的圖片文件,在不同的環境下,可以有下面幾種不同的標識方法:
1.‘JPEG’, OSType表示,
2.’.jpg’, 文件擴展名
3.jpeg’, 文件擴展名
4.’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就是可繼承的,而且是多繼承方式。先上圖:
如上圖所示,‘public.html’這個UTI就是繼承於‘public.text’這個UTI,因為‘public.html’標識的是HTML文本格式,也屬於是文本格式的一種,而文本、圖片等等這些內容又都屬於是數據的一種,所以他們繼承於’public.data’這個UTI。
上面這個圖示指的是UTI中的內容形式的繼承結構,此外,原則上來說,指定UTI層次的時候,即可以指定它的功能結構,也可以指定它的物理結構,上圖是就是一個內容結構圖.物理結構指的就是這個UTI的物理實質,比如它標識一個目錄,一個文件等,而功能結構指的就是這個UTI的用圖,比如同樣是文件,它標識的可以是圖片、視頻等等。 而一般指定UTI層次結構的規則是:
1.一個UTI在物理層次上需要繼承‘public.item’
2.一個UTI在功能層次需要繼承非’public.item’之外的UTI。
然而,指定UTI的功能層次並不是強制的,但是這樣做是考慮到可以更好地將UTI集成到系統一些特性中,就比如Spotlight應用,就可以把我們指定的功能性UTI和命名屬性聯系起來。下面是一個UTI功能順應結構和物理順應結構圖:
這個順序性使得我們的UTI在決定類型上擁有更高的靈活性,不僅避免了大量的條件判斷的使用,而且還可以關聯你想不到的一些類型。
使用UTI
應用場景
在Mac OS中我們開發應用時我們可以經常使用到UTI,但是在開發iOS應用程序時,我們應用到UTI的場景不是很多,這也是現在網上教程偏少得原因。而在iOS開發中,一般我們使用UTI來標識剪貼板的類型。而在具體使用到Apple給我們提供的UTI字符串的時候,我們必須使用在UTCoreTypes.h文件中定義的常量來代替直接使用字符串。關於UIPasteboard的詳細使用,大家可以去這篇博客中詳細學習一下:精通UIPasteboard粘貼板。
操作方法
現在我們來看一下蘋果提供的一些直接操作UTI的函數方法,簡單列舉幾個。我們可以在MobileCoreServices這個framework中的UIType.h文件中找到,我們也可以仔細的看一下這個framework中的其他文件,都是對UTI的一些定義和生命。
1.UITypeEqual
判斷倆個UTI是否完全一樣,後者是一個動態標簽說明是否是另外一個UTI標簽說明的子集。
2.UITypeConformsTo
判斷倆個標簽的順應性,用面向對象的角度理解,就是判斷是否是子類。
3.UTTypeCreatePreferredIdentifierForTag
通過其他類型標識符,如MIME標識符,轉換成UTI,當可以創建多個UTI字符串時,一般返回’public’域名的UTI。
4.UTTypeCreateAllIdentifiersForTag
通過其他類型標識符,如MIME標識符,轉換成UTI,當可以創建多個UTI字符串時,返回所有的UTIs,讓你自己選
5.UTTypeCopyPreferredTagWithClass
交換UTI字符串標識
自定義UTI
用法
蘋果允許Mac開發者為他們的Mac App中獨有的數據格式自定義新的UTI。它們一般被聲明在下面幾個文件中
Info.plist
Application bundles
Spotlight Importer bundles
Automator action bundles
使用官方給我們的一個UTI聲明的例子,Public.jpeg聲明:
UTExportedTypeDeclarations
UTTypeIdentifier
public.jpeg
UTTypeReferenceURL
http://www.w3.org/Graphics/JPEG/;
UTTypeDescription
JPEG image
UTTypeIconFile
public.jpeg.icns
UTTypeConformsTo
public.image
public.data
UTTypeTagSpecification
com.apple.ostype
JPEG
public.filename-extension
jpeg
jpg
public.mime-type
image/jpeg
一個UTI聲明的屬性列表:
Value type
Description
UTExportedTypeDeclarations
array of dictionaries
An array of exported UTI declarations (that is, identifiers owned by the bundle’s publisher).
UTImportedTypeDeclarations
array of dictionaries
An array of imported UTI declarations (that is, identifiers owned by another company or organization).
UTTypeIdentifier
string
The UTI for the declared type. This key is required for UTI declarations.
UTTypeTagSpecification
dictionary
A dictionary defining one or more equivalent type identifiers.
UTTypeConformsTo
array of strings
The UTIs to which this identifier conforms.
UTTypeIconFile
string
The name of the bundle icon resource to associate with this UTI.
UTTypeDescription
string
A user-visible description of this type. You can localize this string by including it in an InfoPlist.strings file.
UTTypeReferenceURL
string
The URL of a reference document describing this type.
而且自定義的UTI必須指定UTExportedTypeDeclarations或者UTImportedTypeDeclarations,這樣第三方App都可以使用,要麼被導入到項目中的其他沒有擁有這種數據格式的bundle中,這樣都可以看得到。而官方文檔中有一句話,“If both imported and exported declarations for a UTI exist, the exported declaration takes precedence over imported one”,我的理解是,被導出去得UTI聲明所有人是定義UTI的發布者,而被導入的UTI聲明所有人就是其他人,所以就是說如果對於一個UTI來說,倆種類型的聲明都存在,則被導出的UTI聲明優先使用。
自定義UTI的建議
1.你的UTI字符串必須是唯一的。以‘com.’開頭的反域名命名方式是確保唯一性的簡單有效的方法。
2.如果你的代碼依賴於第三方App,UTI類型或許不會在系統中展示,你應該在bundle中聲明為導入類型
3.如果你的UTI類型是一個或多個已存在的UTI類型的子類,則必須給它添加順應性,讓它繼承於某個父類。最好是繼承於‘public’類型。