Der neue Frühling der guten alten Telefonnummer

google-hangout

Kommunikation braucht Absender, Empfänger und typischerweise auch deren Adresse (ausser sie treffen sich per Zufall auf der Strasse). Bei einem Telefongespräch beispielsweise die gute alte Telefonnummer. Anbieter von Online-Anwendungen haben Identifikation meist mit Usernamen oder Mail mit E-Mail-Adressen ‚gelöst’. Dies … Weiterlesen

Core Data Multithreading mit MagicalRecord

Nach einigen Abstürzen aufgrund von Zugriffen auf einen NSManagedObjectContext (Kontext) durch mehrere Threads haben wir nach einer Lösung gesucht, die unseren Code robuster macht. Das durch nutzlose Fehlermeldungen mühsame und zeitaufwändige Debugging solcher Fehler wollten wir unbedingt vermeiden.

Die Grundregeln bei dem Umgang mit Core Data Multithreading lesen sich einfach:

  • Jeder Kontext darf nur von dem Thread aus verwendet werden, auf dem er erstellt wurde.
  • NSManagedObjects (Objekte) sind fest an ihren Kontext gebunden. Nur ihre IDs dürfen Kontext- und damit Threadgrenzen passieren.

Unser Projekt enthält zahlreiche NSOperations (Operationen), die vom System auf einem Hintergrund-Thread ausgeführt werden.  Zur Einhaltung der oben genannten Regeln wird am Anfang jeder Operation ein neuer Kontext erstellt. Dieser Kontext wird nun über mehrere Hierarchien an Hilfsobjekte weitergereicht, um beispielsweise Objekte über NSFetchRequests zu holen.

Folgende Probleme ergeben sich dadurch:

  1. Die Signaturen vieler Methoden werden durch einen Kontext-Parameter aufgebläht.
  2. Eine Hilfsmethode, die intern den Standard-Kontext verwendet, wird von einem zweiten Entwickler in gutem Glauben aus einer Operation heraus aufgerufen.
  3. Die Übergabe von nil als Kontext an Hilfsmethoden ist zum Synonym für den Main-Thread-Kontext (Standard-Kontext) geworden. Dieser Automatismus kann aber für Fehler sorgen, wenn man vergisst den korrekten Kontext mitzugeben.

 

Magical Record

Offensichtlich gibt es einen engen Zusammenhang zwischen Kontext und Thread. Also warum wird der Kontext nicht einfach an den Thread gebunden und passend zum aktuellen Thread automatisch gewählt. Mit diesem Gedanken stößt man bei der Recherche auf Github schnell auf MagicalRecord.

Inspiriert von der Active Record Implementierung in Ruby on Rails bietet MagicalRecord zum einen Methoden zum Importieren von Daten und zum anderen die kompakte Erstellung von Fetch-Requests wie im folgenden Beispiel:

[User findFirstByAttribute:@“phone“ withValue:@“012“]

Ohne MagicalRecord wäre die Erstellung des gleichen Fetch-Requests mindestens fünf Zeilen lang.

Unterliegend verwendet MagicalRecord sogenannte Nested-Contexts, die Apple mit iOS 5 eingeführt hat. Dabei hat ein Kontext keinen NSPersistentStore zum Speichern, sondern einen Eltern-Kontext. Beim Aufruf von „save:“ werden die Änderungen in den Eltern-Kontext und nicht direkt in den NSPersistentStore geschrieben.

Das Einbinden von MagicalRecord gestaltete sich in unserem Fall unter iOS 6 erstaunlich einfach. Nach der Initialisierung des defaultContext mit Hilfe des eigenen NSPersistentStoreCoordinators, erstellt MagicalRecord alle weiteren Kontexte automatisch.

Diese bekommt man sehr einfach mit der statischen Methode contextForCurrentThread der NSManagedObjectContext-Klasse geliefert. Gibt es für den aktuellen Thread keinen Kontext, wird einer erstellt.

In unserem Projekt haben wir nun alle Zugriffe auf den Standard-Kontext durch den aktuellen Thread-Kontext ersetzt. Beim Zugriff vom Main-Thread entspricht dies dem defaultContext.

Der erstellte Thread-Kontext hat automatisch den defaultContext als Eltern-Kontext. Dies entspricht in etwa dem häufigen Fall (ohne Nested-Contexts), dass der Standard-Kontext den Thread-Kontext observiert und alle Änderungen abspeichert.

Es hat sich bewährt, am Anfang und am Ende jeder Operation den contextForCurrentThread mit reset zurück zu setzen, damit nicht versehentlich alte Änderungen im Kontext verbleiben, denn der contextForCurrentThread wird wiederverwendet, solange der Thread nicht vom System abgeräumt wurde.

Zum Speichern bietet MagicalRecord verschiedene Methoden an. Bei Umstellung von einem bestehenden Projekt ohne Nested-Contexts sollte man alle Kontext save: Methoden durch saveToPersistentStoreAndWait ersetzen, da bei beiden Methoden alle späteren Aufrufe davon ausgehen können, dass alle Daten in den NSPersistentStore gespeichert wurden.

Will man den aktuellen Thread nicht blockieren, so kann man auch asynchron mit saveToPersistentStoreWithCompletion: speichern. Aufwändiger wird es, wenn man bisher den Erfolg der save: Methode ausgewertet hat. In diesem Fall muss man alles was nach dem Speichern passieren soll manuell in den completionBlock von saveToPersistentStoreWithCompletion: verlegen.


[context saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
      if (success) ...
}];

 

Ergebnis iOS 6

Ein Abgleich des contextForCurrentThread mit unseren manuell durchgereichten Kontexten hat einige Fehler in unserem alten Code aufgespürt. Alle drei am Anfang aufgezählten Probleme sind aufgetreten und durch threadgebundene Kontexte gelöst worden.

Man muss natürlich weiterhin selbst darauf achten, dass man NSManagedObjects nicht über Threadgrenzen hinweg verwendet.

Will man explizit einen anderen Kontext verwenden, so kann man eine Version des persistierten Objekts durch die Methode inContext: im gewünschten Kontext bekommen.

[[Customer findFirst] inContext:differentContext]

 

Desaster auf iOS 5

Nach den Umstellungen lief die App unter iOS 6 problemlos. Ein Test auf iOS 5 war dagegen ein Desaster. Die App ist nicht einmal mehr gestartet.

Innerhalb von performFetch: eines NSFetchedResultsControllers ist es reproduzierbar zu Deadlocks gekommen. Sinnvolle Fehlermeldungen gab es nicht. Aber das war nicht das einzige Problem: Nested-Contexts auf iOS 5 führen zu Deadlocks, fehlerhaften Suchergebnissen, falschen Sortierungen und Abstürzen.

Eines wird daraus klar: Nested-Contexts wurden zwar unter iOS 5 eingeführt, aber sind so fehleranfällig, dass man sie erst ab iOS 6 in einer produktiven Umgebung einsetzen sollte. Apple hat an dieser Stelle leider geschlafen.

Dieser Blog liefert eine Beschreibung der oben genannten Probleme.

 

Magical Record ohne Nested-Contexts

Da wir MagicalRecords Fetching, Logging, ErrorHandling, etc. weiter verwenden wollten, haben wir alle Abhängigkeiten zu Nested-Contexts aus dem Framework entfernt und lassen den defaultContext andere Kontexte observieren.

Das Erstellen von Kontexten, Speichern und alle blockbasierten Methoden mussten dazu angepasst oder entfernt werden.

 

Automatische Tests

Beim automatischen Testen eines Projekts ersetzen wir vor jedem Test MagicalRecords defaultContext durch einen neuen Kontext der einen NSInMemoryStore verwendet. Da alle anderen Kontexte vom defaultContext abgeleitet werden und daher den selben NSPersistentStore verwenden, muss man ansonsten nichts weiter beachten.

 

Fazit

Trotz der wichtigen Lehre, dass Nested-Contexts unter iOS 5 unbrauchbar sind und des damit verbundenen zusätzlichen Aufwands hat sich die Arbeit gelohnt. Dank MagicalRecord und etwas Handarbeit ist der Code jetzt zentralisierter, kürzer und robuster geworden. Kontexte werden passend zum Thread automatisch gewählt, Fehler werden vermieden, Abstürze wurden beseitigt und als Entwickler kann man wieder ruhig schlafen.

Dirty Tricks mit Axure: Responsive.

Axure

Axure ist nach wie vor die erste Wahl beim Entwickeln von Prototypen fürs Web. Auch wenn die Integration dynamischer Funktionen die Graubärtigen unter uns an vielen Stellen noch an Flash 3 erinnert: Axure bietet jede Menge schöner Möglichkeiten, Web-Konzepte mit … Weiterlesen

Ersatzteile für Windows Phone

Die sogenannte “Ersatzteil-App” ist ein Produkt, das wir für den Hersteller von Heiztechnik-Systemen Viessmann entwickelt haben. Es besteht im Wesentlichen aus einer hybriden Lösung, die die Vorzüge einer reinen Web-Applikation mit den technischen Möglichkeiten der mobilen Plattformen in Form nativer Apps verbindet. Sie soll dem Fachhandwerker als Werkzeug und Nachschlagewerk für den umfassenden Produktkatalog Viessmann’s dienen.

Umgesetzt hatten wir diese Applikation vorerst ausschließlich für Apples iOS- und Googles Android-Plattformen, da sie den Markt derzeit gemeinsam wie kaum ein anderer dominieren. Für die übrigen mobilen Systeme war bislang also lediglich eine Web-Applikation verfügbar, ohne erweiterte Funktionen, wie den integrierten Barcode-Scanner.

Im Zuge der parallelen Markteinführung von Windows 8 und Windows Phone 8 durch Microsoft im Herbst letzten Jahres und unter der Annahme, die Plattformen hätten die Voraussetzungen, sich hinter den beiden Großen als dritte Kraft am Markt zu etablieren, haben wir Viessmann den Vorschlag gemacht, die Plattform-Abdeckung der hybriden Apps um Windows zu erweitern. Infolge dessen bekamen wir die Möglichkeit, Erfahrung mit dieser Plattform zu sammeln und die erste Applikation für Windows Phone 8 in unserem Hause zu entwickeln.

Plattform

Grundsätzlich teilen sich Windows 8 und Windows Phone 8 den selben Kern. Das heisst, es gibt die Möglichkeit, z.B. über Portable Class Libraries Logik für beide Plattformen zu schreiben. Da es aber nicht wie für iOS möglich ist, sogenannte Universal Apps zu entwickeln, die auf beiden Plattformen lauffähig sind, müssen zumindest die Views und zum Teil sogar Plattform-spezifische Implementierungen getrennt umgesetzt werden.

Durch diesen gemeinsamen Kern sind Windows Phone 8 Apps unter Windows Phone 7 nicht lauffähig. Umgekehrt können Windows Phone 7 Apps allerdings auf Windows Phone 8 Geräten ausgeführt werden, da hier explizit auf Rückwärtskompatibilität geachtet wurde. Wir haben uns für die Umsetzung gegen die Unterstützung von Windows Phone 7 entschieden, um die technische Nähe zu Windows 8 nutzen zu können.

Umsetzung

Das Setup für die Entwicklung ist unkompliziert und schnell erledigt. Neben Visual Studio 2012 ist lediglich das Windows Phone SDK erforderlich. Um das allerdings vollständig nutzen zu können, sollte ein PC mit Windows 8 Pro als Entwicklungsgerät verwendet werden.

Um Unit Tests innerhalb des Emulators einsetzen zu können, haben wir das Windows Phone Toolkit Test Framework von Microsoft verwendet. Es ermöglicht die manuelle Ausführung der Tests über eine separate Applikation und lässt sich leicht integrieren. Matteo Pagani hat hierzu auf seinem Blog einen hilfreichen Artikel geschrieben.

