30 avr. 2025·8 min de lecture

Synchronisation en arrière-plan pour applications mobiles (offline-first) : conflits, réessais et UX

Planifiez la synchronisation en arrière-plan d'une appli mobile offline-first avec des règles de conflit claires, une logique de réessai et une UX simple pour les modifications en attente, pour des apps natives Kotlin et SwiftUI.

Synchronisation en arrière-plan pour applications mobiles (offline-first) : conflits, réessais et UX

Le problème : les utilisateurs modifient hors-ligne et la réalité change

Quelqu'un commence une tâche avec une bonne connexion, puis entre dans un ascenseur, un coin d'entrepôt ou un tunnel de métro. L'application continue de fonctionner, donc ils continuent de travailler. Ils appuient sur Enregistrer, ajoutent une note, changent un statut, peut‑être créent même un nouvel enregistrement. Tout semble correct parce que l'écran se met à jour immédiatement.

Plus tard, la connexion revient et l'app essaie de rattraper son retard en arrière-plan. C'est là que la synchronisation en arrière-plan peut surprendre.

Si l'app n'est pas prudente, la même action peut être envoyée deux fois (doublons), ou une modification plus récente côté serveur peut écraser ce que l'utilisateur vient de faire (modifications perdues). Parfois l'app affiche des états déroutants comme « Enregistré » et « Non enregistré » en même temps, ou un enregistrement apparaît, disparaît, puis réapparaît après la synchro.

Un conflit est simple : deux modifications différentes ont été faites sur la même chose avant que l'app n'ait eu le temps de les réconcilier. Par exemple, un agent support change la priorité d'un ticket en « Haute » en étant hors-ligne, tandis qu'un collègue en ligne clôt le ticket. Quand le téléphone hors-ligne se reconnecte, les deux changements ne peuvent pas être appliqués proprement sans règle.

L'objectif n'est pas de rendre le hors-ligne parfait. L'objectif est de le rendre prévisible :

  • Les gens peuvent continuer à travailler sans peur de perdre leur travail.
  • La synchro arrive plus tard sans doublons mystérieux.
  • Quand quelque chose demande attention, l'app explique clairement ce qui s'est passé et quoi faire ensuite.

Ceci vaut que vous codiez en Kotlin/SwiftUI ou que vous construisiez des applis natives avec une plateforme no-code comme AppMaster. La partie difficile n'est pas les widgets UI. C'est de décider comment l'app se comporte quand le monde change pendant que l'utilisateur est hors-ligne.

Un modèle offline-first simple (sans jargon)

Une appli offline-first suppose que le téléphone perdra parfois le réseau, mais que l'app doit rester utilisable. Les écrans doivent se charger et les boutons doivent fonctionner même si le serveur est inaccessible.

Quatre termes couvrent la plupart des cas :

  • Cache local : données stockées sur l'appareil pour afficher quelque chose instantanément.
  • File de sync : liste d'actions que l'utilisateur a faites en étant hors-ligne (ou quand le réseau était instable).
  • Vérité serveur : la version stockée côté backend que tout le monde partage finalement.
  • Conflit : quand la modification en file de l'utilisateur ne s'applique plus proprement parce que la version serveur a changé.

Un modèle mental utile est de séparer les lectures des écritures.

Les lectures sont généralement simples : afficher la meilleure donnée disponible (souvent depuis le cache local), puis rafraîchir discrètement quand le réseau revient.

Les écritures sont différentes. Ne comptez pas sur « sauvegarder tout l'enregistrement » en une seule fois. Ça casse dès que vous êtes hors-ligne.

Au lieu de cela, enregistrez ce que l'utilisateur a fait comme de petites entrées dans un journal de changements. Par exemple : « définir le statut sur Approuvé », « ajouter le commentaire X », « changer la quantité de 2 à 3 ». Chaque entrée va dans la file de sync avec un horodatage et un ID. La synchronisation en arrière-plan essaie ensuite de la livrer.

L'utilisateur continue de travailler pendant que les changements passent de en attente à synchronisés.

Si vous utilisez une plateforme no-code comme AppMaster, vous voulez toujours les mêmes briques : lectures en cache pour des écrans rapides, et une file claire d'actions utilisateur qui peut être réessayée, fusionnée ou signalée en cas de conflit.

Décidez ce qui doit vraiment fonctionner hors-ligne

