Włączanie synchronizacji offline z aplikacjami mobilnymi systemu iOS

Omówienie

W tym samouczku omówiono synchronizowanie w trybie offline z funkcją usługi Mobile Apps Azure App Service dla systemu iOS. Dzięki synchronizacji w trybie offline użytkownicy końcowi mogą korzystać z aplikacji mobilnej w celu wyświetlania, dodawania lub modyfikowania danych, nawet jeśli nie mają połączenia sieciowego. Zmiany są przechowywane w lokalnej bazie danych. Po powrocie urządzenia do trybu online zmiany są synchronizowane ze zdalnym zapleczem.

Jeśli jest to pierwsze doświadczenie w usłudze Mobile Apps, najpierw należy ukończyć samouczek Tworzenie aplikacji dla systemu iOS. Jeśli nie używasz pobranego projektu serwera szybkiego startu, musisz dodać pakiety rozszerzeń dostępu do danych do projektu. Aby uzyskać więcej informacji na temat pakietów rozszerzeń serwera, zobacz Praca z zestawem SDK serwera zaplecza platformy .NET dla usługi Azure Mobile Apps.

Aby dowiedzieć się więcej na temat funkcji synchronizacji offline, zobacz Synchronizacja danych w trybie offline w usłudze Mobile Apps.

Przeglądanie kodu synchronizacji klienta

Projekt klienta pobrany na potrzeby samouczka Tworzenie aplikacji systemu iOS zawiera już kod, który obsługuje synchronizację w trybie offline przy użyciu lokalnej bazy danych opartej na danych podstawowych. Ta sekcja zawiera podsumowanie tego, co zostało już uwzględnione w kodzie samouczka. Aby zapoznać się z koncepcyjnym omówieniem tej funkcji, zobacz Temat Offline Data Sync in Mobile Apps (Synchronizacja danych offline w usłudze Mobile Apps).

Korzystając z funkcji synchronizacji danych offline w usłudze Mobile Apps, użytkownicy końcowi mogą korzystać z lokalnej bazy danych nawet wtedy, gdy sieć jest niedostępna. Aby korzystać z tych funkcji w aplikacji, należy zainicjować kontekst MSClient synchronizacji i odwołać się do magazynu lokalnego. Następnie odwołujesz się do tabeli za pośrednictwem interfejsu MSSyncTable .

W QSTodoService.m (Objective-C) lub ToDoTableViewController.swift (Swift) zwróć uwagę, że typ tabeli synchronizacji składowej to MSSyncTable. Synchronizacja w trybie offline używa tego interfejsu tabeli synchronizacji zamiast tabeli MSTable. Gdy jest używana tabela synchronizacji, wszystkie operacje przechodzą do magazynu lokalnego i są synchronizowane tylko z zdalnym zapleczem z jawnymi operacjami wypychania i ściągania.

Aby uzyskać odwołanie do tabeli synchronizacji, użyj metody syncTableWithName w systemie MSClient. Aby usunąć funkcje synchronizacji w trybie offline, zamiast tego użyj polecenia tableWithName .

Aby można było wykonać dowolne operacje tabeli, magazyn lokalny musi zostać zainicjowany. Oto odpowiedni kod:

  • Objective-C. W metodzie QSTodoService.init :

    MSCoreDataStore *store = [[MSCoreDataStore alloc] initWithManagedObjectContext:context];
    self.client.syncContext = [[MSSyncContext alloc] initWithDelegate:nil dataSource:store callback:nil];
    
  • Swift. W metodzie 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)
    

    Ta metoda tworzy magazyn lokalny przy użyciu interfejsu MSCoreDataStore , który udostępnia zestaw SDK usługi Mobile Apps. Alternatywnie możesz podać inny magazyn lokalny, implementując MSSyncContextDataSource protokół. Ponadto pierwszy parametr msSyncContext służy do określania procedury obsługi konfliktów. Ponieważ upłynął błąd nil, otrzymujemy domyślną procedurę obsługi konfliktów, która kończy się niepowodzeniem w każdym konflikcie.

Teraz przeprowadźmy rzeczywistą operację synchronizacji i pobierzmy dane z zdalnego zaplecza:

  • Objective-C. syncData najpierw wypycha nowe zmiany, a następnie wywołuje metodę pullData , aby pobrać dane z zdalnego zaplecza. Z kolei metoda pullData pobiera nowe dane zgodne z zapytaniem:

    -(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()
        }
    }
    

W wersji Objective-C w syncDatasystemie najpierw wywołujemy funkcję pushWithCompletion w kontekście synchronizacji. Ta metoda jest elementem członkowskim MSSyncContext (a nie samej tabeli synchronizacji), ponieważ wypycha zmiany we wszystkich tabelach. Do serwera są wysyłane tylko rekordy, które zostały zmodyfikowane lokalnie (za pośrednictwem operacji CUD). Następnie wywoływana jest funkcja pullData pomocnika, która wywołuje metodę MSSyncTable.pullWithQuery w celu pobrania danych zdalnych i przechowywania ich w lokalnej bazie danych.

