Core Data Backup in iCloud & restore from iCloud

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

Macoun 2019 - Frühbucherrabatt bis 26.7.2019

  • Core Data Backup in iCloud & restore from iCloud

    Hi zusammen,

    ich setzte mich gerade mit dem Thema Daten Backup auseinander und wollte einmal in die Runde fragen, welchen Weg ihr nutzt, bzw. welche Pro & Contra Erfahrungen ihr vielleicht bereits sammeln "durftet".

    Meine kleine App beinhaltet Cora Data, um Daten auf dem Gerät zu speichern. Nun viel mir auf, nachdem ich die App aus dem Simulator deinstalliert und wieder installiert habe, dass die Daten, die ich eingegeben hatte, weg waren. (Ein Glück bin ich darüber gestolpert - bin ja noch neu in der App-Entwicklung)


    Nun habe ich angefangen zu recherchieren und jede Menge völlig unterschiedlicher Meinungen und Ansätze gefunden.

    Die Meinungen gehen anscheint stark auseinander.

    z.B. hieß es:

    "CoreData in iCloud sichern ist simple und gut. Einfach iCloud in XCode aktivieren und das wars"

    oder

    "CoreData sollte in CoreKit gespeichert werden"

    dann ging es immer mal wieder darum Daten auf verschiedenen Geräten zu synchen. Dabei hieß es dann auch

    "Klar das geht mit CloudKit"

    oder

    "Nein, das geht nicht offiziell mit iCloud, aber eben doch (besser) mit CloudKit"

    Einige meinten dann noch etwas von:

    "Speicher die CoreData-Daten als Zip und lade sie dann hoch in die iCloud oder in CoreKit oder in DropBox"


    Am Ende steht man dann als Neuling da und denkt sich "WTF ?( "


    Letztendlich möchte ich eigentlich nur ein Backup der CoreData-Daten in die iCloud realisieren und wenn der User die App einmal löscht, dass man dann die Daten wiederherstellen kann. Geht das überhaupt so?

    Ich bin wirklich sehr auf eure Meinung gespannt und darauf, wie ihr das so löst. Ein Synchen zwischen Geräten muss nicht wirklich sein, wenn das aber als "Nebeneffekt" leicht realisierbar wäre warum nicht?

    Haut in die Tasten und erleuchtet mich :thumbsup: Ich danke euch :rolleyes:

    LG
    Neu in der IOS-App-Entwicklungswelt - Spannend, ab und an nervenaufreibend, aber am Ende einfach faszinierend. Seid bitte nicht zu streng wenn ich Fragen einmal doppelt stelle, ich lerne noch :saint:
  • Mac & i Test Abo
  • CodeNerd schrieb:

    Nutzt niemand heutzutage die iCloud? Das wäre ja schon einmal ein guter Hinweis :D
    Doch, ich nutze iCloud für die Synchronisation von Dateien, nicht aber im Zusammenspiel mit Core Data ... daher habe ich mich etwas zurückgehalten.

    Ich kann Dir also keinen Rat geben, wie sinnvoll / aufwendig eine Realisierung mittels CloudKit ist und welche Fussangeln diese haben mag. Wenn es Dir aber eigentlich nur um eine Sicherung des (privaten) Core Data Inhalts geht, würde ich nur einen Datei-Transfer in die iCloud des Benutzers machen. Hierzu gibt es Methoden des NSFileManagers ... es funktioniert sogar ein (koordiniertes) copyToURL.

    Wenn Du eh an diesem Punkt bist, würde ich mir außerdem Gedanken über eine generelle Import- / Export-Funktion Deiner Daten machen: Natürlich kannst Du einfach den persistent Datastore "unter der Haube" kopieren bzw. ersetzen. Das finde ich aber etwas hacky und spätestens wenn sich diese Repräsentation ändert, bekommst Du potentiell Probleme beim Restore ... unnötig kompliziert. Ich würde die Daten in eine Datei dumpen, die ich auch wieder importieren kann ... erstens verhindert dies eine Abhängigkeit zur Core Data Implementierung, zweitens kann man einen solchen Export auch toll für weitere "Teilen"-Funktionen verwenden (AirDrop, Email, Web-Download, ...). Je nach Art Deiner App vielleicht ein echter Mehrwert.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • MyMattes schrieb:

    CodeNerd schrieb:

    Nutzt niemand heutzutage die iCloud? Das wäre ja schon einmal ein guter Hinweis :D
    Doch, ich nutze iCloud für die Synchronisation von Dateien, nicht aber im Zusammenspiel mit Core Data ... daher habe ich mich etwas zurückgehalten.
    Ich kann Dir also keinen Rat geben, wie sinnvoll / aufwendig eine Realisierung mittels CloudKit ist und welche Fussangeln diese haben mag. Wenn es Dir aber eigentlich nur um eine Sicherung des (privaten) Core Data Inhalts geht, würde ich nur einen Datei-Transfer in die iCloud des Benutzers machen. Hierzu gibt es Methoden des NSFileManagers ... es funktioniert sogar ein (koordiniertes) copyToURL.

    Wenn Du eh an diesem Punkt bist, würde ich mir außerdem Gedanken über eine generelle Import- / Export-Funktion Deiner Daten machen: Natürlich kannst Du einfach den persistent Datastore "unter der Haube" kopieren bzw. ersetzen. Das finde ich aber etwas hacky und spätestens wenn sich diese Repräsentation ändert, bekommst Du potentiell Probleme beim Restore ... unnötig kompliziert. Ich würde die Daten in eine Datei dumpen, die ich auch wieder importieren kann ... erstens verhindert dies eine Abhängigkeit zur Core Data Implementierung, zweitens kann man einen solchen Export auch toll für weitere "Teilen"-Funktionen verwenden (AirDrop, Email, Web-Download, ...). Je nach Art Deiner App vielleicht ein echter Mehrwert.

    Mattes


    Moin Mattes,

    vielen Dank für dein ausführliches Feedback!

    NSFileManagers schaue ich mir einmal genauer an. Vielen Dank für den Hinweis. Letztendlich geht es tatsächlich nur um eine Datensicherung. Beispielsweise für den Fall, wenn die App einmal nach einem Update crashed und der User sie deinstallieren und neu installieren müsste.

    In diesem Fall muss ich die Möglichkeit schaffen, dass er die Daten per 1-click Lösung wieder in seiner App verfügbar macht.

    Hast du sonst noch einen Tipp in Richtung "Als Datei-Transfer in die iCloud...". Gibt es hier irgendetwas zu beachten, was du vielleicht schon mühselig herausfinden musstest?



    "Wenn Du eh an diesem Punkt bist, würde ich mir außerdem Gedanken über eine generelle Import- / Export-Funktion Deiner Daten machen"

    Ich habe bereits einen Export für einzelne Daten als Excel realisiert, sodass der User diese Daten per Mail oder Whatsapp versenden kann. Das ganze ist dann ja in der typischen "Value,Value,Value,..."-Form.

    Meinst du ich sollte so alle CoreData-Daten exportieren oder schlägst du ein anderes Datei-Format für einen Export vor?

    Ich danke dir!

    PS: Kannst du mir vielleicht verraten, warum ich keine Benachrichtigungen vom Forum hier erhalte, wenn ich den Beitrag abonniert habe? Das ist irgendwie blöd, da ich dann neue Beiträge gar nicht sofort mitbekomme und ich finde es echt unhöflich, wenn ich dann mal ein paar Tage später erst antworte :(
    Neu in der IOS-App-Entwicklungswelt - Spannend, ab und an nervenaufreibend, aber am Ende einfach faszinierend. Seid bitte nicht zu streng wenn ich Fragen einmal doppelt stelle, ich lerne noch :saint:
  • Ohne genauere Kenntnis Deiner Anwendung können meine Antworten nur recht unspezifisch sein. Sieh sie als Anregungen zum Recherchieren und als Gedankenanstösse:
    • Tricky bei iCloud fand ich die Implementierung einer Offline-Fähigkeit („greedy client“) und die Migration zwischen lokalen und iCloud-Dateien. Beides dürfte bei Deinem Anwendungsfall irrelevant sein. Bedenke aber, dass eine Grundsatzentscheidung ist, wenn Deine App neben Core Data auch Daten in‘s Dateisystem legt: Sollen diese auch on der iCloud liegen?
    • Eine reine CSV-Datei finde ich zum Abbilden von Relationen ungeeignet. Ich habe letztlich nur ein NSDictionary serialisiert. Königsklasse wäre wohl ein SQL-Dump, aber schwer zu erstellen und vor allem zu parsen.
    • Hast Du in Deinem Forum-Profil die Einstellungen für Benachrichtigungen kontrolliert? Bei mir klappt‘s...
    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • MyMattes schrieb:

    Ohne genauere Kenntnis Deiner Anwendung können meine Antworten nur recht unspezifisch sein. Sieh sie als Anregungen zum Recherchieren und als Gedankenanstösse:
    • Tricky bei iCloud fand ich die Implementierung einer Offline-Fähigkeit („greedy client“) und die Migration zwischen lokalen und iCloud-Dateien. Beides dürfte bei Deinem Anwendungsfall irrelevant sein. Bedenke aber, dass eine Grundsatzentscheidung ist, wenn Deine App neben Core Data auch Daten in‘s Dateisystem legt: Sollen diese auch on der iCloud liegen?
    • Eine reine CSV-Datei finde ich zum Abbilden von Relationen ungeeignet. Ich habe letztlich nur ein NSDictionary serialisiert. Königsklasse wäre wohl ein SQL-Dump, aber schwer zu erstellen und vor allem zu parsen.
    • Hast Du in Deinem Forum-Profil die Einstellungen für Benachrichtigungen kontrolliert? Bei mir klappt‘s...
    Mattes

    Hallo Mattes vielen Danke für deine Tipps!

    Ich schreibe dir gleich mal eine PM. Ich brauch unbedingt deine Hilfe!
    Neu in der IOS-App-Entwicklungswelt - Spannend, ab und an nervenaufreibend, aber am Ende einfach faszinierend. Seid bitte nicht zu streng wenn ich Fragen einmal doppelt stelle, ich lerne noch :saint:
  • CodeNerd schrieb:

    MyMattes schrieb:

    Ohne genauere Kenntnis Deiner Anwendung können meine Antworten nur recht unspezifisch sein. Sieh sie als Anregungen zum Recherchieren und als Gedankenanstösse:
    • Tricky bei iCloud fand ich die Implementierung einer Offline-Fähigkeit („greedy client“) und die Migration zwischen lokalen und iCloud-Dateien. Beides dürfte bei Deinem Anwendungsfall irrelevant sein. Bedenke aber, dass eine Grundsatzentscheidung ist, wenn Deine App neben Core Data auch Daten in‘s Dateisystem legt: Sollen diese auch on der iCloud liegen?
    • Eine reine CSV-Datei finde ich zum Abbilden von Relationen ungeeignet. Ich habe letztlich nur ein NSDictionary serialisiert. Königsklasse wäre wohl ein SQL-Dump, aber schwer zu erstellen und vor allem zu parsen.
    • Hast Du in Deinem Forum-Profil die Einstellungen für Benachrichtigungen kontrolliert? Bei mir klappt‘s...
    Mattes
    Hallo Mattes vielen Danke für deine Tipps!

    Ich schreibe dir gleich mal eine PM. Ich brauch unbedingt deine Hilfe!
    Auch hier noch eimal vielen Dank für deine Unterstützung. Du hast natürlich Recht damit, dass die Informationen hier im Forum auch anderen helfen können, daher komme meine Antworten jetzt hier.

    Worum gehts:

    Alle Daten des App-Nutzers werden in CoreData gespeichert. Ich brauche eine Möglichkeit damit der Nutzer die Daten per Button irgendwo sichern kann. Ganz egal ob iCloud, CloudKit, Dropbox, Zip-File, oder auf dem eigenen Server (PHP & MySql - Parse Server ist leider keine Option da keine MongoDB möglich).

    Antwort von Mattes:

    Du wirfst hier einiges durcheinander: Ein ZIP-File ist ja an sich keine Sicherung: Es ist ein Container zur Kompression mehrerer Dateien. Ohne einem vernünftigen Speicherort ist der nichts wert. Also musst Du zwei Fragen beantworten:

    • Wie hole ich die Daten aus der SQL-DB bzw. lese sie wieder ein. Je nach Datenmodell kann hier das von Dir angesprochene CSV reichen. Bei komplexeren Relationen kommst Du vielleicht mit einer Datei nicht mehr hin. Dann nimm mehrere CSVs oder speichere die Strukturen in NSDictionaries, die sich leicht in Plists schreiben lassen. Oder erstelle Dir einen SQL-Dump, der beim Restore die Datenbank-Struktur mit wiederherstellen könnte … vermutlich aber Overkill. Wie auch immer, die exportieren Dateien kannst Du natürlich gerne zippen.
    • Beim Speicherort würde ich zwischen Dropbox und iCloud entscheiden, ein eigener Server fällt m. E. aus Datenschutzgründen aus: Ich zumindest würde als Benutzer keinem mir unbekannten Developer meine Daten anvertrauen. Und ich glaube nicht, dass Du die DSGVO-Vorgaben erfüllen möchtest. Dropbox-Erfahrung habe ich nur mit der API v1, als diese abgelöst wurde, wechselte ich auf iCloud: Besser integriert und ich erwarte eine höhere Stabilität der API gegen Änderungen.


    Meine Antwort:

    Genau, ich hatte Zip-File nur mit aufgelistet, da die Idee dahinter war, das der Nutzer die CoreData-Daten per Button in ein Zip-File verpacken lässt und dieses dann ganz einfach per Mail versenden an sich selber versenden oder auf dem iPhone Speichern kann.

    Die zwei Fragen würde ich wie folgt beantworten.

    Frage 1:

    Ich nutze aktuell noch keine Relationen da ich davon bis vor wenigen Tagen noch nichts wusste. Jetzt wollte ich vor 2 Tagen die Datenbank umbauen, da ich dachte hey das ist ja viel cooler, als jedem Datenbank-Eintrag (Item) immer eine ID zu geben und anhand dieser dann den Datensatz bearbeiten zu können. Tja und dann der Supergau. Ich hatte gerade alle Relationen eingefügt starte die App und die Datenbank war nicht mehr lesbar.

    Nach einiger Recherche bin ich dann über den Begriff Migration gestolpert und habe versucht dieses Thema zu verstehen. Allerdings stehe ich da noch total auf dem Schlauch, da so ziemlich alle Beispiele immer in Kombination mit der manuellen Erstellung neuer Managed Object Classes zusammen hängen, ich diese aber bisher gar nicht verwende geschweige denn habe. Xcode erstellt bisher alles automatisch im Hintergrund und ich habe noch nicht rausbekommen können, ob ich diese Managed Object Classes denn überhaupt erstellen muss damit dann alles funktioniert.

    Die nächste große Überraschung kam dann, als ich in einem bestehenden (einer bestehender?) Entity weitere Items hinzugefügt habe. Auch hier ist die App dann gecrasht und die Datenbank war nicht mehr lesbar. Ich weiß noch nicht wie diese Migration funktioniert, aber ich weiß, dass sie wohl notwendig ist. Jetzt habe ich erst einmal einfach neue Entity´s für die neuen Zusatz-Items angelegt und das scheint die Datenbank bzw. Xcode so zu mögen bzw. wieder automatisch zu korrigieren. Die App läuft und nach einem Update von vorheriger App-Version auf neue App-Version läuft alles stabil. *PUH*

    Zurück zu eigentlichen Thema, da ich keine Relationen verwende müsste ich ja einfach eine CSV-Datei als Backup erstellen können, was ich bereits eh schon für den Export der Nutzer-Einträge verwende. Wenn ich dich richtig verstanden habe, dann kann ich auch diese CSV-Datei wieder importieren. Das klingt ja nach eine möglichen Backup-Lösung ohne eine Cloud zu nutzen.

    Deinen Tip zu NSDictionaries und Plists finde ich sogar noch interessanter. Plists sind doch letztendlich XML-Files richtig? Das schaue ich mir gleich einmal näher an.

    Frage 2:

    OK, also Dropbox habe ich nur in Betracht gezogen, weil im Netz oft diese Möglichkeit genannt wird. Tendieren tue ich aber definitiv zur iCloud, da dann alles bei Apple unter einem Dach bleibt. Sprechen wir denn hier gerade von iCloud oder von CloudKit?

    Bei cloudKit müsste man dann ja die private cloudKit-Variante verwenden (soweit ich das gelesen habe gibt es ja 3 verschiedenen Varianten). Durch die private Variante entstehen dann ja auch keine Kosten oder?

    iCloud habe ich bereits im 4. letzten Update meiner App über Xcode aktiviert allerdings habe ich hierzu auch noch eine Frage die du mir vielleicht beantworten kannst. In den IOS-Einstellungen finde ich jetzt die App unter iCloud gelistet. Was genau bedeutet das jetzt? Ist das nur dafür da, damit die Daten der App automatisch ab und an in die iCloud geschoben werden, um sie im Fall, wenn man z.B. das iPhone verliert oder durch ein Neues austauscht wiederhergestellt werden können?

    Wenn ja, kann ich diesen Mechanismus auch aus der App heraus im Hintergrund starten und vor allem die Daten auch aus diesem icloud-Backup wieder in die App einspielen? Das wäre dann ja genau das was ich gerade suche! Und wenn das so möglich sein sollte, sind die Daten dann auch noch in der iCloud, wenn die App gelöscht wurde und z.B. eine Woche später wieder neu installiert wird, oder werden die Daten mit dem iPhone gesynched, sodass sie dann ebenfalls beim Löschen der App aus der iCloud gelöscht werden?

    Ich habe die Vermutung, dass die Backup Erstellung in der iCloud, die du meinst, ein anderer Mechanismus ist als das was ich jetzt schon aktiviert habe. Bin da echt auf deinen Antwort gespannt!

    Was nur 10000 Zeichen? Ok gleich gehts im zweiten Beitrag weiter... :saint:
    Neu in der IOS-App-Entwicklungswelt - Spannend, ab und an nervenaufreibend, aber am Ende einfach faszinierend. Seid bitte nicht zu streng wenn ich Fragen einmal doppelt stelle, ich lerne noch :saint:
  • Weitere Antwort von Mattes:

    Die explizite Sicherung per Button würde ich nicht anbieten: iCloud-Synchronisierung funktioniert mit lokalen Verzeichnissen, in denen die App schnell (z. B. beim Wechsel in den Hintergrund) schreiben kann. Der Transfer in die Cloud findet dann asynchron ohne App statt. Sonst wirst Du Benutzer haben, die nicht sichern und Dir dann auch die Schuld geben … siehe oben.

    Also die wichtigsten Schritte zu iCloud:

    • Ergänze Deine App in Xcode um die iCloud-Capability (iCloud Drive)
    • Hole Dir z. B. im AppDelegate den Ordner, der in den Cloud-Container synchronisiert wird. NSFileManager hat die Methode URLForUbiquityContainerIdentifier dafür. Diese solltest Du in einer asynchronen Queue aufrufen, da sie etwas dauert (wenn z. B. der Container erst erstellt wird)
    • Rechne damit, dass der Benutzer iCloud deaktiviert hat … Zeit für eine Fehlermeldung
    • Wenn Du die URL hast, lege einen „Documents“-Unterordner an, mach die URL z. B. über ein Property verfügbar
    • Wenn die App in den Hintergrund geht, exportierst Du die DB und nutzt einen NSFileCoordinator zum Schreiben der Backupdatei in das o.g. Verzeichnis (NSFileManager copyItemAtURL:)
    • Bei App-Start prüfst Du, ob Deine SQL-DB gefunden werden kann. Wenn nicht, bietest Du (a) ein Restore oder (b) ein Weitermachen mit leerer DB an … je nach App
    • Restore wie oben: NSFileCoordinator zu Lesen von der Cloud-URL (die wie gesagt eigentlich ein spezielles lokales Verzeichnis ist.


    Meine Antwort:
    OK, also ich bin voll dabei. Die iCloud muss es am Ende werden! Auch deinen Vorschlag die Sicherung im Hintergrund auszuführen und beim App-Aufruf, über einen Dialog, im Fall eines Datenverlusts, den Nutzer die Möglichkeit zu geben ein Restore auszuführen oder alles leer zu lassen und so weiter zu arbeiten, finde ich sehr gut!

    Zu deinen genannten Schritten:

    • Ergänze Deine App in Xcode um die iCloud-Capability (iCloud Drive)


      Ich habe in Xcode bereits iCloud aktiviert. Darin finde ich jetzt folgende Unterpunkte:
      1. Key-value storage (aktiviert by default)
      2. iCloud Documents (deaktiviert)
      3. CloudKit (Deaktiviert)


      iCloud Drive finde ich nicht oder ist damit "iCloud Documents" gemeint?

    • Hole Dir z. B. im AppDelegate den Ordner, der in den Cloud-Container synchronisiert wird. NSFileManager hat die Methode URLForUbiquityContainerIdentifier dafür. Diese solltest Du in einer asynchronen Queue aufrufen, da sie etwas dauert (wenn z. B. der Container erst erstellt wird)

      Okay.

    • Rechne damit, dass der Benutzer iCloud deaktiviert hat … Zeit für eine Fehlermeldung


      Okay, heißt, wenn er iCloud deaktiviert hat, gibts auch kein Backup. Dann wäre hier ein zusätzliches Backup per CSV doch ganz gut, welches der Nutzer sich dann per Mail senden kann und dieses wieder importieren kann, um die Datenbank zu restoren. Okay man könnte den Nutzer darauf hinweisen, dass er iCloud zulassen muss, um backuppen zu können, aber ich denke das kommt dann eher negativ bei dem Nutzer an.
      Oder was meinst du? Ich habe immer die iCloud an und weiß daher leider nicht, ob es solche Meldungen in anderen Apps gibt bzw. ob man innerhalb der App überhaupt prüfen kann, ob iCloud deaktiviert ist, wobei das muss ja möglich sein?

    • Wenn Du die URL hast, lege einen „Documents“-Unterordner an, mach die URL z. B. über ein Property verfügbar

      Okay.


    • Wenn die App in den Hintergrund geht, exportierst Du die DB und nutzt einen NSFileCoordinator zum Schreiben der Backupdatei in das o.g. Verzeichnis (NSFileManager copyItemAtURL:)

      Alles klar, jetzt versteh ich es. Ich exportiere also jetzt erst z.B. als CSV und lege dieses File in den Ordner, der mit der iCloud synchronisiert wird.

    • Bei App-Start prüfst Du, ob Deine SQL-DB gefunden werden kann. Wenn nicht, bietest Du (a) ein Restore oder (b) ein Weitermachen mit leerer DB an … je nach App

      Okay genau so!

    • Restore wie oben: NSFileCoordinator zu Lesen von der Cloud-URL (die wie gesagt eigentlich ein spezielles lokales Verzeichnis ist.

      Alles klar, also kann ich per NSFileCoordinator die z.B. CSV-Datei auf das Gerät laden dann auslesen und per Loop CSV-Zelle für CSV-Zelle durchgehen und damit die Datenbank wieder neu aufbauen.

    Letzte Antwort von Mattes:

    Hier (und per Google) findest Du mehr Infos:

    developer.apple.com/library/ar…gGuide/iCloud/iCloud.html

    Einen NSFilePresenter brauchst Du nicht zwingend, der ist „nur“ gedacht, um die GUI auf Änderungen von Cloud-Dokumenten reagieren zu lassen. Du weisst auf Basis des Rückgabewertes vom copyItem, dass die Datei kopiert wurde. Dass diese Methode den Thread blockiert, sollte in Deinem Anwendungsfall kaum stören … ohne Restore lässt sich ja eh nix machen. Sonst asynchron dispatchen…


    Meine Antwort:

    Super den Link schau ich mir auf jeden Fall gleich noch einmal an. Mir ist jetzt auch gerade bewusst wo die ganze Zeit mein Gedanken-Fehler lag. Ich hatte mehrfach im Netz gelesen, dass man die CoreData an die iCloud direkt übertragen und die Felder & Inhalte direkt in der iCloud ansehen bzw. editieren kann. Jetzt ist mir erst klar, dass das aber nur für iCloudKit gilt und das ich letztendlich eine Datei in die iCloud hochschieben muss und für den Restore wieder runterlade, auslese und über einen Loop die Datenbank wieder herstelle. Mein ursprünglicher Gedanke lag immer darin, dass es eine Backup und Restore Möglichkeit für CoreData ud iCloud gibt.

    Also so, dass man per bestehender Funktion bzw. Methode den CoreData Inhalt an die Cloud sendet und wieder empfangen kann und dass die Funktion (die es ja nicht gibt) dann die empfangenen Daten sofort wieder als CoreData anlegt. Man Apple macht es hier aber auch kompliziert ^^


    Mattes ich danke dir wirklich sehr für deine Zeit und ausführliche top Erklärung. Vielen vielen Dank!
    Neu in der IOS-App-Entwicklungswelt - Spannend, ab und an nervenaufreibend, aber am Ende einfach faszinierend. Seid bitte nicht zu streng wenn ich Fragen einmal doppelt stelle, ich lerne noch :saint:
  • CodeNerd schrieb:

    iCloud habe ich bereits im 4. letzten Update meiner App über Xcode aktiviert allerdings habe ich hierzu auch noch eine Frage die du mir vielleicht beantworten kannst. In den IOS-Einstellungen finde ich jetzt die App unter iCloud gelistet. Was genau bedeutet das jetzt?
    Gegenfrage: Warum hast Du "einfach so" iCloud in den App-Capabilities aktiviert, ohne zu wissen, was es macht?

    Nicht bös' gemeint, aber Du solltest in Deiner App nichts implementieren, was Du nicht wirklich verstanden hast. Besonders bei einer stark wachsenden Benutzerzahl ist hier mehr Sorgfalt angeraten: Lies Dir das oben verlinkte Apple-Dokument sorgfältig durch, insbesondere bzgl. iCloud Documents (ja, das meinte ich). Dann bau' Dir ein Beispiel zusammen und teste, teste, teste: Gerade hier gibt es viele Situationen, auf die Deine App kontrolliert reagieren muss: Der Anwender deaktiviert iCloud, es gib keine Backup-Datei, diese unterstützt nicht eine neue / geänderte Datenstruktur usw.

    Ich würde Update-Zyklen einplanen, die es mir erlauben, sauber zu konzeptionieren, lernen, programmieren, testen, verwerfen ... Notfall-Fixes vielleicht etwas aussen vorgelassen.

    Nur gut gemeint, Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • MyMattes schrieb:

    CodeNerd schrieb:

    iCloud habe ich bereits im 4. letzten Update meiner App über Xcode aktiviert allerdings habe ich hierzu auch noch eine Frage die du mir vielleicht beantworten kannst. In den IOS-Einstellungen finde ich jetzt die App unter iCloud gelistet. Was genau bedeutet das jetzt?
    Gegenfrage: Warum hast Du "einfach so" iCloud in den App-Capabilities aktiviert, ohne zu wissen, was es macht?
    Nicht bös' gemeint, aber Du solltest in Deiner App nichts implementieren, was Du nicht wirklich verstanden hast. Besonders bei einer stark wachsenden Benutzerzahl ist hier mehr Sorgfalt angeraten: Lies Dir das oben verlinkte Apple-Dokument sorgfältig durch, insbesondere bzgl. iCloud Documents (ja, das meinte ich). Dann bau' Dir ein Beispiel zusammen und teste, teste, teste: Gerade hier gibt es viele Situationen, auf die Deine App kontrolliert reagieren muss: Der Anwender deaktiviert iCloud, es gib keine Backup-Datei, diese unterstützt nicht eine neue / geänderte Datenstruktur usw.

    Ich würde Update-Zyklen einplanen, die es mir erlauben, sauber zu konzeptionieren, lernen, programmieren, testen, verwerfen ... Notfall-Fixes vielleicht etwas aussen vorgelassen.

    Nur gut gemeint, Mattes

    Hey Mattes,

    du hast natürlich absolut Recht. Ich aktiviere eigentlich auch nichts, was ich nicht kenne. Aber bei der iCloud Funktionalität hatte ich bei StackOverflow gelesen, dass man nur diese Einstellung aktivieren braucht, um die normale Sicherung per iCloud sicherzustellen. Also die Sicherung die das iPhone automatisch für alle Apps macht in sofern der Nutzer iCloud in den Einstellungen aktiviert hat, um dann im Fall, dass der Nutzer sein iPhone tauscht oder verliert seine Daten wiederherstellen kann. Ich hatte das so verstanden, dass diese Einstellung an sein muss, damit diese automatische Sicherung funktioniert.

    Ich bin bereits dabei mich, auch über deinen genannten Link, in die iCloud-Thematik einzulesen und das Ganze zu verstehen.

    Derzeit probiere ich gerade die CoreData Daten erst einmal als Datei zu exportieren. Von CSV bin ich zwischenzeitlich ab und bei JSON gelandet, da ich viel dazu gelesen hatte, dass dieses Format sinnvoller wäre.

    Dabei bin ich auf ein Problem gestoßen, wessen Ausmaß mir bislang nicht bewusst war.

    Das Problem ist Folgendes:

    In den ersten App-Versionen hatte ich einen Fehler, den ich aber erst nicht bemerkte, da er nur sehr selten auftrat und ich immer dachte ich habe einfach irgendwas zu diesem Zeitpunkt falsch gemacht. Der Fehler lag in einer Auto-Save-Funktion die ich geschrieben habe und einem NSDate.

    In Kurzform, der Nutzer füllt in der App Eingabefelder aus und klickt auf "zurück" statt erst zu speichern. Bei den Eingabefeldern ist auch eines in das man ein Datum und eine Urzeit eingeben kann also z.B. 01.01.2019, 08:00 oder 1/1/2019, 08:00 bei englischer Sprache.

    Wenn der User nun auf zurück klickt speichert die App die Daten automatisch, damit sie nicht verloren gehen. Hin und wieder kam es vor, dass das eingegebene Datum anscheint über den Formatter nicht schnell genug als NSDate umgewandelt wurde und dann hat die Datenbank einen Nil-Wert in die Datenbank geschrieben.

    Ja ich weiß ich hätte das vor dem Schreiben prüfen müssen, wusste eich aber zu dieser Zeit noch nicht, da ich da noch nichts von nil-Werten wusste und dachte die App setzt dann einen einen leeren Wert also "" als Value in CoreData. Pustekuchen und ein absoluter Supergau, da ich einen weiteren Fehler oder eher einen falschen Code eingesetzt habe, nämlich einen GUARD für jede abgefragte Property innerhalb der CoreData-Auslese-Funktion. Das Resultat war jetzt, dass durch den Nil-Wert in der Datenbank kein einziger ViewController mehr richtig funktionierte, da das Auslesen der Daten durch den Guard per return abgebrochen wurde. Der Nutzer hatte plötzlich keine Daten mehr in seiner App oder schlimmer die App ist gecrasht.

    Ja schöne Schei***. Aber man lernt ja dazu. In einem Update habe ich dann schnell alle Guards aus der gesamten App entfernt und gegen normale Checks mit Default-Wert bei nil getauscht.

    Jetzt überlege ich mir gerade wie ich die Datenbank am besten säubern kann, ohne womöglich einen Datenverlust zu provozieren.

    Dazu habe ich jetzt eine Funktion geschrieben, die die gesamte Datenbank durchliest und jeden Eintrag einzeln prüft. Wenn dann ein nil-Wert gefunden wird wird dieser durch eine "0" oder ein NSDate ausgetauscht. Ich habe noch ein bisschen Schiss diese Funktion an die Nutzer auszuteilen, aber ich denke ich komme da wohl nicht drum herum um zukünftige nil-Probleme vorbeugen zu können.

    Ich schreibe dazu gleich noch einen neuen Forums-Posts um hier nicht die Themen zu vermischen, da ich dazu auch noch eine Frage hätte und vielleicht Andere ebenfalls dieses Problem haben.


    Zurück zum Thema:

    Ich habe es mittlerweile geschafft, die CoreData Daten als JSON zu exportieren und dann in eine JSON-Datei zu schreiben. Was ich derzeit noch nicht schaffe ist das NSDate umzuwandeln. @MyMattes hast du dazu einen Tipp für mich?

    Hier der Code den ich dafür nutze:

    Quellcode

    1. func icloudBackup(){
    2. let myGroup = DispatchGroup()
    3. myGroup.enter()
    4. checkIfCoreDataHasNilValues()
    5. myGroup.leave()
    6. myGroup.notify(queue: DispatchQueue.main) {
    7. var backup_user = [[String: Any]]()
    8. guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
    9. return
    10. }
    11. let context = appDelegate.persistentContainer.viewContext
    12. let request_user = NSFetchRequest<NSFetchRequestResult>(entityName: „User“)
    13. do{
    14. let results_user = try context.fetch(request_user)
    15. backup_user = convertToJSONArray(CoreDataArray: results_user as! [NSManagedObject]) as! [[String : Any]]
    16. writeInFile(backupArray: backup_user, fileName: „App-user-backup.json")
    17. }catch{
    18. print(error)
    19. }
    20. }
    21. }
    22. func convertToJSONArray(CoreDataArray: [NSManagedObject]) -> Any {
    23. var jsonArray: [[String: Any]] = []
    24. for item in CoreDataArray {
    25. var dict: [String: Any] = [:]
    26. for attribute in item.entity.attributesByName {
    27. dict[attribute.key] = item.value(forKey: attribute.key)
    28. }
    29. jsonArray.append(dict)
    30. }
    31. return jsonArray
    32. }
    33. func writeInFile(backupArray: [[String: Any]], fileName:String){
    34. let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
    35. let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!
    36. let jsonFilePath = documentsDirectoryPath.appendingPathComponent(fileName)
    37. let fileManager = FileManager.default
    38. var isDirectory: ObjCBool = false
    39. // creating a .json file > Documents folder
    40. if !fileManager.fileExists(atPath: (jsonFilePath?.absoluteString)!, isDirectory: &isDirectory) {
    41. let created = fileManager.createFile(atPath: jsonFilePath!.absoluteString, contents: nil, attributes: nil)
    42. if created {
    43. print("File created ")
    44. } else {
    45. print("Couldn't create file")
    46. }
    47. } else {
    48. print("File already exists")
    49. }
    50. // creating JSON
    51. var jsonData: NSData!
    52. do {
    53. jsonData = try JSONSerialization.data(withJSONObject: backupArray, options: JSONSerialization.WritingOptions()) as NSData
    54. let jsonString = String(data: jsonData as Data, encoding: String.Encoding.utf8)
    55. print(jsonString as Any)
    56. } catch let error as NSError {
    57. print("Array to JSON conversion failed: \(error.localizedDescription)")
    58. }
    59. // Write that JSON to the file
    60. do {
    61. let file = try FileHandle(forWritingTo: jsonFilePath!)
    62. file.write(jsonData as Data)
    63. print("JSON data was written to teh file successfully!")
    64. } catch let error as NSError {
    65. print("Couldn't write to file: \(error.localizedDescription)")
    66. }
    67. }
    68. }
    Alles anzeigen



    In der Funktion convertToJSONArray muss ich irgendwie eingreifen und in dem Moment, wenn sie bei der NSDate - Property angekommen ist statt:


    Quellcode

    1. dict[attribute.key] = item.value(forKey: attribute.key)


    sowas wie:


    Quellcode

    1. var NSDateValue = NSDate()
    2. if(item.value(forKey: attribute.key) != nil){
    3. NSDateValue = item.value(forKey: attribute.key) as! NSDate
    4. let dateExample = Date()
    5. let formatter = DateFormatter()
    6. formatter.timeZone = TimeZone.current
    7. formatter.dateStyle = .short
    8. formatter.timeStyle = .medium
    9. formatter.locale = Locale.current
    10. var NSDateString = formatter.string(from: NSDateValue)
    11. }else{
    12. NSDateValue = Date()
    13. let dateExample = Date()
    14. let formatter = DateFormatter()
    15. formatter.timeZone = TimeZone.current
    16. formatter.dateStyle = .short
    17. formatter.timeStyle = .medium
    18. formatter.locale = Locale.current
    19. var NSDateString = formatter.string(from: NSDateValue)
    20. }
    21. dict[attribute.key] = NSDateString
    Alles anzeigen


    Ich finde keinen Weg, wie ich prüfen kann, ob ich bei dem NSDate-Property angekommen bin (beim durchlauf aller Properties und dadurch kommt es beim Export als JSON zum Fehler da die Formatierung nicht angenommen wird.

    Irgendeine Idee? :D
    Neu in der IOS-App-Entwicklungswelt - Spannend, ab und an nervenaufreibend, aber am Ende einfach faszinierend. Seid bitte nicht zu streng wenn ich Fragen einmal doppelt stelle, ich lerne noch :saint:
  • CodeNerd schrieb:


    Ich finde keinen Weg, wie ich prüfen kann, ob ich bei dem NSDate-Property angekommen bin (beim durchlauf aller Properties und dadurch kommt es beim Export als JSON zum Fehler da die Formatierung nicht angenommen wird.

    Irgendeine Idee? :D
    Ich habe jetzt nicht Deinen ganzen Code durchgeschaut, verstehe aber nicht ganz, warum Du etwas händisch lösen willst, für das es gute und fertige Verfahren gibt. Welchen Vorteil siehst Du in der Verwendung eines JSON statt Serialisieren eines NSDictionaries? Dieses unterstützt wie NSDate das NSSecureCoding-Protokoll, und damit brauchst Du Dich um keine Umwandlung kümmern. Das Ergebnis ist vielleicht nicht so leicht editierbar, aber diesen Anspruch hast Du bei einer Backup-Datei doch auch gar nicht. Eher im Gegenteil...

    Ich würde Deine Datensätze in ein Array von Dictionaries packen und dieses per writeToURL: persistieren.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • MyMattes schrieb:

    CodeNerd schrieb:

    Ich finde keinen Weg, wie ich prüfen kann, ob ich bei dem NSDate-Property angekommen bin (beim durchlauf aller Properties und dadurch kommt es beim Export als JSON zum Fehler da die Formatierung nicht angenommen wird.

    Irgendeine Idee? :D
    Ich habe jetzt nicht Deinen ganzen Code durchgeschaut, verstehe aber nicht ganz, warum Du etwas händisch lösen willst, für das es gute und fertige Verfahren gibt. Welchen Vorteil siehst Du in der Verwendung eines JSON statt Serialisieren eines NSDictionaries? Dieses unterstützt wie NSDate das NSSecureCoding-Protokoll, und damit brauchst Du Dich um keine Umwandlung kümmern. Das Ergebnis ist vielleicht nicht so leicht editierbar, aber diesen Anspruch hast Du bei einer Backup-Datei doch auch gar nicht. Eher im Gegenteil...
    Ich würde Deine Datensätze in ein Array von Dictionaries packen und dieses per writeToURL: persistieren.

    Mattes
    Das hatte den Grund, dass ich eventuell irgendwann doch noch Daten mit einem Server austauschen möchte und daher dachte ich, dass es vielleicht ganz schlau wäre, diese Funktion direkt zu entwickeln und dann fürs Backup und den eventuell späteren Datenaustausch zu nutzen.

    Ich habe heute auch die relativ simple Lösung dazu gefunden. Statt den Namen des Properties auszulesen prüfe ich einfach den Wert bzw. den Typ der aus der CoreData-DB kommt.

    Quellcode

    1. if(item.value(forKey: attribute.key) is NSDate){
    2. }

    Manchmal ist es so einfach, wenn man es schon gekannt hätte ^^

    :thumbsup:
    Neu in der IOS-App-Entwicklungswelt - Spannend, ab und an nervenaufreibend, aber am Ende einfach faszinierend. Seid bitte nicht zu streng wenn ich Fragen einmal doppelt stelle, ich lerne noch :saint: