iPad plantage et perte données coredata

Bonjour à tous, il m'arrive un truc vraiment chaud.
En chargeant la dernière version de mon app en dev sur TestFlight, mon iPad a planté et a installé l'app (sans update) en perdant mes données coredata. Pas grave pour moi, mais comment anticiper si cela arrivait à mes testeurs, notamment mon client ?

Y-a-t-il du code à mettre dans l'app pour anticiper cela et retrouver ses données en cas de perte ?

Merci d'avance...

Mots clés:

Réponses

  • PyrohPyroh Membre

    Je commencerai par essayer de comprendre pourquoi l'iPad a planté. Si c'est à cause de ton app c'est bien de le voir dans une version de test (sauf si des gens utilisent la version de test en production...). Comme ça tu peux le corriger.

    Pour récupérer les données en l'état, si tu as mis le fichier Core Data dans un endroit qui se retrouve dans un backup iCloud ou iTunes (voir ici) restaurer la dernière sauvegarde va aussi restaurer les données. Tout ça, bien entendu, si il y a un backup.

    Au niveau de la stratégie de backup pour une solution maison je te conseillerai d'essayer d'envoyer tes données dans iCloud. Tu gagne au passage la synchro entre appareils. Mais ça c'est plus compliqué à mettre en place.

    De toute façon je pense qu'il faut commencer par comprendre ce qu'il s'est passé. Par exemple as-tu changé ton schéma Core Data par exemple ?

  • Merci Pyroh pour ta réponse très explicite.

    L'iPad a planté, enfin, redémarré pendant que testFlight faisait l'update de mon app.
    Puis après ce redémarrage, il restait bloqué sur le chargement.

    J'ai fait l'erreur de jeter l'icône de l'app sur le bureau de l'iPad, puis ensuite de la recharger sur testFlight, et là, évidemment le bouton update était changé en install. Et j'ai installé.

    En gros je me suis comporté comme un gros naze.

    Pourquoi l'iPad a planté... Il plante sans arrêt. En fait il redémarre très souvent.

    Je n'ai pas changé mon schéma coreData récemment.

    Merci pour le lien - J'ai du mal à comprendre comment faire, mais bon...

  • Suite à mon erreur, j'ai une question subsidiaire.

    Si qq jette son app malencontreusement, et la recharge cette fois-ci d'après l'AppStore, récupère-t-il automatiquement ses données ou pas ?

    Merci d'avance

  • LarmeLarme Membre

    @busterTheo a dit :
    Suite à mon erreur, j'ai une question subsidiaire.

    Si qq jette son app malencontreusement, et la recharge cette fois-ci d'après l'AppStore, récupère-t-il automatiquement ses données ou pas ?

    Merci d'avance

    Non, enfin, pour la plupart des cas, non. Ça serait l'horreur sinon !
    Tu peux cependant avec iCloud faire quelques sauvegardes et cela sera demandé à l'utilisateur lorsqu'il veut supprimer l'app de garder ou non ces datas.
    Les données Keychain sont gardées aussi normalement.

    Pour le reste, une donnée locale est une donnée locale.

  • Merci Larme pour ta réponse.

    Donc, j'imagine que la plupart des apps sérieuses ont une sauvegarde dans la cloud.

    J'ai fait pas mal de recherches sur ce sujet, et j'avoue que je n'y comprend pas grand chose.
    J'ai lu le lien de Pyroh.

    Comment ça marche ce truc ?
    On vérifie d'abord que le user a effectivement un cloud ?

    Et puis en cas de suppression de l'app, c'est l'app qui a dans son code, la proposition de garder ou supprimer les datas, ou c'est automatique ?

    Parce que moi, par ex., je n'ai pas de cloud, je suis de la vieille école !
    Je sais, c'est pas bien, mais je n'en ai nullement besoin. J'aime bien mes disques durs...

    Donc si qq a un bon lien de tuto pour faire une sauvegarde dans l'hypothétique cloud du user, je suis preneur.

    Merci d'avance.

  • PyrohPyroh Membre

    Avant d'aller plus loin il serait bon de savoir de quoi on parle exactement. Quelle est la nature de ces données ?
    Aussi il peut être intéressant de savoir comment tu initialise la stack CoreData pour qu'on voit où ces données sont stockées.

    Pour la suppression de l'App et des données liées c'est la responsabilité de l'utilisateur, pas la tienne. Tu peux être responsable de la corruption des données si ton app fait n'importe quoi mais pas des actions directes de l'utilisateur. Client direct ou pas, faut pas déconner.
    Au pire il y a des backups qui sont faits pour les appareils iOS, sur iTunes ou iCloud. En cas de gros soucis on restaure et on en parle plus.

  • Depuis quelque temps, il est facile d'ajouter la possibilité de copier des fichiers dans d'autres apps : Fichiers, apps de stockage, AirDrop, etc..., voire les envoyer par mail.
    L'utilisateur peut donc faire lui-même ses sauvegardes où il le souhaite.
    De plus, lorsqu'une app est supprimée, il y a des alertes concernant les données.

  • busterTheobusterTheo Membre
    mars 2021 modifié #9

    Ah merci à tous les deux pour ces précisions.

    Concernant la stack coreData, voici le code que j'ai dans le appDelegate.
    J'avoue que je plane un peu sur tout ça, mais ça fonctionne nickel.

    Voici le code (qui n'est pas de moi) :

    // MARK: - Premières Méthodes Apple

    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }
    
    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }
    
    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    }
    
    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }
    
    func applicationWillTerminate(_ application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
        // Saves changes in the application's managed object context before the application terminates.
        self.saveContext()
    }
    

    // MARK: - Core Data stack

    lazy var applicationDocumentsDirectory: URL = {
        // The directory the application uses to store the Core Data store file. This code uses a directory named "THE NAME OF MY APP" in the application's documents Application Support directory.
        let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return urls[urls.count-1]
    }()
    
    lazy var managedObjectModel: NSManagedObjectModel = {
        // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
        let modelURL = Bundle.main.url(forResource: "THE NAME OF MY APP", withExtension: "momd")!
        return NSManagedObjectModel(contentsOf: modelURL)!
    }()
    
    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
        // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
        // Create the coordinator and store
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
        var failureReason = "There was an error creating or loading the application's saved data."
    
        do {
            let options = [NSMigratePersistentStoresAutomaticallyOption: true,NSInferMappingModelAutomaticallyOption: true]
    
            try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)
        } catch {
            // Report any error we got.
            var dict = [String: AnyObject]()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
            dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
    
            dict[NSUnderlyingErrorKey] = error as NSError
            let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
            // Replace this with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
            abort()
        }
    
        return coordinator
    }()
    
    lazy var managedObjectContext: NSManagedObjectContext = {
        // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
        let coordinator = self.persistentStoreCoordinator
        var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = coordinator
        return managedObjectContext
    }()
    

    // MARK: - Core Data Saving support

    func saveContext () {
        if managedObjectContext.hasChanges {
            do {
                try managedObjectContext.save()
            } catch {
                // Replace this implementation with code to handle the error appropriately.
                // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
                abort()
            }
        }
    }
    
  • D'après ce que je lis tu mets un fichier SingleViewCoreData.sqlite dans le dossier Documents liés à l'app. C'est une DB commune à l'ensemble des services de l'App.

    Maintenant pour tes sauvegardes tu as une poignée de solutions :

    • Tu t'en tapes. En cas de vrai souci l'utilisateur restaure l'appareil. Le fichier, à l'endroit où il est placé, est sauvegardé automatiquement sur iCloud ou iTunes. L'utilisateur final est responsable de ses données. Pas de sauvegarde ? En anglais on dira a valuable lesson...
    • Tu offre une option import/export. L'utilisateu a la possibilité d'exporter et importer le fichier SingleViewCoreData.sqlite. Pour valider le fichier à l'import tu essaie de créer un managed object context avec en suivant ton schema Core Data. Si pas d'erreur le fichier est valide et tu peux remplacer SingleViewCoreData.sqlite. À la limite tu peux faire un backup de SingleViewCoreData.sqlite pour le restaurer si l'utilisateur s'est planté. Mais ça c'est de la gourmandise.
    • Tu mets tout dans iCloud. Ça c'est plus rapidement dit que fait. Ça va dépendre de la complexité du schema Core Data. Il va sans dire que la synchro automatique Core Data <-> CloudKit c'est une fable qu'on raconte pour attirer les développeurs. En vrai ça marche pas. Ce qui fait qu'il faut répliquer —adapter quand c'est pas possible— le schéma Core Data vers un schéma CloudKit et ensuite synchroniser "à la main" en mappant les donnés au besoin. La complexité dépend des données bien entendu. Dans tous les cas t'auras les mains dans le cambouis pour un minimum de mise en place, c'est pas une case à cocher.

    Il reste l'option d'autres services de cloud mais je n'en ai pas à te conseiller. Du moins rien de gratuit ou même d'abordable.

    Dans tous les cas ce qui va te motiver c'est le coût en temps et le retour sur investissement. Est-ce que c'est prévu dans le contrat, est-ce que ton client est prêt à payer pour l'implémentation, etc.. Tu peux aussi passer par un consultant pour implémenter tout ça (on peut en discuter par MP, j'offre ce genre de services).

    Quelle que soit l'action que tu entreprendras ça te coûtera en temps ou en argent (voir les 2). Commence par te demander si tu as vraiment besoin de faire quelque chose 😉

  • @Pyroh a dit :
    Il va sans dire que la synchro automatique Core Data <-> CloudKit c'est une fable qu'on raconte pour attirer les développeurs. En vrai ça marche pas. Ce qui fait qu'il faut répliquer —adapter quand c'est pas possible— le schéma Core Data vers un schéma CloudKit et ensuite synchroniser "à la main" en mappant les donnés au besoin. La complexité dépend des données bien entendu.

    Bonsoir,

    Apple a bien revu la cohérence et la synchro entre core data et iCloud (en 2019 je crois).
    Il y a certainement des limites, mais pour la petite appli que je me suis faite pour gérer ma cave à vins, ça marche nickel ! Je m'occupe de mettre à jour la base de données locale Core Data, et la synchro iCloud se passe comme un charme; une mise à jour sur l'iPhone apparait quasi instantanément sur l'iPad.
    Et c'est le bonheur, par rapport à une version précédente de l'appli où je devais gérer (mal !) tous les cas de synchro entre la base de données locale et iCloud.

    Cordialement
    Nicolas

  • @Ristretto j'ai une fonction d'historique que j'aimerai synchroniser. C'est simple, une pile de données basiques. Je vais re donner une chance au produit alors 😉
    Merci du retour.

  • Merci Pyroh pour ces précision auxquelles je vais me tenir.
    Purée, tu as mis cela en forme comme un pro.
    C'est parfaitement détaillé, je te remercie encore pour le temps passé.
    Je vais en rester à ta réponse A, en gardant à l'esprit de m'intéresser quand j'aurais du temps à la B qui a l'air sympa..
    Et j'oublie donc la C.

    Merci au dernier bar ouvert...

Connectez-vous ou Inscrivez-vous pour répondre.