Offline-first peut sonner comme « tout fonctionne sans connexion », mais cette promesse est souvent le point où beaucoup d'apps se plantent. Choisissez les parties qui bénéficient vraiment du hors-ligne, et laissez le reste clairement en ligne uniquement.

Pensez en termes d'intention utilisateur : que doivent faire les personnes dans une cave, dans un avion ou dans un entrepôt avec un réseau instable ? Un bon défaut est de supporter les actions qui créent ou modifient le travail quotidien, et de bloquer les actions où la « vérité la plus récente » est cruciale.

Un ensemble pratique d'actions adaptées au hors-ligne inclut souvent la création et l'édition d'enregistrements centraux (notes, tâches, inspections, tickets), la rédaction de commentaires et l'ajout de photos (stockées localement, uploadées plus tard). La suppression peut aussi fonctionner, mais en sécurité via une suppression douce avec une fenêtre d'annulation jusqu'à confirmation serveur.

Décidez maintenant ce qui doit rester en temps réel parce que le risque est trop élevé. Paiements, changements de permissions, validations d'approbation et tout ce qui implique des données sensibles devraient généralement nécessiter une connexion. Si l'utilisateur ne peut pas vérifier la validité d'une action sans contacter le serveur, ne l'autorisez pas hors-ligne. Affichez un message clair « nécessite connexion », pas une erreur mystérieuse.

Mettez des attentes sur la fraîcheur. « Hors-ligne » n'est pas binaire. Définissez combien les données peuvent être périmées : minutes, heures, ou « la prochaine ouverture de l'app ». Indiquez cette règle en clair dans l'UI, par exemple « Dernière mise à jour il y a 2 heures » et « Synchronisation quand en ligne ».

Enfin, signalez tôt les données à risque de conflit. Les comptes d'inventaire, les tâches partagées et les messages d'équipe attirent souvent les conflits car plusieurs personnes les modifient rapidement. Pour ceux-ci, envisagez de limiter les éditions hors-ligne à des brouillons, ou de capturer les changements comme des événements séparés au lieu d'écraser une seule valeur.

Si vous construisez dans AppMaster, cette étape de décision vous aide à modéliser les données et règles métier pour que l'app puisse stocker des brouillons sûrs hors-ligne tout en gardant les actions risquées en ligne uniquement.

Concevoir la file de sync : ce que vous stockez pour chaque changement

Quand un utilisateur travaille hors-ligne, ne tentez pas de « synchroniser la base de données ». Synchronisez les actions de l'utilisateur. Une file d'actions claire est l'épine dorsale de la synchronisation en arrière-plan, et elle reste compréhensible quand quelque chose tourne mal.

Gardez les actions petites et compréhensibles, alignées avec ce que l'utilisateur a réellement fait :

  • Créer un enregistrement
  • Mettre à jour un ou plusieurs champs spécifiques
  • Changer de statut (soumettre, approuver, archiver)
  • Supprimer (de préférence suppression douce jusqu'à confirmation)

Les petites actions sont plus faciles à déboguer. Si le support doit aider un utilisateur, il est beaucoup plus simple de lire « Changement de statut Brouillon -> Soumis » que d'inspecter un énorme blob JSON modifié.

Pour chaque action en file, stockez assez de métadonnées pour la rejouer en sécurité et détecter les conflits :

  • Identifiant de l'enregistrement (et un ID local temporaire pour les nouveaux enregistrements)
  • Horodatage de l'action et identifiant de l'appareil
  • Version attendue (ou dernier horodatage connu) de l'enregistrement
  • Payload (les champs spécifiques modifiés, plus la valeur ancienne si possible)
  • Clé d'idempotence (un ID d'action unique afin que les réessais ne créent pas de doublons)

Cette version attendue est la clé d'un traitement honnête des conflits. Si la version serveur a avancé, vous pouvez mettre en pause et demander une décision au lieu d'écraser silencieusement quelqu'un d'autre.

Certaines actions doivent être appliquées ensemble parce que l'utilisateur les voit comme une seule étape. Par exemple, « Créer une commande » plus « Ajouter trois lignes » doit réussir ou échouer en bloc. Stockez un ID de groupe (ou ID de transaction) pour que le moteur de sync puisse les envoyer ensemble et valider tout ou garder tout en attente.

Que vous construisiez à la main ou dans AppMaster, le but est le même : chaque changement est enregistré une fois, rejoué en sécurité et explicable quand quelque chose ne correspond pas.

