前言:作為一名swift初學(xué)者,我希望能通過寫文章的方式來更好地幫助自己學(xué)習(xí),更希望能得到你的建議和批評(píng)。
如有紕漏之處,還望不吝賜教。
一.簡(jiǎn)介
在使用一些不需要服務(wù)端支持的App時(shí),我發(fā)現(xiàn)部分App通過使用iCloud文檔存儲(chǔ)功能來滿足應(yīng)用數(shù)據(jù)云存儲(chǔ)的需求,如饑荒的游戲數(shù)據(jù)存儲(chǔ)、素記的日記存儲(chǔ)等,用戶可以在自己iCloud賬號(hào)下的任何設(shè)備訪問或修改App的這部分?jǐn)?shù)據(jù),十分方便。
二.基本概念
在iOS iCloud存儲(chǔ)中,蘋果提供了三個(gè)功能,分別是:
- Key-value storage
- iCloud Documents
- CloudKit
1.Key-value storage
顧名思義,這是一個(gè)類似于iOS里NSUserDefaults的通過鍵值對(duì)來保存簡(jiǎn)單數(shù)據(jù)的功能,這種Property-list數(shù)據(jù)格式適合存儲(chǔ)一些非關(guān)鍵數(shù)據(jù),如用戶配置。
2.iCloud Documents
這個(gè)功能提供了文件及目錄的數(shù)據(jù)類型,這個(gè)特性決定了此功能相較于Key-value storage更適合進(jìn)行關(guān)鍵數(shù)據(jù)的存儲(chǔ)。
3.CloudKit
相較于前兩個(gè),CloudKit就要復(fù)雜得多,這是蘋果為開發(fā)者提供的一整套數(shù)據(jù)庫工具,類似于Maxleap這類第三方云服務(wù)。開發(fā)者通過蘋果提供的Cloud dashboard網(wǎng)站可以配置所需的表結(jié)構(gòu),并通過在代碼中導(dǎo)入CloudKit進(jìn)行數(shù)據(jù)庫操作。
可以看出,這三個(gè)數(shù)據(jù)庫工具是蘋果為了滿足不同層次的數(shù)據(jù)云存儲(chǔ)場(chǎng)景而設(shè)置的,所以在使用前要根據(jù)自己的需要來選擇相對(duì)應(yīng)的工具。對(duì)我而言,iCloud Documents更能滿足我的需求,下面將開始iCloud Documents的學(xué)習(xí)。
三.iCloud Documents的深入學(xué)習(xí)
簡(jiǎn)單說來,iCloud Documents只需要兩個(gè)類就可以實(shí)現(xiàn)其功能:
1.類名:NSMetadataQuery
功能:定位數(shù)據(jù),也就是查詢文件列表。
2.類名:UIDocument
功能:文件操作,對(duì)某一個(gè)文件進(jìn)行操作,包括新增文件。
四.iCloud Documents功能的簡(jiǎn)單實(shí)現(xiàn)
1.準(zhǔn)備工作
1.1新建項(xiàng)目
1.2開啟iCloud功能
如圖:
這里需要注意的是如果在Capabilities里沒有找到這個(gè)選項(xiàng)的話,你需要的是一個(gè)付費(fèi)的蘋果開發(fā)者賬號(hào)。
而配置里的Containers就像iOS的沙盒目錄一樣,將app內(nèi)不同的文件目錄分開,如果你需要多個(gè)容器的話請(qǐng)自行配置,這里我選擇默認(rèn)容器。
1.3在你的iOS設(shè)備上登錄iCloud賬號(hào)
1.4創(chuàng)建一個(gè)簡(jiǎn)單的列表視圖,增加輸入、保存、修改、刪除這四個(gè)按鈕
2.檢測(cè) iCloud 可用性
使用 iCloud Documents 之前,需要檢測(cè)當(dāng)前設(shè)備是否開啟了 iCloud 功能。
這里通過獲取iCloudDocuments路徑來進(jìn)行可用性判斷:
func iCloudDocumentURL() -> URL? {
let fileManager = FileManager.default
if let url = fileManager.url(forUbiquityContainerIdentifier: nil) {
return url.appendingPathComponent("Documents")
}
return nil
}
3.查詢文件列表
確認(rèn)iCloud可用之后,我們查詢Documents目錄下的子目錄。這一步,通過NSMetadataQuery這個(gè)類來實(shí)現(xiàn):
private var query : NSMetadataQuery = NSMetadataQuery()
func loadDocuments()->Bool{
let baseURL = self.iCloudDocumentURL()
guard baseURL != nil else {
return false
}
let center = NotificationCenter.default
query.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope]
query.predicate = NSPredicate(value: true)
center.addObserver(self, selector: #selector(metadataQueryDidFinishGathering), name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: nil)
self.query.enableUpdates()
query.start()
return true
}
searchScopes這個(gè)屬性用來設(shè)置所需查詢的目錄,NSMetadataQueryUbiquitousDocumentsScope為子目錄,也就是Documents下的目錄或文件。predicate可根據(jù)自己的需求設(shè)置查詢條件。
查詢的結(jié)果通過在通知中心注冊(cè)觀察者來監(jiān)聽,注冊(cè)后就可以開始查詢。
監(jiān)聽到查詢結(jié)束的通知后,建議關(guān)閉查詢操作。
這樣,我們就得到了Documents目錄下的所有子文件,子文件以NSMetadataItem的實(shí)例存在query.results數(shù)組里。接下來,通過對(duì)NSMetadataItem實(shí)例調(diào)用value(forAttribute: NSMetadataItemURLKey)這樣的方法來獲取子文件的URL、文件名、修改時(shí)間等信息。代碼如下:
func metadataQueryDidFinishGathering() {
query.disableUpdates()
query.stop()
let center = NotificationCenter.default
center.removeObserver(self)
var diaryList = Array<Any>()
if (query.resultCount == 1) {
let item = query.results.first as! NSMetadataItem
let fileURL = item.value(forAttribute: NSMetadataItemURLKey) as! URL
let document = XDocument(fileURL: fileURL )
document.open(completionHandler: { (success) in
for dic in document.diaries{
let diary = Diary(dic: dic)
diaryList.append(diary)
}
self.delegate?.queryDocumentsComplete(results: diaryList)
document.close(completionHandler: nil)
})
}else{
self.delegate?.queryDocumentsComplete(results: diaryList)
}
}
示例代碼中僅在Documents目錄下保存了一個(gè)文件,所以處理查詢結(jié)果時(shí)也只是操作這一個(gè)文件,你可以根據(jù)自己的需求對(duì)results遍歷及操作。在上述代碼中可以看到,獲取到子文件的路徑后,我是通過繼承自UIDocument的XDocument這個(gè)類進(jìn)行文件讀取的,具體原因在下面詳述。
4.實(shí)現(xiàn)UIDocument的方法進(jìn)行文件操作
iCloud 的官方文檔中強(qiáng)制要求使用者對(duì)文件的操作通過 NSFileCoordinator 和 NSFilePresenter 來進(jìn)行的,而UIDocument就是對(duì)這兩個(gè)類的封裝,由于iCloud的文件可以在用戶的不同設(shè)備進(jìn)行操作,所以為了讀寫安全,我們需要使用UIDocument對(duì)文件進(jìn)行操作。
需要注意的是,UIDocument不能直接使用,我們需要自己實(shí)現(xiàn)對(duì)文件內(nèi)容的操作邏輯,即繼承UIDocument并實(shí)現(xiàn)contents(forType typeName: String)和load(fromContents contents: Any, ofType typeName: String?) 這兩個(gè)方法。這兩個(gè)回調(diào)方法前者是用來自定義所需保存的數(shù)據(jù),后者是用來在我們將獲取的數(shù)據(jù)解析后保存起來。代碼如下:
let kArchiveKey = "Diary"
import UIKit
class XDocument: UIDocument {
var diaries : Array<Dictionary<String, Any>>!
override func contents(forType typeName: String) throws -> Any {
let data = NSMutableData.init()
let archiver:NSKeyedArchiver = NSKeyedArchiver.init(forWritingWith: data)
archiver.encode(self.diaries, forKey: kArchiveKey)
archiver.finishEncoding()
return data
}
override func load(fromContents contents: Any, ofType typeName: String?) throws {
let unarchiver:NSKeyedUnarchiver = NSKeyedUnarchiver.init(forReadingWith: contents as! Data)
self.diaries = unarchiver.decodeObject(forKey: kArchiveKey) as! Array
unarchiver.finishDecoding()
}
}
代碼中,我將Diary這個(gè)類轉(zhuǎn)成Dictionary后所保存在的數(shù)據(jù)列表用NSKeyedArchiver轉(zhuǎn)成Data保存起來。
這樣我們可以使用XDocument這個(gè)類對(duì)我們的數(shù)據(jù)進(jìn)行保存及修改,記得在讀寫操作前分別調(diào)用UIDocument的open及close方法。
新建文件或者修改文件都是通過save(to url: URL, for saveOperation: UIDocumentSaveOperation, completionHandler: ((Bool) -> Swift.Void)? = nil)這個(gè)方法進(jìn)行的,區(qū)別在于UIDocumentSaveOperation這個(gè)枚舉,
一目了然。
具體代碼如下:
func save(diaries:Array<Diary>){
let baseURL = self.iCloudDocumentURL()
if baseURL != nil{
var dataList = Array<Dictionary<String, Any>>()
for diary in diaries {
let dic = diary.convertToDictionary()
dataList.append(dic)
}
var url = self.iCloudDocumentURL()
url = url?.appendingPathComponent("saveData")
let document = XDocument(fileURL: url!)
document.open { (success) in
if(success){
document.diaries = dataList
document.save(to: url!, for: .forOverwriting) { (success) in
if success{
print("Overwrit success")
document.close(completionHandler: nil)
}
}
}else{
document.diaries = dataList
document.save(to: url!, for: .forCreating) { (success) in
if success{
print("Creat success")
document.close(completionHandler: nil
}
}
}
}
}
}
這樣,我們就簡(jiǎn)單的實(shí)現(xiàn)了iCloud documents的數(shù)據(jù)存儲(chǔ)。
如果demo中有任何問題或疑問,請(qǐng)告訴我,謝謝。