Menu Docs
Página inicial do Docs
/ /
Atlas Device SDKs
/ /

Gravar dados em um domínio sincronizado - Swift SDK

Nesta página

  • Visão geral
  • Determinar quais dados são sincronizados
  • Configuração do App Services
  • Modelo de dados Realm e configuração do cliente
  • Quais dados são sincronizados?
  • Escreva para um domínio sincronizado
  • Gravações bem-sucedidas
  • Compensação de escritas
  • Informações de erro de gravação compensatórias
  • Gravações que não correspondem à assinatura da query
  • Gravações que não correspondem às permissões
  • Escritas em grupo para melhorar o desempenho
  • Não escreva para um Realm sincronizado em uma extensão de aplicativo
  • Falhas relacionadas à abertura de um Realm sincronizado em vários processos
  • Alternativas para escrever em um Realm sincronizado em uma extensão de aplicativo
  • Passar dados no disco
  • Comunique-se diretamente com a collection de apoio Atlas

Ao gravar dados em um Realm sincronizado usando o Flexible Sync, você pode usar a mesma API que ao gravar em um Realm local. No entanto, há algumas diferenças de comportamento que devem ser levadas em conta ao desenvolver seu aplicativo.

Quando você escreve em um Realm sincronizado, suas operações de escrita devem corresponder aos dois itens a seguir:

  • A query de assinatura de sincronização.
    • Se sua operação de gravação não corresponder à query na assinatura, a gravação será revertida com um erro de gravação compensatória não fatal (ErrorCompensatingWrite).

  • As permissões em seu App Services App.
    • Se você tentar gravar dados que não correspondem à expressão de permissões, a gravação será revertida com um erro de permissão negada não fatal. No cliente, isso aparece como um erro (ErrorCompensatingWrite). No servidor, você pode ver mais detalhes sobre como a gravação foi negada por um filtro de escrita na role.

    • To learn more about configuring permissions for your app, see Role-based Permissions and the Device Sync Permissions Guide in the App Services documentation.

Aviso

A sincronização de vários processos não é suportada

Atualmente, o Device Sync não permite a abertura ou gravação em um domínio sincronizado a partir de mais de um processo. Para obter mais informações, incluindo alternativas sugeridas, consulte: Não escreva para um domínio Realm em uma extensão de aplicativo.

Os dados que você pode gravar em um Realm sincronizado são a interseção da configuração do Realm Mobile Sync, suas permissões e a query de assinatura do Flexible Sync que você usa quando abre o Realm.

Os exemplos nesta página usam as seguintes configurações e modelos:

O Realm Mobile Sync é configurado com os seguintes campo de query:

  • _id (Sempre incluído)

  • complexity

  • ownerId

O App Services App tem permissões configuradas para permitir que os usuários leiam e escrevam somente seus próprios dados:

{
"name": "owner-read-write",
"apply_when": {},
"document_filters": {
"read": { "ownerId": "%%user.id" },
"write": { "ownerId": "%%user.id" }
},
"read": true,
"write": true
}

Os exemplos nesta página usam o seguinte modelo de objeto:

class Item: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var ownerId: String
@Persisted var itemName: String
@Persisted var complexity: Int
}

Utilizando esse modelo de objetos, a configuração do Realm sincronizado sincroniza objeto que correspondem à query onde o valor da propriedade complexity é menor ou igual a 4:

let app = App(id: YOUR_APP_ID_HERE)
do {
let user = try await app.login(credentials: Credentials.anonymous)
do {
var flexSyncConfig = user.flexibleSyncConfiguration()
flexSyncConfig.objectTypes = [Item.self]
let realm = try await Realm(configuration: flexSyncConfig)
let subscriptions = realm.subscriptions
try await subscriptions.update {
subscriptions.append(
QuerySubscription<Item>(name: "simple-items") {
$0.complexity <= 4
})
}
print("Successfully opened realm: \(realm)")
} catch {
print("Failed to open realm: \(error.localizedDescription)")
// handle error
}
} catch {
fatalError("Login failed: \(error.localizedDescription)")
}

