Habilitar a sincronização offline com aplicativos móveis do iOS
Visão geral
Este tutorial aborda a sincronização offline com o recurso Aplicativos Móveis do Serviço de Aplicativo do Azure para iOS. Com a sincronização offline, os usuários podem interagir com um aplicativo móvel para exibir, adicionar ou alterar dados, mesmo quando não têm conexão com a rede. As alterações são armazenadas em um banco de dados local. Quando o dispositivo estiver online novamente, as alterações são sincronizadas com o back-end remoto.
Se essa for sua primeira experiência com Aplicativos Móveis, você deve primeiro concluir o tutorial Criar um aplicativo iOS. Se você não usar o projeto baixado do início rápido do servidor, deve adicionar os pacotes de extensão de acesso de dados ao seu projeto. Para obter mais informações sobre pacotes de extensão do servidor, confira Trabalhar com o servidor .NET back-end do SDK para Aplicativos Móveis do Azure.
Para saber mais sobre o recurso de sincronização offline, confira Sincronização de dados offline em Aplicativos Móveis.
Examine o código de sincronização do cliente
O projeto cliente que você baixou para o tutorial Criar um aplicativo iOS já contém o código que oferece suporte à sincronização offline usando um banco de dados local baseado em Dados Básicos. Esta seção resume o que já está incluso no código do tutorial. Para obter uma visão geral conceitual do recurso, confira Sincronização de dados offline em Aplicativos Móveis.
Usando o recurso de sincronização de dados offline dos Aplicativos Móveis, os usuários podem interagir com um banco de dados local mesmo quando a rede estiver inacessível. Para usar esses recursos em seu aplicativo, inicialize o contexto de sincronização de MSClient
e faça referência a um repositório local. Em seguida, faça referência à sua tabela por meio da interface MSSyncTable.
Em QSTodoService.m (Objective-C) ou em ToDoTableViewController.swift (Swift), observe que o tipo do membro syncTable é MSSyncTable. A sincronização offline usa esta interface de tabela de sincronização em vez de MSTable. Ao usar uma tabela de sincronização, todas as operações vão para o armazenamento local e são sincronizadas somente com o back-end remoto com operações de push e pull explícitas.
Para obter uma referência a uma tabela de sincronização, use o método syncTableWithName no MSClient
. Para remover a funcionalidade de sincronização offline, use tableWithName.
Antes de qualquer operação de tabela poder ser executada, o armazenamento local deve ser inicializado. Este é o código relevante:
Objective-C. No método QSTodoService.init:
MSCoreDataStore *store = [[MSCoreDataStore alloc] initWithManagedObjectContext:context]; self.client.syncContext = [[MSSyncContext alloc] initWithDelegate:nil dataSource:store callback:nil];
Swift. No método 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)
Isso cria um repositório local usando a interface
MSCoreDataStore
, que é fornecida no SDK de Aplicativos Móveis. Como alternativa, é possível fornecer um repositório local diferente implementando o protocoloMSSyncContextDataSource
. Além disso, usa-se o primeiro parâmetro de MSSyncContext para especificar um manipulador de conflito. Já que passamosnil
, obteremos o manipulador de conflito padrão, que falha em qualquer conflito.
Agora vamos executar a operação de sincronização em si e obter dados do back-end remoto:
Objective-C. Primeiro,
syncData
envia novas alterações por push e chama pullData para obter dados do back-end remoto. Por sua vez, o método pullData obtém novos dados que correspondem a uma consulta:-(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() } }
Na versão do Objective-C, em syncData
, primeiro chamamos pushWithCompletion no contexto da sincronização. Esse método é um membro de MSSyncContext
(e não da tabela de sincronização em si) porque envia por push as alterações para todas as tabelas. Somente os registros que foram modificados localmente de alguma forma (por meio de operações CUD) são enviados ao servidor. Em seguida, o auxiliar pullData é chamado, o que chama MSSyncTable.pullWithQuery para recuperar os dados remotos e armazená-los no banco de dados local.
Na versão do Swift, porque a operação de envio não era estritamente necessária, não há nenhuma chamada para pushWithCompletion. Se houver alterações pendentes no contexto de sincronização para a tabela que está executando uma operação de push/pull, push é sempre emitido primeiro. No entanto, se você tiver mais de uma tabela de sincronização, é melhor chamar explicitamente por push para garantir que tudo esteja consistente em todas as tabelas relacionadas.
Em ambas as versões Objective-C e Swift, o método pullWithQuery permite que você especifique uma consulta para filtrar os registros que deseja recuperar. Neste exemplo, a consulta recupera todos os registros na tabela remota TodoItem
.
O segundo parâmetro de pullWithQuery é uma ID de consulta usada para sincronização incremental. A sincronização incremental recupera apenas os registros que foram modificados desde a última sincronização, usando o carimbo de data/hora do UpdatedAt
registro (chamado updatedAt
no repositório local). A ID da consulta deve ser uma cadeia de caracteres descritiva exclusiva para cada consulta lógica em seu aplicativo. Se desejar sair da sincronização incremental, passe nil
como a ID da consulta. Observe que isso pode ser potencialmente ineficiente, já que recupera todos os registros de cada operação de recepção.
O aplicativo Objective-C é sincronizado ao modificar ou adicionar dados, quando um usuário executa a atualização e na inicialização.
O aplicativo Swift é sincronizado quando um usuário executa a atualização e na inicialização.
Como o aplicativo é sincronizado sempre que dados são modificados (Objective-C) ou sempre que o aplicativo é iniciado (Objective-C e Swift), o aplicativo pressupõe que o usuário está online. Em uma seção posterior, atualizaremos o aplicativo para que os usuários possam editar mesmo quando estiverem offline.
Examinar o modelo de dados principais
Ao usar o armazenamento offline de Dados Básicos, você precisa definir determinadas tabelas e campos em seu modelo de dados. O aplicativo de exemplo já inclui um modelo de dados com o formato correto. Nesta seção percorreremos essas tabelas e veremos como são usadas.
Abra QSDataModel.xcdatamodeld. Há quatro tabelas definidas - três que são usadas pelo SDK e uma usada para os itens pendentes:
- MS_TableOperations: para acompanhar os itens que devem ser sincronizados com o servidor.
- MS_TableOperationErrors: para acompanhar todos os erros que ocorrerem durante a sincronização offline.
- MS_TableConfig: para controlar a hora da última atualização para a última operação de sincronização para todas as operações de recepção.
- TodoItem: armazena os itens de tarefas pendentes. As colunas do sistema createdAt, updatedAt e version são propriedades opcionais do sistema.
Observação
O SDK dos Aplicativos Móveis reserva nomes de coluna que começam com "``". Não use esse prefixo em algo diferente das colunas do sistema. Caso contrário, os nomes de coluna serão modificados ao usar o back-end remoto.
Ao usar o recurso de sincronização offline, você define as três tabelas do sistema e a tabela de dados.
Tabelas do sistema
MS_TableOperations
Atributo | Tipo |
---|---|
id | Número Inteiro 64 |
itemId | String |
properties | Binary Data |
tabela | String |
tableKind | Número inteiro 16 |
MS_TableOperationErrors
Atributo | Tipo |
---|---|
id | String |
operationId | Número Inteiro 64 |
properties | Binary Data |
tableKind | Número inteiro 16 |
MS_TableConfig
Atributo | Tipo |
---|---|
id | String |
chave | String |
keyType | Número Inteiro 64 |
tabela | String |
value | String |
Tabela de dados
TodoItem
Atributo | Type | Observação |
---|---|---|
id | Cadeia de caracteres, marcadas como obrigatórias | Chave primária no repositório remoto |
concluído | Boolean | Campo To-do item |
text | String | Campo To-do item |
createdAt | Data | (opcional) É mapeado para a propriedade do sistema createdAt |
updatedAt | Data | (opcional) É mapeado para a propriedade do sistema updatedAt |
version | String | (opcional) Usado para detectar conflitos, é mapeado para a versão |
Alterar o comportamento de sincronização do aplicativo
Nesta seção, você altera o aplicativo para que ele não sincronize na inicialização ou ao inserir e atualizar itens. Ele sincroniza somente quando o botão de atualização é executado.
Objective-C:
Em QSTodoListViewController.m, altere o método viewDidLoad para remover a chamada para
[self refresh]
no final do método. Agora os dados não são sincronizados com o servidor na inicialização do aplicativo. Em vez disso, são sincronizados com o conteúdo do repositório local.Em QSTodoService.m, modifique a definição de
addItem
para que não ocorra sincronização após o item ser inserido. Remova o blocoself syncData
e substitua-o pelo seguinte:if (completion != nil) { dispatch_async(dispatch_get_main_queue(), completion); }
Modifique a definição de
completeItem
como mencionado anteriormente. Remova o blocoself syncData
e substitua-o pelo seguinte:if (completion != nil) { dispatch_async(dispatch_get_main_queue(), completion); }
Swift:
Em viewDidLoad
de ToDoTableViewController.swift, comente essas duas linhas para parar a sincronização na inicialização do aplicativo. No momento da redação deste artigo, o aplicativo Todo do Swift não atualiza o serviço quando alguém adiciona ou conclui um item, somente na inicialização do aplicativo.
self.refreshControl?.beginRefreshing()
self.onRefresh(self.refreshControl)
Testar o aplicativo
Nesta seção, você se conecta a uma URL inválida para simular um cenário offline. Ao adicionar itens de dados, eles serão mantidos no repositório local de Dados Básicos, mas não serão sincronizados com o back-end do aplicativo móvel.
Altere a URL do aplicativo móvel em QSTodoService.m para uma URL inválida e execute novamente o aplicativo:
Objective-C. Em QSTodoService.m:
self.client = [MSClient clientWithApplicationURLString:@"https://sitename.azurewebsites.net.fail"];
Swift. Em ToDoTableViewController.swift:
let client = MSClient(applicationURLString: "https://sitename.azurewebsites.net.fail")
Adicione alguns itens pendentes. Feche o simulador (ou force o fechamento do aplicativo) e reinicie. Verifique se suas mudanças foram mantidas.
Exiba o conteúdo da tabela Remota TodoItem :
- Para um back-end do Node.js, vá para o Portal do Azure e, no back-end de seu aplicativo móvel, clique em Tabelas simples>TodoItem.
- Para um back-end do .NET, use uma ferramenta SQL, como o SQL Server Management Studio, ou um cliente REST, como o Fiddler ou o Postman.
Confirme que os novos itens não tenham sido sincronizados com o servidor.
Altere a URL novamente para a correta em QSTodoService.m e execute o aplicativo outra vez.
Faça o gesto de atualização puxando a lista de itens para baixo.
Um controle giratório de progresso é exibido.Exiba os dados do TodoItem novamente. Os itens de tarefas novos e modificados agora devem ser exibidos.
Resumo
Para dar suporte a esse recurso de sincronização offline, usamos a interface MSSyncTable
e inicializamos MSClient.syncContext
com um repositório local. Nesse caso, o repositório local era um banco de dados baseado em Dados Básicos.
Ao usar um repositório local de Dados Básicos, você deve definir várias tabelas com as propriedades corretas do sistema.
As operações CRUD (criar, ler, atualizar e excluir) normais nos aplicativos móveis funcionam como se o aplicativo ainda estivesse conectado, mas todas as operações ocorrem no repositório local.
Ao sincronizar o repositório local com o servidor, usamos o método MSSyncTable.pullWithQuery.
Recursos adicionais
- Sincronização de dados offline nos Aplicativos Móveis
- Cloud Cover: Sincronização offline nos Serviços Móveis do Azure (o vídeo é sobre os Serviços Móveis, mas a sincronização offline dos Aplicativos Móveis funciona de maneira semelhante.)