W wersji Swift, ponieważ operacja wypychania nie była ściśle konieczna, nie ma wywołania pushWithCompletion. Jeśli w kontekście synchronizacji tabeli, która wykonuje operację wypychania, w kontekście synchronizacji istnieją jakiekolwiek zmiany, ściąganie zawsze najpierw wystawia wypychanie. Jeśli jednak masz więcej niż jedną tabelę synchronizacji, najlepiej jest jawnie wywołać wypychanie, aby upewnić się, że wszystko jest spójne w powiązanych tabelach.

W wersjach Objective-C i Swift można użyć metody pullWithQuery , aby określić zapytanie w celu filtrowania rekordów, które chcesz pobrać. W tym przykładzie zapytanie pobiera wszystkie rekordy w tabeli zdalnej TodoItem .

Drugi parametr pullWithQuery to identyfikator zapytania używany do synchronizacji przyrostowej. Synchronizacja przyrostowa pobiera tylko rekordy, które zostały zmodyfikowane od ostatniej synchronizacji, przy użyciu sygnatury UpdatedAt czasowej rekordu (nazywanej updatedAt w magazynie lokalnym). Identyfikator zapytania powinien być ciągiem opisowym, który jest unikatowy dla każdego zapytania logicznego w aplikacji. Aby zrezygnować z synchronizacji przyrostowej, przekaż nil jako identyfikator zapytania. Takie podejście może być potencjalnie nieefektywne, ponieważ pobiera wszystkie rekordy w każdej operacji ściągania.

Aplikacja Objective-C synchronizuje się podczas modyfikowania lub dodawania danych, gdy użytkownik wykonuje gest odświeżania i podczas uruchamiania.

Aplikacja Swift synchronizuje się, gdy użytkownik wykonuje gest odświeżania i podczas uruchamiania.

Ponieważ aplikacja synchronizuje się za każdym razem, gdy dane są modyfikowane (Objective-C) lub za każdym razem, gdy aplikacja zostanie uruchomiona (Objective-C i Swift), aplikacja zakłada, że użytkownik jest w trybie online. W późniejszej sekcji zaktualizujesz aplikację, aby użytkownicy mogli edytować nawet wtedy, gdy są w trybie offline.

Przeglądanie podstawowego modelu danych

W przypadku korzystania z magazynu podstawowe dane w trybie offline należy zdefiniować określone tabele i pola w modelu danych. Przykładowa aplikacja zawiera już model danych z odpowiednim formatem. W tej sekcji omówimy te tabele, aby pokazać, jak są używane.

Otwórz plik QSDataModel.xcdatamodeld. Cztery tabele są zdefiniowane — trzy, które są używane przez zestaw SDK i jeden, który jest używany dla samych elementów do wykonania:

  • MS_TableOperations: śledzi elementy, które należy zsynchronizować z serwerem.
  • MS_TableOperationErrors: śledzi wszelkie błędy występujące podczas synchronizacji w trybie offline.
  • MS_TableConfig: śledzi czas ostatniej aktualizacji ostatniej operacji synchronizacji dla wszystkich operacji ściągania.
  • TodoItem: przechowuje elementy do wykonania. Kolumny systemowe utworzoneAt, zaktualizowanaAt i wersja są opcjonalnymi właściwościami systemu.

Uwaga

Zestaw SDK usługi Mobile Apps rezerwuje nazwy kolumn rozpoczynające się od "``". Nie należy używać tego prefiksu z innymi kolumnami niż kolumny systemowe. W przeciwnym razie nazwy kolumn są modyfikowane podczas korzystania z zaplecza zdalnego.

W przypadku korzystania z funkcji synchronizacji w trybie offline zdefiniuj trzy tabele systemowe i tabelę danych.

Tabele systemowe

MS_TableOperations

atrybuty tabeli MS_TableOperations

Atrybut Typ
identyfikator Liczba całkowita 64
Itemid Ciąg
properties Dane binarne
tabela Ciąg
tableKind Liczba całkowita 16

MS_TableOperationErrors

atrybuty tabeli MS_TableOperationErrors

Atrybut Typ
identyfikator Ciąg
operationId Liczba całkowita 64
properties Dane binarne
tableKind Liczba całkowita 16

MS_TableConfig

Atrybut Typ
identyfikator Ciąg
key Ciąg
Keytype Liczba całkowita 64
tabela Ciąg
wartość Ciąg

Tabela danych

TodoItem

