啟用與 iOS 行動裝置應用程式的離線同步處理
概觀
本教學課程涵蓋使用適用於 iOS 的 Azure App Service Mobile Apps 功能進行離線同步處理。 透過離線同步處理,終端使用者可以與行動應用程式互動,以檢視、新增或修改數據,即使他們沒有網路連線也一樣。 變更會儲存在本機資料庫中。 裝置重新上線之後,變更會與遠端後端同步。
如果這是您第一次使用Mobile Apps的體驗,您應該先完成 建立iOS應用程式的教學課程。 如果您未使用下載的快速入門伺服器專案,您必須將資料存取延伸模組套件新增至專案。 如需有關伺服器擴充套件的更多資訊,請參閱 使用適用於 Azure 行動應用的 .NET 後台伺服器 SDK。
若要深入瞭解離線同步處理功能,請參閱 Mobile Apps中的離線數據同步。
檢閱用戶端同步程序代碼
您為 建立 iOS 應用程式 教學課程下載的用戶端項目已經包含使用本機 Core 數據型資料庫支援離機同步處理的程式碼。 本節摘要說明教學課程程序代碼中已包含的內容。 如需功能的概念性概觀,請參閱 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)
這個方法會使用
MSCoreDataStore
Mobile Apps SDK提供的介面來建立本地存儲。 或者,您可以藉由實作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 作業)傳送到伺服器。 接著會呼叫 Helper pullData ,它會呼叫 MSSyncTable.pullWithQuery 來擷取遠端數據,並將其儲存在本機資料庫中。
在 Swift 版本中,因為推送作業並非絕對必要,因此不會呼叫 pushWithCompletion。 如果在進行推送操作的資料表的同步內容中有任何待處理的變更,提取動作總是會先發出推送。 不過,如果您有不只一個同步表格,最好明確地呼叫推送同步,以確保所有資料在相關表格之間保持一致。
在 Objective-C 和 Swift 版本中,您可以使用 pullWithQuery 方法來指定查詢來篩選您想要擷取的記錄。 在此範例中,查詢會擷取遠端 TodoItem
數據表中的所有記錄。
pullWithQuery 的第二個參數是用於增量同步處理的查詢標識碼。累加同步只會使用記錄的時間戳來擷取自上次同步之後修改的UpdatedAt
記錄(在本地存儲中呼叫updatedAt
)。查詢標識碼應該是應用程式中每個邏輯查詢唯一的描述性字串。 若要退出增量同步,請傳遞 nil
作為查詢 ID。 這種方法可能沒有效率,因為每次提取時都會檢索所有記錄。
當您修改或新增數據、使用者執行重新整理手勢及啟動時,Objective-C 應用程式會同步處理。
當使用者執行重新整理手勢並啟動時,Swift 應用程式就會同步處理。
因為應用程式會在修改數據時同步處理(Objective-C),或每當應用程式啟動時(Objective-C 和 Swift),應用程式會假設使用者處於在線狀態。 在稍後的章節中,您將更新應用程式,讓使用者即使在離線時也能編輯。
檢閱核心數據模型
當您使用核心資料離線存放區時,您必須在資料模型中定義特定的數據表和欄位。 範例應用程式已經包含具有正確格式的數據模型。 在本節中,我們會逐步解說這些數據表,以示範其使用方式。
開啟 QSDataModel.xcdatamodeld。 四個數據表是由 SDK 所定義,一個用於 to-do 專案本身:
- MS_TableOperations:追蹤需要與伺服器同步的項目。
- MS_TableOperationErrors:追蹤離線同步處理期間發生的任何錯誤。
- MS_TableConfig:追蹤所有推拉作業中,上次同步作業的最後更新時間。
- TodoItem:儲存 to-do 項目。 createdAt、updatedAt 和 version 的系統數據行是選擇性的系統屬性。
備註
Mobile Apps SDK 會保留開頭為 “``” 的資料行名稱。 請勿將此前置詞與系統欄以外的其他任何項目搭配使用。 否則,當您使用遠端後端時,欄位名稱將會被修改。
當您使用離線同步處理功能時,請定義三個系統資料表及一個資料表。
系統數據表
MS_TableOperations (表格操作)
屬性 | 類型 |
---|---|
識別碼 | 整數 64 |
項目編號 | 字符串 |
屬性 | 二進位資料 |
表格 | 字符串 |
tableKind | 整數 16 |
MS_表格操作錯誤
屬性 | 類型 |
---|---|
識別碼 | 字符串 |
operationId | 整數 64 |
屬性 | 二進位資料 |
tableKind | 整數 16 |
MS_TableConfig
屬性 | 類型 |
---|---|
識別碼 | 字符串 |
鑰匙 | 字符串 |
鍵類型 | 整數 64 |
表格 | 字符串 |
價值 | 字符串 |
資料表
待辦事項
屬性 | 類型 | 備註 |
---|---|---|
識別碼 | 字串,標示為必要 | 遠端存放區中的主鍵 |
完成 | 布爾邏輯 | 待辦事項欄位 |
文字 | 字符串 | 待辦事項欄位 |
創建於 | 日期 | (選擇性)對應至 createdAt 系統屬性 |
更新於 | 日期 | (選擇性)對應至 updatedAt 系統屬性 |
版本 | 字符串 | (選擇性)用來偵測衝突,映射至版本 |
變更應用程式的同步處理行為
在本節中,您會修改應用程式,使其不會在應用程式啟動時或在插入和更新專案時同步處理。 只有在執行重新整理手勢按鈕時,才會同步處理。
「Objective-C」:
在 QSTodoListViewController.m 中,變更 viewDidLoad 方法以移除方法結尾的 呼叫
[self refresh]
。 現在數據不會與應用程式啟動時的伺服器同步處理。 相反地,它會與本地存儲的內容同步處理。在 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:
在 viewDidLoad
ToDoTableViewController.swift 中,註解掉這裡顯示的兩行代碼,防止應用程式啟動時同步處理。 撰寫本文時,Swift Todo 應用程式不會在有人新增或完成專案時更新服務。 它只會在應用程式啟動時更新服務。
self.refreshControl?.beginRefreshing()
self.onRefresh(self.refreshControl)
測試應用程式
在本節中,您會連線到無效的 URL,以模擬離線情境。 當您新增數據項時,它們會保留在本機 Core 資料存放區中,但不會與行動應用程式後端同步。
將 QSTodoService.m 中的行動應用程式 URL 變更為無效的 URL,然後再次執行應用程式:
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 項目。 結束模擬器(或強制關閉應用程式),然後重新啟動它。 確認您的變更持續存在。
檢視遠端 TodoItem 資料表的內容:
- 如需 Node.js 後端,請移至 Azure 入口網站 ,然後在行動應用程式後端中,按兩下 [簡易數據表>TodoItem]。
- 針對 .NET 後端,請使用 SQL 工具,例如 SQL Server Management Studio 或 REST 用戶端,例如 Fiddler 或 Postman。
確認新項目 尚未 與伺服器同步。
將 URL 變更回 QSTodoService.m 中的正確 URL,然後重新執行應用程式。
藉由下拉項目清單來執行重新整理手勢。
進度旋轉器顯示。再次檢視 TodoItem 數據。 現在應該會顯示新的和變更的 to-do 項目。
總結
為了支持離線同步功能,我們使用了 MSSyncTable
介面,並以本地存儲初始化 MSClient.syncContext
。 在此情況下,本地存儲是以核心數據為基礎的資料庫。
當您使用 Core Data 本地儲存時,必須使用 正確的系統屬性來定義數個數據表。
行動應用程式的一般建立、讀取、更新和刪除 (CRUD) 作業的運作方式就像應用程式仍然連線一樣,但所有作業都會針對本地存儲進行。
當我們將本地存儲與伺服器同步處理時,我們使用 MSSyncTable.pullWithQuery 方法。
其他資源
- Mobile Apps 中的離線數據同步處理
- 雲端覆蓋:Azure 行動服務中的離線同步(影片是關於行動服務,但行動應用程式的離線同步運作方式類似。)