Règles de résolution de conflit que vous pouvez expliquer aux utilisateurs

Passez au natif sans tout réécrire
Utilisez des sorties natives Kotlin et SwiftUI sans réécrire tout votre code, tout en gardant une source de vérité unique.
Commencer

Les conflits sont normaux. Le but n'est pas de les rendre impossibles. Le but est de les rendre rares, sûrs et faciles à expliquer quand ils arrivent.

Nommez le moment où un conflit survient : l'app envoie une modification et le serveur répond « Cet enregistrement n'est pas la version que vous aviez commencé à modifier ». C'est pourquoi la gestion des versions importe.

Gardez deux valeurs avec chaque enregistrement :

  • Version serveur (la version actuelle sur le serveur)
  • Version attendue (la version que le téléphone croyait modifier)

Si la version attendue correspond, acceptez la mise à jour et incrémentez la version serveur. Sinon, appliquez votre règle de conflit.

Choisir une règle par type de donnée (pas une règle unique pour tout)

Différentes données nécessitent des règles différentes. Un champ statut n'est pas la même chose qu'une longue note.

Règles que les utilisateurs comprennent souvent :

  • Dernière écriture gagne : acceptable pour des champs peu risqués comme une préférence d'affichage.
  • Fusion de champs : meilleur quand les champs sont indépendants (statut vs notes).
  • Demander à l'utilisateur : idéal pour des modifications sensibles comme un prix, des permissions ou des totaux.
  • Serveur gagne avec copie : garder la valeur serveur, mais sauvegarder la modification utilisateur comme brouillon réappliquable.

Dans AppMaster, ces règles se traduisent bien en logique visuelle : vérifier les versions, comparer les champs, puis choisir la voie.

Décidez comment se comportent les suppressions (ou vous perdrez des données)

Les suppressions sont le cas délicat. Utilisez une tombe (tombstone, un marqueur "supprimé") au lieu de supprimer l'enregistrement immédiatement. Ensuite, décidez ce qui arrive si quelqu'un édite un enregistrement qui a été supprimé ailleurs.

Une règle claire : « Les suppressions gagnent, mais vous pouvez restaurer. » Exemple : un commercial édite une note client hors-ligne tandis qu'un admin supprime ce client. Quand la sync s'exécute, l'app affiche « Le client a été supprimé. Restaurer pour appliquer votre note ? » Cela évite la perte silencieuse et laisse le contrôle à l'utilisateur.

Réessais et états d'échec : restez prévisible

Quand la sync échoue, la plupart des utilisateurs ne se soucient pas de la raison. Ils veulent savoir si leur travail est en sécurité et ce qui se passera ensuite. Un ensemble d'états prévisible évite la panique et les tickets support.

Commencez avec un modèle d'états visible simple et gardez-le cohérent sur tous les écrans :

  • Queued : sauvegardé sur l'appareil, en attente de réseau
  • Syncing : en cours d'envoi
  • Sent : confirmé par le serveur
  • Failed : n'a pas pu être envoyé, sera réessayé ou nécessite une action
  • Needs review : envoyé, mais le serveur a rejeté ou signalé l'élément

Les réessais doivent préserver la batterie et les données. Utilisez des réessais rapides au départ (pour gérer de brefs décrochages), puis ralentissez. Un backoff simple comme 1 min, 5 min, 15 min, puis toutes les heures est facile à raisonner. Réessayez aussi seulement quand cela a du sens (ne relancez pas en boucle une action invalide).

