Zeitnahe Synchronisation über CloudKit möglich / Auf Änderung des Datenbestands reagieren?

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

    • Zeitnahe Synchronisation über CloudKit möglich / Auf Änderung des Datenbestands reagieren?

      Guten Abend!

      Ich arbeite an einer App, die Daten in CoreData speichert und jeden Tag zufällig einen oder mehrere dieser Einträge wieder ausgibt. Hierzu speichere ich für jeden Eintrag eine ID und generiere aus allen IDs ein Array mit der aktuellen Reihenfolge. Bei jedem Öffnen der App vergleiche ich das aktuelle Datum mit dem zuletzt gespeicherten und lösche die erste Stelle des Arrays, wenn das hinterlegte Datum älter ist als heute. Die jeweils erste Stelle des Arrays wird dann angezeigt.
      Das funktioniert soweit alles problemlos. Nun möchte ich allerdings gerne die Option hinzufügen, die Einträge auch via CloudKit zu synchronisieren. Idealerweise sollte auch die aktuelle Reihenfolge abgeglichen werden, sodass zwei Installationen am selben Tag immer auch denselben Eintrag anzeigen.

      Programmiertechnisch kriege ich das wohl hin, allerdings bin ich mir unsicher, ob das technisch mit CloudKit überhaupt sinnvoll zu realisieren ist? Nach meinem Verständnis habe ich ja keine Kontrolle darüber, wann genau CloudKit die Daten synchronisiert. Wenn ich nun z.B. zwei Installationen habe, auf Gerät 1 die App zwei Tage lang nutze, entsprechend mein Array um zwei Einträge kürze, und dann die Installation auf Gerät 2 öffne, (wie) kriege ich das hin, dass Gerät 2 früh genug weiß, dass es ebenfalls die ersten zwei Einträge aus dem Array streichen muss, bevor es etwas anzeigt?

      Ich hoffe, ich konnte mein Problem halbwegs verständlich darstellen. Für Ideen, ob bzw. wie das zu realisieren wäre, wäre ich sehr dankbar!

      Beste Grüße
      Michael
    • Ich bin nicht ganz sicher, Deine Fragestellung genau verstanden zu haben. Wenn Du allerdings Deinen NSPersistentContainer in einen NSPersistentCloudKitContainer umstellst und einen NSFetchedResultsController verwendest, erhälst Du eine Synchronisierung Deines Core-Data-Models quasi umsonst und Notifications über Änderungen des Data-Stores.

      Vielleicht lässt sich Dein Datenmodell ja komplett auf Core Data umstellen. Die Synchronisation läuft übrigens asynchron, so dass die App Änderungen zur Laufzeit händeln muss...

      Mattes
      Diese Seite bleibt aus technischen Gründen unbedruckt.
    • Hallo Mattes,

      den NSPersistentCloudKitContainer verwende ich bereits. Die reine Synchronisierung der Einträge in CoreData klappt damit auch schon gut. Für den Abgleich verschiedener Werte, u.a. der gewünschten zufälligen Sortierung der Einträge, will ich eigentlich NSUbiquitousKeyValueStores nutzen, das habe ich aber noch nicht fertig gebaut. Das Ganze ist ein Hobbyprojekt und die letzten Tage hatte ich leider wenig Zeit dafür.

      Im Zuge dessen kam ich aber eben dazu, erstmal grundsätzlich zu hinterfragen, ob sich die Funktionalität so umsetzen lässt, wie von mir angedacht. Die Synchronisierung der Daten hat im Simulator ja schon eine deutliche Latenz und wird ja eben während der Laufzeit durchgeführt. Damit stehe ich vor dem Problem, dass meine App womöglich oder wahrscheinlich schon den View erstellt und ausgibt, bevor sie überhaupt die aktuellsten Daten von CloudKit gezogen hat. Und so, wie ich den Abgleich über CloudKit verstehe, habe ich offenbar keine Möglichkeit, solange z.B. einen Ladeindikator anzuzeigen, bis ich sicher weiß, dass Cloud und lokale Daten abgeglichen wurden.

      Ich hoffe, das erklärt meine Problematik ein wenig genauer?
    • Danke, damit wird mir Dein Problem klarer, auch wenn ich noch nicht verstehe, welchen Nutzen das Array gegenüber einer Speicherung der zufällig ausgewählten Datensatz-ID und des Tagesdatums hat.

      Ich glaube, Du wirst hier mit iCloud-Synch nicht glücklich werden: Edit: Vielleicht doch, siehe nächsten Beitrag :)
      • Durch die Verzögerung ist nicht klar, wann (oder ob) die App aktuelle Daten hat. Die App solange zu "blockieren" ist m. E. keine Option.
      • Der Key-Value-Store mag die Problematik mindern (wenige Daten), aber nicht lösen.
      • Bei iCloud-Drive gibt es NSFileManager-Methoden, bei denen man auf den Download warten kann. Aber das wäre nur ein Hack und löst auch nicht das eigentliche Problem.
      Wenn Deine App-Einstellungen für Datensatz und Datum nicht benutzerspezifisch wären, könnte eine kleine Datei auf einem Web-Server helfen, die Du explizit requestest. Natürlich müsstest Du auch da mit Latenz / Nicht-Erreichbarkeit umgehen, hättest aber volle Kontrolle über den Prozess.

      Ich würde versuchen, den Ansatz zum Zeigen eines Datensatzes zu ändern, um ihn autarker zu machen: Warum nicht einen Algorithmus schreiben, der deterministisch auf Basis des Tagesdatums einen Datensatz "pseudo-zufällig" ermittelt. Dieser müsste - bei gleichen Datensätzen - immer den gleichen Satz für einen Tag ermitteln und würde so keine Synchronisierung erfordern. Ähnlich einer Token-Generierung bei einer SecureID-Card.

      Es bliebe allerdings immer noch das Problem, ob die Datenbasis synchronisiert wurde, hier fällt mir noch nichts gutes ein, eine fehlende Synchronisierung zu erkennen. Die Wichtigkeit hängt etwas davon ab, in welcher Frequenz die Datensätze geändert werden...

      Mattes
      Diese Seite bleibt aus technischen Gründen unbedruckt.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MyMattes ()

    • MyMattes schrieb:

      Es bliebe allerdings immer noch das Problem, ob die Datenbasis synchronisiert wurde, hier fällt mir noch nichts gutes ein, eine fehlende Synchronisierung zu erkennen. Die Wichtigkeit hängt etwas davon ab, in welcher Frequenz die Datensätze geändert werden...
      Eine alternative Idee, um dieses Problem abzuschwächen - und hier komme ich wieder auf Dein Array zurück:

      Warum nicht in einer eigenen Core-Data-Entität die Tagesdaten der z. B. nächsten Woche zusammen mit zufällig ermittelten Datensatz-IDs speichern. Diese wird auch synchronisiert und die App entfernt veraltete Einträge und nutzt den des aktuellen Datums zur Anzeige des entsprechenden Datensatzes. Wenn das Array nur noch sechs Tage abdeckt, wird ein neuer Eintrag erstellt und mit synchronisiert. Damit schlägst Du m. E. mehrere Fliegen mit einer Klappe:
      1. Die Zeit ohne Synchronisierung kann bis zu eine Woche umfassen und die App zeigt dennoch auf allen Geräten den gleichen Datensatz an.
      2. Aufgrund des Datums erkennst Du, falls das Array den aktuellen Tag - weil länger nicht synchronisiert - nicht mehr abdeckt und kannst eine ordentliche Fehlerbehandlung vornehmen.
      3. Ein Gerät hat automatisch immer den Datensatz für einen Tag im Repository: Wenn das Tag-ID-Pärchen vorhanden ist, wurde Core Data ja synchronisiert und somit auch der Datensatz.
      Ich hoffe, die Morgenstunde hat keinen Knoten in mein Gehirn gemacht.

      Mattes
      Diese Seite bleibt aus technischen Gründen unbedruckt.
    • Hallo Mattes,

      besser spät, als nie: Vielen lieben Dank für den tollen Lösungsansatz! Ich habe die Arbeit an meiner App letztes Jahr begonnen, als ich Corona-bedingt für einige Wochen bezahlt zuhause saß und seit im letzten Sommer die Arbeit dann wieder rief, fehlte mir leider oft die Zeit dafür. Aber mittlerweile konnte ich Deine Idee mit der zweiten CoreData-Entität mit der Sortierung in die Tat umsetzen und tatsächlich funktioniert sie zumindest in meinen bisherigen Tests ziemlich gut!

      Ein paar eher grundsätzliche Verständnis-Schwierigkeiten habe ich noch hinsichtlich des Umgangs mit den Datums-Werten. Ich hoffe, es ist ok, dass ich diesen Thread nochmal dafür hochhole.

      In meiner App gibt es an mehreren Stellen noch Fehler, die ich letztlich auf die Tatsache zurückführen konnte, dass Swift und CoreData mit Datumswerten in UTC arbeiten, ich ja aber in meiner jeweiligen Zeitzone agiere. Wenn ich z.B. die Liste mit künftigen Daten generiere, ist effektiv gerade alles um einen Tag verschoben, wenn ich die Daten basierend auf der aktuellen Zeit generiere:

      Quellcode

      1. let today = Date()
      2. let calendar = Calendar.current
      3. let startToday = calendar.startOfDay(for: today)
      4. //...
      5. let newDate = calendar.date(byAdding: .day, value: 6, to: startToday)


      So einigermaßen habe ich verstanden, dass meine Zeitzone der Standardzeit + 2h entspricht, daher startOfDay() in UTC 22 Uhr draus macht – jedoch verstehe ich schon nicht mehr so ganz, warum dies auch gleich das Datum um einen Tag zurücksetzt (z.B. print(today) ergibt 2021-06-23, print(startToday) ergibt 2021-06-22). Was sich mir auch nicht erschließt, ist, dass das in CoreData gespeicherte Datum dann wiederum einen Tag höher ist als gewollt – sprich print(newDate) ergibt z.B. 2021-06-29, in CoreData ist anschließend aber 2021-06-30 gespeichert.

      Ich habe jetzt schon mehrere Tage dazu recherchiert, aber stehe irgendwie auf dem Schlauch – auch bezüglich der Frage, wie ich damit in meiner App umgehen soll. Mein Ansatz wäre, basierend auf der jeweiligen eingestellten Zeitzone des Gerätes Null Uhr des aktuellen Tages zu berechnen, diesen Wert in UTC umzuwandeln und davon ausgehend alle weiteren Tage zu berechnen bzw. den Abgleich durchzuführen, welcher meiner Einträge gerade gezeigt werden soll. Allerdings fehlt mir auch hier noch der Ansatz, wie ich das genau machen kann. Alles, was ich im Internet dazu finde, dreht sich eher um die Ausgabe von Zeiten.

      Ich hoffe, meine Problematik ist einigermaßen nachvollziehbar und nicht zu konfus. Über Tipps oder hilfreiche Literatur dazu wäre ich sehr dankbar!

      Viele Grüße
      Michael
    • Gehirnduebel schrieb:


      So einigermaßen habe ich verstanden, dass meine Zeitzone der Standardzeit + 2h entspricht, daher startOfDay() in UTC 22 Uhr draus macht – jedoch verstehe ich schon nicht mehr so ganz, warum dies auch gleich das Datum um einen Tag zurücksetzt (z.B. print(today) ergibt 2021-06-23, print(startToday) ergibt 2021-06-22). Was sich mir auch nicht erschließt, ist, dass das in CoreData gespeicherte Datum dann wiederum einen Tag höher ist als gewollt – sprich print(newDate) ergibt z.B. 2021-06-29, in CoreData ist anschließend aber 2021-06-30 gespeichert.
      Zeitarithmetik ist oft etwas fies :) Ich würde es mir einfach machen, und intern immer mit UTC arbeiten. Die lokale Zeit wird dann nur bei Ausgaben errechnet.

      Aber letztlich hängt es von Deiner Aufgabenstellung ab: Soll jetzt weltweit der gleiche Datensatz angezeigt werden (und mit welchem Tagesdatum?) oder soll am 24. Juni 2021 überall der gleiche Datensatz angezeigt werden? Ich vermute letzteres und dann läuft es komplett mit UTC-Zeit sauber. Andernfalls müsstest Du zur Anzeige des Datensatzes mit dem Tag der lokalen Zeit arbeiten.

      Deine Tagesdifferenz liegt daran, dass es nicht 24:00 Uhr gibt, dies ist 0:00 des Folgetages. Somit ergibt 0:00 CEST eben 22:00 UCT des Vortages...

      Mattes
      Diese Seite bleibt aus technischen Gründen unbedruckt.