啟用 iOS Mobile Apps 的離線同步處理
概觀
本教學課程涵蓋適用於 iOS 的 Azure App Service Mobile Apps 功能的離線同步處理說明。 透過離線同步處理,終端使用者即使沒有網路連線,也可以和行動裝置 App 互動以檢視、新增、修改資料。 變更會儲存在本機資料庫中。 裝置恢復上線後,這些變更就會與遠端後端進行同步處理。
如果這是您第一次接觸 Mobile Apps,請先完成建立 iOS 應用程式 教學課程。 如果您不使用下載的快速入門伺服器專案,則必須將資料存取擴充套件新增至您的專案。 如需伺服器擴充套件的詳細資訊,請參閱使用 Azure Mobile Apps 的 .NET 後端伺服器 SDK。
若要深入了解離線同步處理功能,請參閱 Mobile Apps 中的離線資料同步處理。
檢閱用戶端同步處理程式碼
您針對建立 iOS 應用程式教學課程下載的用戶端專案,已經包含了使用本機核心資料式資料庫支援離線同步處理的程式碼。 本節將摘要說明已包含在教學課程程式碼中的內容。 如需此功能的概念性概觀,請參閱 Mobile Apps 中的離線資料同步處理。
Mobile Apps 的離線資料同步處理功能可讓終端使用者在無法存取網路時,仍可與本機資料庫互動。 若要在您的應用程式中使用這些功能,您可初始化 MSClient
的同步處理內容以及參考本機存放區。 然後透過 MSSyncTable 介面參考您的資料表。
在 QSTodoService.m (Objective-C) 或 ToDoTableViewController.swift (Swift) 中,注意到成員 syncTable 的類型為 MSSyncTable。 離線同步處理會使用此同步處理資料表介面,而不是 MSTable。 使用同步處理資料表時,所有作業都會移至本機存放區,而且只會與具有明確推送和提取作業的遠端後端同步處理。
若要取得同步處理資料表的參考,請在 MSClient
上使用 syncTableWithName 方法。 若要移除離線同步處理功能,請改用 tableWithName。
必須先初始化本機存放區,才可以執行資料表作業。 以下是相關的程式碼:
Objective-C. 在 QSTodoService.init 方法中:
MSCoreDataStore *store = [[MSCoreDataStore alloc] initWithManagedObjectContext:context]; self.client.syncContext = [[MSSyncContext alloc] initWithDelegate:nil dataSource:store callback:nil];
Swift。 在 ToDoTableViewController.viewDidLoad 方法中︰
let client = MSClient(applicationURLString: "http:// ...") // URI of the Mobile App let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext! self.store = MSCoreDataStore(managedObjectContext: managedObjectContext) client.syncContext = MSSyncContext(delegate: nil, dataSource: self.store, callback: nil)
這方法會以 Mobile Apps SDK 提供的
MSCoreDataStore
介面建立一個本機存放區。 或者,您也可以實作MSSyncContextDataSource
通訊協定,改為提供不同的本機存放區。 此外,MSSyncContext 的第一個參數是用來指定衝突處理常式。 因為已傳遞nil
,所以我們會取得預設衝突處理常式,該處理常式在任何衝突時都會失敗。
現在,讓我們執行實際的同步處理作業,並從遠端後端取得資料:
Objective-C.
syncData
先推送新的變更,然後呼叫 pullData 以從遠端後端取得資料。 最後,pullData 方法會取得符合查詢的新資料︰-(void)syncData:(QSCompletionBlock)completion { // Push all changes in the sync context, and then pull new data. [self.client.syncContext pushWithCompletion:^(NSError *error) { [self logErrorIfNotNil:error]; [self pullData:completion]; }]; } -(void)pullData:(QSCompletionBlock)completion { MSQuery *query = [self.syncTable query]; // Pulls data from the remote server into the local table. // We're pulling all items and filtering in the view. // Query ID is used for incremental sync. [self.syncTable pullWithQuery:query queryId:@"allTodoItems" completion:^(NSError *error) { [self logErrorIfNotNil:error]; // Lets the caller know that we have finished. if (completion != nil) { dispatch_async(dispatch_get_main_queue(), completion); } }]; }
Swift:
func onRefresh(sender: UIRefreshControl!) { UIApplication.sharedApplication().networkActivityIndicatorVisible = true self.table!.pullWithQuery(self.table?.query(), queryId: "AllRecords") { (error) -> Void in UIApplication.sharedApplication().networkActivityIndicatorVisible = false if error != nil { // A real application would handle various errors like network conditions, // server conflicts, etc. via the MSSyncContextDelegate print("Error: \(error!.description)") // We will discard our changes and keep the server's copy for simplicity if let opErrors = error!.userInfo[MSErrorPushResultKey] as? Array<MSTableOperationError> { for opError in opErrors { print("Attempted operation to item \(opError.itemId)") if (opError.operation == .Insert || opError.operation == .Delete) { print("Insert/Delete, failed discarding changes") opError.cancelOperationAndDiscardItemWithCompletion(nil) } else { print("Update failed, reverting to server's copy") opError.cancelOperationAndUpdateItem(opError.serverItem!, completion: nil) } } } } self.refreshControl?.endRefreshing() } }
在 Objective-C 版本中,於 syncData
,我們會先在同步處理內容上呼叫 pushWithCompletion。 此方法是 MSSyncContext
的成員 (而非同步處理資料表本身),因為它會將變更推送至所有資料表。 只有以某種方式在本機上修改過的記錄 (透過 CUD 作業),才會傳送至伺服器。 接著會呼叫 pullData 協助程式,該程式會呼叫 MSSyncTable.pullWithQuery 來擷取遠端資料並存放在本機資料庫中。
在 Swift 版本中,因為推送作業不是絕對必要,所以並沒有呼叫 pushWithCompletion。 如果同步處理內容中正在進行推送作業的資料表有任何變更擱置,則提取一律會先發出推送。 不過,如果您有一個以上的同步處理資料表,最好能明確呼叫推送,以確保所有的相關資料表都能一致。
在 Objective-C 與 Swift 版本中,您可以使用 pullWithQuery 方法來指定查詢,以篩選想要擷取的記錄。 在此範例中,查詢會擷取遠端 TodoItem
資料表中的所有記錄。
pullWithQuery的第二個參數是用於增量同步處理的查詢識別碼。累加同步只會擷取自上次同步處理後修改的記錄,使用本機存放區中呼叫 updatedAt
的 UpdatedAt
記錄時間戳記 (。) 查詢識別碼應該是應用程式中每個邏輯查詢唯一的描述性字串。 若選擇不要增量同步處理,請傳遞 nil
做為查詢識別碼。 此方法可能效率不佳,因為它會在每次提取作業擷取所有記錄。
當您修改或新增資料、使用者執行重新整理動作及啟動時,Objective-C 應用程式就會同步處理。
當使用者執行重新整理動作及啟動時,Swift 應用程式就會同步處理。
因為每當資料修改 (Objective-C) 或 App 啟動 (Objective-C 和 Swift) 時,App 就會同步處理,故 App 假設使用者在線上。 在後續章節中,您將會更新 App,讓使用者即使是離線狀態也能進行編輯。
檢閱核心資料模型
在使用「核心資料離線」存放區時,您必須在資料模型中定義特定資料表和欄位。 範例應用程式已經包含具有正確格式的資料模型。 在這一節中,我們會逐步介紹這些資料表並示範其使用方式。
開啟 QSDataModel.xcdatamodeld。 已定義四個資料表--其中三個由 SDK 使用,而一個是用於 To-do 項目本身:
- MS_TableOperations:追蹤需要與伺服器同步的項目。
- MS_TableOperationErrors:追蹤在離線同步處理期間發生的任何錯誤。
- MS_TableConfig:追蹤所有提取作業的最後一次同步處理作業的上次更新時間。
- TodoItem:儲存 To-do 項目。 系統資料行 createdAt、updatedAt 和 version 為選擇性系統屬性。
注意
Mobile Apps SDK 會保留以 "``" 為開頭的資料行名稱。 請不要將此前置詞用於系統資料行以外的項目。 否則,當您使用遠端後端時,系統會修改您的資料行名稱。
當您使用離線同步處理功能時,請定義三個系統資料表,以及資料資料表。
系統資料表
MS_TableOperations
屬性 | 類型 |
---|---|
id | 整數 64 |
itemId | String |
properties | Binary Data |
資料表 | String |
tableKind | 整數 16 |
MS_TableOperationErrors
屬性 | 類型 |
---|---|
id | String |
operationId | 整數 64 |
properties | Binary Data |
tableKind | 整數 16 |
MS_TableConfig
屬性 | 類型 |
---|---|
id | String |
索引鍵 | String |
keyType | 整數 64 |
資料表 | String |
value | String |
資料表
TodoItem
屬性 | 類型 | 注意 |
---|---|---|
識別碼 | 字串 (標示為必要) | 遠端存放區中的主索引鍵 |
完成 | Boolean | To-do 項目欄位 |
text | String | To-do 項目欄位 |
建立時間 | Date | (選擇性) 對應至 createdAt 系統屬性 |
更新時間 | Date | (選擇性) 對應至 updatedAt 系統屬性 |
version | String | (選擇性) 用來偵測衝突,對應至版本 |
變更應用程式的同步處理行為
在本節中,您將修改 App,使它在啟動或有使用者插入並更新項目時不會同步處理。 只有在執行重新整理動作按鈕時,它才會同步。
Objective-C:
在 QSTodoListViewController.m 中,變更 viewDidLoad 方法以移除在方法結尾的
[self refresh]
呼叫。 資料現在已經不會在 App 啟動時同步。 反之,它是與本機存放區的內容同步。在 QSTodoService.m 中,修改
addItem
的定義,使其不會在插入項目後同步處理。 移除self syncData
區塊並以下列項目取代:if (completion != nil) { dispatch_async(dispatch_get_main_queue(), completion); }
如先前所述修改
completeItem
的定義。 移除self syncData
的區塊並以下列項目取代:if (completion != nil) { dispatch_async(dispatch_get_main_queue(), completion); }
Swift:
在 ToDoTableViewController.swift 的 viewDidLoad
中,註解化這兩行以停止在 App 啟動時同步處理。 在本文撰寫期間,當某人新增或完成項目時,Swift Todo App 不會更新服務。 只有在 App 啟動時它才會更新。
self.refreshControl?.beginRefreshing()
self.onRefresh(self.refreshControl)
測試應用程式
在本節中,您將連接至無效的 URL,以模擬離線情況。 當您新增資料項目時,這些項目會存放在本機核心資料存放區,但不會同步到行動裝置 App 後端。
將 QSTodoService.m 中的行動裝置 App URL 變更為無效的 URL,然後再次執行該 App:
Objective-C. 在 QSTodoService.m 中:
self.client = [MSClient clientWithApplicationURLString:@"https://sitename.azurewebsites.net.fail"];
Swift。 在 ToDoTableViewController.swift 中:
let client = MSClient(applicationURLString: "https://sitename.azurewebsites.net.fail")
新增一些 To-do 項目。 結束模擬器 (或強制關閉 App),然後重新啟動它。 確認已保存您的變更。
檢視遠端 TodoItem 資料表的內容:
- 針對 Node.js 後端,請移至 Azure 入口網站,在您的行動裝置 App 後端中按一下 [簡易表]> [TodoItem]。
- 針對 .NET 後端,請使用 SQL 工具 (例如 SQL Server Management Studio) 或 REST 用戶端 (例如 Fiddler 或 Postman)。
請確認新項目「尚未」同步處理到伺服器。
請將 QSTodoService.m 中的 URL 變更回正確的 URL,然後重新執行 App。
將項目清單往下拉,執行重新整理動作。
會出現旋轉進度指示器。再次檢視 TodoItem 資料。 其中會顯示已變更的新 To-do 項目。
總結
為了支援離線同步處理功能,我們使用了 MSSyncTable
介面,並對本機存放區初始化 MSClient.syncContext
。 在此案例中,本機存放區是以核心資料為基礎的資料庫。
使用核心資料本機存放區時,您必須使用正確的系統屬性定義數個資料表。
針對行動裝置 App 的建立、讀取、更新及刪除 (CRUD) 等一般作業,會以 App 有如處於連線狀態之下執行,但所有的作業都是對本機存放區執行。
我們使用 MSSyncTable.pullWithQuery 方法來同步處理本機存放區與伺服器。
其他資源
- Mobile Apps 中的離線資料同步處理
- 雲端涵蓋範圍:Azure 行動服務中的離線同步 處理 (影片與行動服務有關,但 Mobile Apps 離線同步處理的運作方式類似。)