Traitez les erreurs différemment, car l'action suivante diffère :

  • Hors-ligne / pas de réseau : reste en file, réessayer à la mise en ligne
  • Timeout / serveur indisponible : marquer comme failed, réessayer automatiquement avec backoff
  • Auth expirée : mettre la sync en pause et demander à l'utilisateur de se reconnecter
  • Validation échouée (donnée invalide) : needs review, afficher quoi corriger
  • Conflit (l'enregistrement a changé) : needs review, appliquer vos règles de conflit

L'idempotence est ce qui rend les réessais sûrs. Chaque changement doit avoir un ID d'action unique (souvent un UUID) envoyé avec la requête. Si l'app renvoie la même modification, le serveur doit reconnaître l'ID et renvoyer le même résultat au lieu de créer des doublons.

Exemple : un technicien enregistre un travail terminé hors-ligne, puis entre dans un ascenseur. L'app envoie la mise à jour, le call timeoute, et la relance plus tard. Avec un ID d'action, le deuxième envoi est sans conséquence. Sans cela, vous pourriez créer des événements « terminé » en double.

Dans AppMaster, traitez ces états et règles comme des champs et logiques de première classe dans votre processus de sync, afin que vos apps Kotlin et SwiftUI se comportent de la même manière partout.

UX des modifications en attente : ce que l'utilisateur voit et peut faire

Queuez les changements en toute sécurité
Gérez l'expiration d'auth et les changements de permission en toute sécurité avant d'envoyer les actions en file.
Mettre en place la logique

Les gens doivent se sentir en sécurité quand ils utilisent l'app hors-ligne. Une bonne UX « modifications en attente » est calme et prévisible : elle reconnaît que le travail est sauvegardé sur l'appareil et rend l'étape suivante évidente.

Un indicateur discret fonctionne mieux qu'une bannière d'avertissement. Par exemple, affichez une petite icône « Synchronisation » dans l'en-tête, ou une étiquette discrète « 3 en attente » sur l'écran où se font les modifications. Gardez les couleurs d'alerte pour les vrais dangers (comme « impossible d'uploader car vous êtes déconnecté »).

Donnez aux utilisateurs un endroit unique pour comprendre ce qui se passe. Une simple Boîte d'envoi ou écran « Modifications en attente » peut lister les éléments avec un langage clair comme « Commentaire ajouté au Ticket 104 » ou « Photo de profil mise à jour ». Cette transparence évite la panique et réduit les tickets support.

Ce que les utilisateurs peuvent faire

La plupart des gens n'ont besoin que de quelques actions, et elles doivent être cohérentes partout dans l'app :

  • Réessayer maintenant
  • Éditer à nouveau (crée un changement plus récent)
  • Supprimer la modification locale
  • Copier les détails (utile pour signaler un problème)

Gardez les libellés de statut simples : En attente, Synchronisation, Échec. Quand quelque chose échoue, expliquez-le comme le ferait une personne : « Impossible d'uploader. Pas d'internet. » ou « Rejeté car cet enregistrement a été modifié par quelqu'un d'autre. » Évitez les codes d'erreur.

Ne bloquez pas toute l'application

Bloquez seulement les actions qui nécessitent vraiment d'être en ligne, comme « Payer avec Stripe » ou « Inviter un nouvel utilisateur ». Tout le reste doit continuer à fonctionner, y compris la consultation des dernières données et la création de nouveaux brouillons.

Un flux réaliste : un technicien sur le terrain édite un rapport de mission dans une cave. L'app affiche « 1 en attente » et le laisse continuer à travailler. Plus tard, l'état passe à « Synchronisation », puis se vide automatiquement. Si ça échoue, le rapport reste disponible, marqué « Échec », avec un seul bouton « Réessayer maintenant ».

Si vous construisez dans AppMaster, modélisez ces états comme partie de chaque enregistrement (pending, failed, synced) afin que l'UI puisse les refléter partout sans écrans de cas particuliers.

Auth, permissions et sécurité en hors-ligne

Mettez la checklist en pratique
Transformez la checklist de cet article en un flux d'app fonctionnel que vous pouvez démontrer en quelques heures.
Démarrer maintenant

Le mode hors-ligne change votre modèle de sécurité. Un utilisateur peut prendre des actions sans connexion, mais votre serveur reste la source de vérité. Traitez chaque changement en file comme « demandé », pas « approuvé ».

Expiration de la session pendant le hors-ligne

Les jetons expirent. Quand cela arrive hors-ligne, laissez l'utilisateur continuer à créer des modifications et stockez-les en attente. Ne prétendez pas que les actions nécessitant une confirmation serveur sont terminées. Marquez-les comme en attente jusqu'à la prochaine actualisation d'auth réussie.

Quand l'app revient en ligne, tentez d'abord un rafraîchissement silencieux. Si vous devez demander à l'utilisateur de se reconnecter, faites-le une seule fois, puis reprenez la sync automatiquement.

Après la reconnexion, révalidez chaque élément en file avant de l'envoyer. L'identité utilisateur peut avoir changé (appareil partagé), et les anciennes modifications ne doivent pas sync sous le mauvais compte.

Changements de permissions et actions interdites