Für die Umsetzung des Barcode-Scanners haben wir ZXing.Net von Michael Jahn verwendet, das einer Portierung der Java-Bibliothek ZXing entspricht. Die Herausforderung bei der Integration lag hierbei vor allem in der Übergabe der Kameradaten. Die Untersuchung der Beispiel-Implementierungen war hier bei der Lösung eine große Hilfe.

Etwas abenteuerlich war die Umsetzung lokalisierter Titel für die App. Was unter iOS recht einfach über die Lokalisierung der Info Property List realisiert werden kann, erfordert für Windows Phone die Verwendung einer speziellen Dynamic Link Library, sowie die Erstellung entsprechender Multilingual User Interface Dateien. Folgt man der ausführlichen Anleitung von Microsoft, lässt sich das Thema allerdings schneller lösen, als es zuerst den Anschein hat. Eine elegantere Lösung wäre hier zukünftig wünschenswert.

Fazit

Das Projekt war sehr lehrreich und die Umsetzung hat insgesamt großen Spaß gemacht. Die Einarbeitung anhand der bereitgestellten Dokumentation gestaltet sich angenehm und die Werkzeuge zur Umsetzung erleichtern die Entwicklung spürbar, wobei hier insbesondere der schnelle Emulator positiv zu erwähnen ist. Auf der Suche nach Problemlösungen im Netz stellt man schnell fest, dass die Plattform noch sehr jung ist und demnach die damit arbeitenden Entwickler schlicht nicht so zahlreich vertreten sind. Zudem bieten vorhandene Komponenten, wie z.B. eine WebView, hier schlicht noch Raum für zukünftige Erweiterungen, was die Funktionalität und mögliche Einflussnahme durch den Entwickler angeht. Ich bin allerdings optimistisch, dass Microsoft diesbezüglich bereits Ende des Jahres nachliefern wird.

Windows Phone 8 im Dauertest

Startbildschirm eines Windows Phone 8 Geräts

“Wow, ist das Scrolling flüssig!”. Diesen Satz höre ich immer dann, wenn ich jemanden mein Nokia Lumia 820 zeige: Das Scrolling ist butterweich und die Bildschirmübergänge durchdacht animiert. Die kantige Oberfläche wirkt frisch und hebt sich angenehm von Android und … Weiterlesen

Veröffentlicht unter Mobile

jQuery Europe 2013, Tag 2

jquery-europe

Am 20 bis 22. Februar fand der europäische Ableger der jQuery Conference statt. Dieser Post besteht aus einer Zusammenfassung von Tag 2 der Konferenz. Die Zusammenfassung von Tag 1 wurde letzte Woche veröffentlicht. jQuery Mobile and Responsive Web Design – … Weiterlesen

Nutzung von Apps im B2B-Umfeld

iPad-Kundenberatung

Spricht man von Mobilen Apps denkt man an erfolgreiche Apps wie Angry Birds, Instagram oder den WhatsApp Messenger. Die meisten der Apps, die ganz oben auf den Chartlisten von Google, Apple und Microsoft zu finden sind, richten sich klar an … Weiterlesen

jQuery Europe 2013, Tag 1

jqconfeu13

Am 20 bis 22. Februar fand der europäische Ableger der jQuery Conference statt. Als Austragungsort wurde das verschneite Wien gewählt; und mit dem Palais Liechtenstein ein würdiger Veranstaltungsort gefunden (siehe Bilder unten). Rund 400 Besucher trafen sich in Österreich um sich an einem … Weiterlesen

Adaptiver Content: Die Content Strategie der Zukunft

Adaptiver Content wird einmal erstellt und überall  plattformoptimiert publiziert.

Wäre es nicht schön, Content nur einmal in einem einzigen System zu erstellen und überall in optimaler Form sprich plattform- und kontextspezifisch zu publizieren? Unmöglich, meinen Sie? – Keinesfalls! Die Lösung lautet adaptiver Content und ist in den USA bereits … Weiterlesen

Seite 1 von 812345...Letzte »