Atrybut Typ Uwaga
identyfikator Ciąg, oznaczony jako wymagany Klucz podstawowy w magazynie zdalnym
kończenie Wartość logiczna Pole elementu do wykonania
tekst Ciąg Pole elementu do wykonania
createdAt Date (opcjonalnie) Mapowanie na właściwość systemową createdAt
updatedAt Date (opcjonalnie) Mapowanie na właściwość systemowa UpdatedAt
Wersja Ciąg (opcjonalnie) Służy do wykrywania konfliktów, mapowania na wersję

Zmienianie zachowania synchronizacji aplikacji

W tej sekcji zmodyfikujesz aplikację tak, aby nie była synchronizowana podczas uruchamiania aplikacji ani podczas wstawiania i aktualizowania elementów. Synchronizuje się tylko wtedy, gdy jest wykonywany przycisk gestu odświeżania.

Objective-C:

  1. W pliku QSTodoListViewController.m zmień metodę viewDidLoad , aby usunąć wywołanie na [self refresh] końcu metody . Teraz dane nie są synchronizowane z serwerem podczas uruchamiania aplikacji. Zamiast tego jest synchronizowana z zawartością magazynu lokalnego.

  2. W pliku QSTodoService.m zmodyfikuj addItem definicję, tak aby nie była synchronizowana po wstawieniu elementu. self syncData Usuń blok i zastąp go następującymi elementami:

    if (completion != nil) {
        dispatch_async(dispatch_get_main_queue(), completion);
    }
    
  3. Zmodyfikuj definicję , completeItem jak wspomniano wcześniej. Usuń blok dla self syncData elementu i zastąp go następującymi elementami:

    if (completion != nil) {
        dispatch_async(dispatch_get_main_queue(), completion);
    }
    

Swift:

W pliku w pliku ToDoTableViewController.swiftviewDidLoad oznacz dwa wiersze pokazane tutaj jako komentarz, aby zatrzymać synchronizację na początku aplikacji. W momencie pisania tego tekstu aplikacja Swift Todo nie aktualizuje usługi, gdy ktoś dodaje lub kończy element. Aktualizuje ona usługę tylko podczas uruchamiania aplikacji.

self.refreshControl?.beginRefreshing()
self.onRefresh(self.refreshControl)

Testowanie aplikacji

W tej sekcji nawiąż połączenie z nieprawidłowym adresem URL w celu symulowania scenariusza offline. Po dodaniu elementów danych są one przechowywane w lokalnym magazynie danych Core Data Store, ale nie są one synchronizowane z zapleczem aplikacji mobilnej.

  1. Zmień adres URL aplikacji mobilnej w QSTodoService.m na nieprawidłowy adres URL i uruchom ponownie aplikację:

    Objective-C. W pliku QSTodoService.m:

    self.client = [MSClient clientWithApplicationURLString:@"https://sitename.azurewebsites.net.fail"];
    

    Swift. W pliku ToDoTableViewController.swift:

    let client = MSClient(applicationURLString: "https://sitename.azurewebsites.net.fail")
    
  2. Dodaj niektóre elementy do wykonania. Zamknij symulator (lub wymuszone zamknięcie aplikacji), a następnie uruchom go ponownie. Sprawdź, czy zmiany są utrwalane.

  3. Wyświetl zawartość zdalnej tabeli TodoItem :

    • W przypadku zaplecza Node.js przejdź do Azure Portal, a następnie w zapleczu aplikacji mobilnej kliknij pozycję Łatwe tabele>TodoItem.
    • W przypadku zaplecza platformy .NET użyj narzędzia SQL, takiego jak SQL Server Management Studio, lub klienta REST, takiego jak Fiddler lub Postman.
  4. Sprawdź, czy nowe elementy nie zostały zsynchronizowane z serwerem.

  5. Zmień adres URL z powrotem na poprawny w QSTodoService.m i uruchom ponownie aplikację.

  6. Wykonaj gest odświeżania, ściągając listę elementów.
    Zostanie wyświetlony pokrętło postępu.

  7. Ponownie wyświetl dane todoItem . Teraz powinny być wyświetlane nowe i zmienione elementy do wykonania.

Podsumowanie

Aby obsługiwać funkcję synchronizacji w trybie offline, użyliśmy interfejsu MSSyncTable i zainicjowaliśmy MSClient.syncContext go przy użyciu magazynu lokalnego. W tym przypadku magazyn lokalny był bazą danych opartą na podstawowych danych.

W przypadku korzystania z magazynu lokalnego Core Data należy zdefiniować kilka tabel z poprawnymi właściwościami systemu.

Normalne operacje tworzenia, odczytu, aktualizowania i usuwania (CRUD) dla aplikacji mobilnych działają tak, jakby aplikacja nadal jest połączona, ale wszystkie operacje występują w sklepie lokalnym.

Podczas synchronizowania magazynu lokalnego z serwerem użyto metody MSSyncTable.pullWithQuery .

Dodatkowe zasoby