Les permissions peuvent changer pendant que l'utilisateur est hors-ligne. Une modification autorisée hier peut être interdite aujourd'hui. Gérez cela explicitement :

  • Re-vérifiez côté serveur les permissions pour chaque action en file
  • Si c'est interdit, arrêtez l'élément et affichez une raison claire
  • Conservez la modification locale pour que l'utilisateur puisse la copier ou demander l'accès
  • Évitez les réessais répétés pour les erreurs « forbidden »

Exemple : un agent support édite une note client hors-ligne en vol. Pendant la nuit, son rôle est supprimé. Quand la sync s'exécute, le serveur rejette la mise à jour. L'app doit afficher « Impossible d'uploader : vous n'avez plus l'accès » et garder la note comme brouillon local.

Données sensibles stockées hors-ligne

Stockez le minimum nécessaire pour rendre les écrans et rejouer la file. Chiffrez le stockage hors-ligne, évitez de cacher des secrets, et définissez des règles claires pour la déconnexion (par exemple : effacer les données locales, ou ne conserver les brouillons qu'avec le consentement explicite de l'utilisateur). Si vous construisez avec AppMaster, commencez par son module d'auth et concevez votre file pour qu'elle attende toujours une session valide avant d'envoyer des changements.

Pièges courants qui causent perte de travail ou doublons

La plupart des bugs hors-ligne ne sont pas compliqués. Ils viennent de quelques petites décisions qui semblent inoffensives avec un Wi‑Fi parfait, puis cassent le travail réel plus tard.

Un échec courant est les écrasements silencieux. Si l'app upload une version ancienne et que le serveur l'accepte sans vérifier, vous pouvez effacer la modification plus récente de quelqu'un d'autre et personne ne s'en rend compte avant qu'il ne soit trop tard. Synchronisez avec un numéro de version (ou un last updated) et refusez d'écraser si le serveur a avancé, afin que l'utilisateur ait un choix clair.

Un autre piège est la tempête de réessais. Quand un téléphone retrouve une connexion faible, l'app peut inonder le backend toutes les quelques secondes, vidant la batterie et créant des écritures en double. Les réessais doivent être calmes : ralentissez après chaque échec et ajoutez un peu d'aléatoire pour que des milliers d'appareils ne réessaient pas tous en même temps.

Les erreurs qui mènent le plus souvent à la perte de travail ou aux doublons :

  • Traiter chaque échec comme « réseau » : séparez les erreurs permanentes (donnée invalide, permission manquante) des temporaires (timeout).
  • Cacher les échecs de sync : si les gens ne voient pas ce qui a échoué, ils refont la tâche et créent deux enregistrements.
  • Envoyer la même modification deux fois sans protection : attachez toujours un ID de requête unique pour que le serveur détecte et ignore les doublons.
  • Fusionner automatiquement des champs texte sans prévenir : si vous combinez des éditions automatiquement, laissez les utilisateurs réviser le résultat quand ça compte.
  • Créer des enregistrements hors-ligne sans ID stable : utilisez un ID local temporaire et mappez-le à l'ID serveur après l'upload, pour que les éditions suivantes n'en créent pas un second.

Un exemple rapide : un technicien crée une nouvelle "Visite de site" hors-ligne, puis la modifie deux fois avant de se reconnecter. Si l'appel de création est relancé et crée deux enregistrements serveur, les modifications ultérieures peuvent s'attacher au mauvais enregistrement. Des IDs stables et un dédoublonnage côté serveur évitent ça.

Si vous implémentez cela avec AppMaster, les règles ne changent pas. La différence est l'endroit où vous les implémentez : dans la logique de sync, le modèle de données et les écrans qui affichent "failed" vs "sent".

Scénario exemple : deux personnes modifient le même enregistrement

Rendez les conflits prévisibles
Ajoutez des contrôles de version et des règles de conflit comme des processus métier clairs et testables.
Construire maintenant

Une technicienne, Maya, met à jour le ticket « Job #1842 » dans une cave sans signal. Elle change le statut de « En cours » à « Terminé » et ajoute une note : « Remplacement de la vanne, test OK. » L'app sauvegarde instantanément et l'affiche comme en attente.

En haut, son collègue Leo est en ligne et modifie le même job en même temps. Il change l'heure prévue et assigne le job à un autre technicien, car un client a appelé avec une mise à jour.