A query de assinatura combinada com as permissões significa que o domínio sincronizado sincroniza apenas objetos em que:

  • O ownerId corresponde ao user.id do usuário conectado (a partir das permissões)

  • O valor da propriedade complexity é menor ou igual a 4 (da query de assinatura)

Qualquer objeto na Atlas collection em que ownerId não corresponda ao user.id do usuário conectado ou em que o valor da propriedade complexity seja maior que 4 não pode ser sincronizado com esse Realm.

As gravações nos domínios do Flexible Sync podem se enquadrar em uma de duas categorias:

  • Gravações bem- sucedidas: o objeto gravado corresponde à assinatura da query e às permissões do usuário. O objeto é gravado com êxito no Realm e sincroniza com êxito com o backend do App Services e outros dispositivos.

  • Escritas compensatórias: quando o objeto de escrita não corresponde à query de assinatura, ou quando o usuário não tem permissões suficientes para realizar a escrita, o Realm reverte a escrita ilegal.

Quando a gravação corresponde às permissões e à query de inscrição do Flexible Sync no cliente, o Realm Swift SDK pode gravar o objeto com êxito no domínio sincronizado. Este objeto é sincronizado com o backend do Atlas App Services quando o dispositivo tem uma conexão de rede.

// This write falls within the subscription query and complies
// with the Device Sync permissions, so this write should succeed.
do {
let learnRealm = Item()
learnRealm.ownerId = user.id
learnRealm.itemName = "Learn Realm CRUD stuff"
learnRealm.complexity = 3
try realm.write {
realm.add(learnRealm)
}
} catch {
print("Failed to write to realm: \(error.localizedDescription)")
}

Em alguns casos, uma escrita que inicialmente parece bem-sucedida é, na verdade, uma escrita ilegal. Nesses casos, o objeto grava no reconhecimento de data center, mas quando o reconhecimento de data center sincroniza com o backend, o Realm reverte a escrita em uma operação de erro não fatal chamada escrita compensatória. As gravações compensatórias podem ocorrer quando:

  • As gravações não correspondem à assinatura da query: O objeto gravado corresponde às permissões do usuário, mas não corresponde à assinatura da query.

  • As gravações não correspondem às permissões: o objeto gravado corresponde à assinatura da query, mas não corresponde às permissões do usuário.

Em mais detalhes, quando você grava dados que estão fora dos limites de uma assinatura de query ou não correspondem às permissões do usuário, ocorre o seguinte:

  1. Como o Realm do cliente não tem nenhum conceito de gravações "ilegais", a gravação inicialmente é bem-sucedida até que o Realm resolva o conjunto de alterações com o backend do App Services.

  2. Após a sincronização, o servidor aplica as regras e permissões. O servidor determina que o usuário não tem autorização para realizar a gravação.

  3. O servidor envia uma operação de reversão, chamada de "escrita compensatória", de volta ao cliente.

  4. O Realm do cliente reverte a operação de escrita ilegal.

Qualquer escrita do lado do cliente em um determinado objeto entre uma escrita ilegal nesse objeto e a escrita compensatória correspondente será perdida.

Na prática, isso pode parecer um objeto sendo gravado no Realm e desaparecer depois que o servidor enviar a gravação compensatória de volta ao cliente.

Para saber mais sobre erros de permissão negada, erros de gravação compensatórios e outros tipos de erro do Device Sync , consulte Erros de sincronização na documentação do Atlas App Services .

Os registrosAtlas App Services contêm mais informações sobre o motivo de um erro de gravação compensatório.

Novidade na versão 10.37.0 .

Você pode obter informações adicionais no cliente sobre o motivo de ocorrer uma gravação compensatória. O Swift SDK expõe um campo compensatingWriteInfo em um SyncError cujo código é .writeRejected. Você pode acessar essas informações por meio do manipulador de erros de sincronização.

Este campo contém uma array de objetos RLMCompensatingWriteInfo , que fornecem:

  • O objectType do objeto que o cliente tentou escrever

  • O primaryKey do objeto específico

  • O reason para o erro de gravação compensatória

Essas informações são as mesmas que você pode encontrar nos registros do App Services. Ele é exposto no cliente para fins de conveniência e depuração.

Exemplo

Este manipulador de erros mostra um exemplo de como você pode registrar informações para compensar erros de escrita:

myApp.syncManager.errorHandler = { syncError, session in
if let thisError = syncError as? SyncError {
switch thisError.code {
// ... additional SyncError.code cases ...
case .writeRejected:
if let compensatingWriteErrorInfo = thisError.compensatingWriteInfo {
for anError in compensatingWriteErrorInfo {
print("A write was rejected with a compensating write error")
print("The write to object type: \(anError.objectType)")
print("With primary key of: \(anError.primaryKey)")
print("Was rejected because: \(anError.reason)")
}
}
}
}
}

O exemplo de manipulador de erros acima produz essa saída quando ocorre um erro de gravação compensatório:

A write was rejected with a compensating write error
The write to object type: Optional("Item")
With primary key of: objectId(641382026852d9220b2e2bbf)
Was rejected because: Optional("write to \"641382026852d9220b2e2bbf\" in table \"Item\" not
allowed; object is outside of the current query view")
  • O Optional("Item") nesta mensagem é um Item objeto usado no aplicativo Swift Template.

  • A chave primária é o objectId do objeto específico que o cliente tentou escrever.

  • O table \"Item"\ refere-se à collection Atlas onde este objeto sincronizaria.

  • O motivo object is outside of the current query view nesse exemplo é porque a assinatura de query foi definida para exigir que a propriedade isComplete do objeto fosse true, e o cliente tentou gravar um objeto em que essa propriedade era false.

Você só pode gravar objeto em um Realm do Flexible Sync se eles corresponderem à query. Se você executar uma gravação que não corresponda à query de assinatura, o Realm gravará inicialmente o objeto, mas depois executará uma gravação compensatória. Esta é uma operação não fatal que reverte uma gravação ilegal que não corresponde à query de assinatura.

Na prática, isso pode parecer que a gravação foi bem-sucedida, mas o objeto "desaparece" quando o Realm sincroniza com o backend do App Services e executa a gravação compensatória.

Se você quiser gravar um objeto que não corresponde à query, deverá abrir um Realm diferente onde o objeto corresponde à query. Como alternativa, você pode gravar o objeto em um domínio não sincronizado que não impõe permissões ou consultas de assinatura.

Dada a configuração do Realm Flexible Sync acima, a tentativa de gravar esse objeto não corresponde à query subscription:

do {
let fixTheBug = Item()
fixTheBug.ownerId = user.id
fixTheBug.itemName = "Fix the bug with the failing method"
// The complexity of this item is `7`. This is outside the bounds
// of the subscription query, so this write triggers a compensating write.
fixTheBug.complexity = 7
try realm.write {
realm.add(fixTheBug)
}
} catch {
print("Failed to write to realm: \(error.localizedDescription)")
}

A mensagem de erro nos registros do lado do cliente nesse cenário é:

Sync: Connection[1]: Session[1]: Received: ERROR "Client attempted a
write that is outside of permissions or query filters; it has been
reverted" (error_code=231, try_again=true, error_action=Warning)

A mensagem de erro nos registros do App Services nesse cenário é:

"FlexibleSync_Item": {
"63bdfc40f16be7b1e8c7e4b7": "write to \"63bdfc40f16be7b1e8c7e4b7\"
in table \"FlexibleSync_Item\" not allowed; object is outside of
the current query view"
}

A tentativa de gravar no cliente também pode trigger um erro de gravação compensatório quando o objeto não corresponde às permissões de gravação no servidor do usuário.

No cliente, esse tipo de gravação se comporta da mesma forma que uma gravação que não corresponde à assinatura da query. Na prática, isso pode parecer que a gravação foi bem-sucedida, mas o objeto "desaparece" quando o Realm sincroniza com o backend do App Services e executa a gravação compensatória.

Dadas as permissões na Configuração do Realm Mobile Sync detalhadas acima, tentar gravar um objeto em que a propriedade ownerId não corresponde ao user.id do usuário conectado não é uma gravação legal:

do {
let itemWithWrongOwner = Item()
// The `ownerId` of this item does not match the `user.id` of the logged-in
// user. The user does not have permissions to make this write, so
// it triggers a compensating write.
itemWithWrongOwner.ownerId = "This string does not match the user.id"
itemWithWrongOwner.itemName = "Write code that generates a permission error"
itemWithWrongOwner.complexity = 1
try realm.write {
realm.add(itemWithWrongOwner)
}
} catch {
print("Failed to write to realm: \(error.localizedDescription)")
}

O erro do cliente nesse cenário é o mesmo de quando você tenta gravar um objeto que está fora do filtro de query:

Sync: Connection[1]: Session[1]: Received: ERROR "Client attempted a
write that is outside of permissions or query filters; it has been
reverted" (error_code=231, try_again=true, error_action=Warning)

A mensagem de erro nos registros do App Services fornece algumas informações adicionais para ajudá-lo a determinar que é um problema de permissões e não um problema de assinatura de query. Neste exemplo, a mensagem de erro mostra que o objeto não corresponde à role do usuário:

"FlexibleSync_Item": {
"63bdfc40f16be7b1e8c7e4b8": "write to \"63bdfc40f16be7b1e8c7e4b8\"
in table \"FlexibleSync_Item\" was denied by write filter in role
\"owner-read-write\""
}

Cada transação de escrita para um conjunto de assinaturas tem um custo de desempenho. Se você precisar fazer várias atualizações em um objeto do Realm durante uma sessão, considere manter objetos editados na memória até que todas as alterações sejam concluídas. Isso melhora o desempenho da sincronização, gravando apenas o objeto completo e atualizado em seu domínio, em vez de cada alteração.

Se você estiver desenvolvendo um aplicativo que usa extensões de aplicativo, como uma extensão de compartilhamento, evite gravar em um Realm sincronizado nessa extensão. O Realm Mobile Sync oferece suporte à abertura de um Realm sincronizado em no máximo um processo. Na prática, isso significa que, se o seu aplicativo usar um Realm em uma Extensão de Aplicativo, ele poderá falhar de forma intermitente.

Se você tentar abrir um Realm sincronizado em uma Extensão de Compartilhamento ou outra Extensão de Aplicativo, e esse Realm não estiver aberto no aplicativo principal, uma gravação de uma Extensão de Compartilhamento poderá ser bem-sucedida. No entanto, se o Realm sincronizado já estiver aberto no aplicativo principal ou estiver sincronizando dados em segundo plano, você poderá ver uma falha relacionada a Realm::MultiSyncAgents. Nesse cenário, talvez seja necessário reiniciar o dispositivo.

Se você precisar ler ou gravar em um Realm a partir de uma extensão de aplicativo, existem algumas alternativas recomendadas:

  • Offline-first: passe dados no disco de ou para o aplicativo principal

  • Sempre atualizado : comunique-se diretamente com a collection de apoio do Atlas por meio de uma conexão de rede

Se a funcionalidade offline-first for a consideração mais importante para sua aplicação, você poderá passar dados em disco de ou para sua aplicação principal. Você pode copiar objeto para um Realm não sincronizado e lê-lo e compartilhá-lo entre aplicativos em um grupo de aplicativos. Ou você pode usar uma fila no disco para enviar os dados de ou para o aplicativo principal e somente escrever no Realm sincronizado a partir daqui. Em seguida, independentemente da conectividade de rede do dispositivo, as informações podem ser compartilhadas a qualquer momento de ou para a extensão de aplicativo.

Se ter as informações sempre atualizadas em todos os dispositivos é a consideração mais importante para o seu aplicativo, você pode ler ou gravar dados diretamente de ou para a coleção de apoio do Atlas em toda a rede. Dependendo de suas necessidades, você pode querer usar uma destas ferramentas para se comunicar diretamente com Atlas:

Em seguida, qualquer dispositivo que tenha uma conexão de rede está sempre obtendo as informações mais atualizadas, sem esperar que o usuário abra seu aplicativo principal, como na opção acima.

Essa opção exige que o dispositivo do usuário tenha uma conexão de rede ao usar a extensão de aplicativo. Como alternativa, você pode verificar se há uma conexão de rede. Em seguida, use a opção no disco acima caso o dispositivo do usuário não tenha conectividade de rede.

Voltar

Gerenciar assinaturas de sincronização flexível

Próximo

Gerenciar erros de sincronização