Quand Maya retrouve du signal, la sync en arrière-plan démarre discrètement. Voici ce qui se passe dans un flux prévisible et convivial :

  1. La modification de Maya est encore dans la file (ID du job, champs changés, horodatage et version du record qu'elle a vue).
  2. L'app tente d'envoyer. Le serveur répond : « Ce job a été mis à jour depuis votre version » (un conflit).
  3. Votre règle de conflit s'exécute : statut et notes peuvent être fusionnés, mais les changements d'assignation faits plus tard côté serveur gagnent.
  4. Le serveur accepte un résultat fusionné : statut = « Terminé » (de Maya), note ajoutée (de Maya), technicien assigné = choix de Leo (de Leo).
  5. Le job se rouvre dans l'app de Maya avec une bannière claire : « Synchronisé avec des mises à jour. L'assignation a changé pendant que vous étiez hors-ligne. » Une action « Revoir » montre ce qui a changé.

Ajoutons un moment d'échec : le token de session de Maya a expiré pendant qu'elle était hors-ligne. La première tentative de sync échoue avec « Connexion requise ». L'app garde ses modifications, les marque « En pause », et affiche une invite simple. Après qu'elle se reconnecte, la sync reprend automatiquement sans ressaisir quoi que ce soit.

S'il y a un problème de validation (par exemple, « Terminé » requiert une photo), l'app ne doit pas deviner. Elle marque l'élément comme « Nécessite attention », indique précisément ce qu'il faut ajouter, puis laisse l'utilisateur renvoyer.

Des plateformes comme AppMaster aident ici car vous pouvez concevoir visuellement la file, les règles de conflit et l'état des pendings, tout en déployant des applis natives Kotlin et SwiftUI réelles.

Checklist rapide et prochaines étapes

Traitez la sync hors-ligne comme une fonctionnalité de bout en bout testable, pas comme une pile de correctifs. Le but est simple : les utilisateurs ne doivent jamais se demander si leur travail est sauvegardé, et l'app ne doit pas créer de doublons surprises.

Une courte checklist pour confirmer les fondations :

  • La file de sync est stockée sur l'appareil, et chaque changement a un ID local stable plus un ID serveur quand disponible.
  • Des statuts clairs existent (queued, syncing, sent, failed, needs review) et sont utilisés de façon cohérente.
  • Les requêtes sont idempotentes (sûres à réessayer), et chaque opération inclut une clé d'idempotence.
  • Les enregistrements ont une gestion de version (updatedAt, numéro de révision ou ETag) pour détecter les conflits.
  • Les règles de conflit sont rédigées en langage clair (qui gagne, ce qui se fusionne, quand on demande à l'utilisateur).

Une fois cela en place, vérifiez que l'expérience est aussi solide que le modèle de données. Les utilisateurs doivent pouvoir voir ce qui est en attente, comprendre ce qui a échoué, et agir sans craindre de perdre du travail.

Testez avec des scénarios proches de la vraie vie :

  • Mode avion : créer, modifier, supprimer, puis se reconnecter.
  • Réseau instable : couper la connexion en plein envoi et vérifier que les réessais ne créent pas de doublons.
  • App tuée : forcer la fermeture pendant l'envoi, rouvrir et confirmer que la file est récupérée.
  • Décalage d'horloge : l'heure de l'appareil est fausse, vérifier que la détection de conflit fonctionne quand même.
  • Taps en double : l'utilisateur appuie sur Enregistrer deux fois, vérifier que cela devient une seule modification serveur.

Prototypez le flux complet avant de peaufiner l'UI. Construisez un écran, un type d'enregistrement et un cas de conflit (deux modifications du même champ). Ajoutez une zone de statut de sync, un bouton Réessayer pour les échecs et un écran de conflit clair. Quand cela marche, répétez pour d'autres écrans.

Si vous construisez sans coder, AppMaster (appmaster.io) peut générer des applis natives Kotlin et SwiftUI alongside the backend, so you can focus on the queue, version checks, and user-facing states instead of wiring everything by hand.

Facile à démarrer
Créer quelque chose d'incroyable

Expérimentez avec AppMaster avec un plan gratuit.
Lorsque vous serez prêt, vous pourrez choisir l'abonnement approprié.

Démarrer
Synchronisation en arrière-plan pour applications mobiles (offline-first) : conflits, réessais et UX | AppMaster