{"id":2597,"date":"2013-08-06T08:52:12","date_gmt":"2013-08-06T07:52:12","guid":{"rendered":"http:\/\/www.thetawelle.de\/?p=2597"},"modified":"2013-08-06T10:03:02","modified_gmt":"2013-08-06T09:03:02","slug":"seewetter-pro-cllocationmanager-background-location-tracking","status":"publish","type":"post","link":"https:\/\/www.thetawelle.de\/?p=2597","title":{"rendered":"Seewetter Pro: CLLocationManager &#038; Background Location Tracking"},"content":{"rendered":"<p><a href=\"\/wp-upload\/seewetterpro_logo_icon.png\"><img loading=\"lazy\" decoding=\"async\" data-id=\"2618\"  src=\"\/wp-upload\/seewetterpro_logo_icon.png\" alt=\"seewetterpro_logo_icon\" width=\"195\" height=\"190\" class=\"alignright size-full wp-image-2618\" \/><\/a>Derzeit bin ich gerade dabei meine App <strong>Seewetter Pro<\/strong> (<a href=\"https:\/\/itunes.apple.com\/de\/app\/seewetter-pro\/id384086202?mt=8\">im AppStore<\/a>, <a href=\"https:\/\/www.facebook.com\/pages\/Seewetter-Pro\/353609904694719\">auf Facebook<\/a>, <a href=\"https:\/\/twitter.com\/seewetterpro\">bei Twitter<\/a>) zu aktualisieren. Ein ganz besonderes Feature dieser App ist u.a. der <strong>Ankeralarm<\/strong>, eine Funktion, die mit Hilfe des GPS eines iPhone\/iPad die Position eines Segelbootes vor Anker \u00fcberwacht. Damit diese \u00dcberwachung \u00fcber mindestens volle 8 Stunden funktioniert, muss die \u00dcberwachung im Hintergrund funktionieren, denn die App kann nicht einfach 8 Stunden lang das Display an lassen, und die Nutzer wollen nat\u00fcrlich auch zwischendurch nochmal ihre Mails einsehen k\u00f6nnen trotz \u00dcberwachung.<\/p>\n<p>Das jedoch bedeutet, die Positions\u00fcberwachung muss auch dann 100 Prozent funktionieren, wenn die App im Hintergrund ist und dazu geh\u00f6rt z.B. wenn ich kurz den LockScreen sehe (Lock-Taste gedr\u00fcckt oder AutoLock), oder wenn ich den Homebutton doppeltippe um die Taskbar unten zu sehen und es geh\u00f6rt ganz klassisch dazu die App zu schlie\u00dfen mit dem Home-Button. Das Ger\u00e4t schaut dann aus als w\u00fcrde es nichts tun, es tut aber doch etwas, und das merkt man dann auch sp\u00e4ter am Stromverbrauch.<\/p>\n<p><center><a href=\"\/wp-upload\/anchoralarm_hack.png\"><img loading=\"lazy\" decoding=\"async\" data-id=\"2599\"  src=\"\/wp-upload\/anchoralarm_hack_550.png\" alt=\"anchoralarm_hack_550\" width=\"550\" height=\"261\" class=\"aligncenter size-full wp-image-2599\" srcset=\"https:\/\/www.thetawelle.de\/wp-upload\/anchoralarm_hack_550.png 550w, https:\/\/www.thetawelle.de\/wp-upload\/anchoralarm_hack_550-300x142.png 300w\" sizes=\"auto, (max-width: 550px) 85vw, 550px\" \/><\/a><br \/>\n<small>Vier Screenshots vom Ankeralarm auf einem iPod 5<\/small><\/center><\/p>\n<p>Hintergrundaktivit\u00e4ten sind von Apple allerdings starken Einschr\u00e4nkungen unterworfen, und eine ebensolche Einschr\u00e4nkung habe ich unter iOS 6 bemerkt als ich jetzt mein Update freigeben wollte und einen letzten Test gemacht habe. Obwohl meine App zu der Kategorie Apps geh\u00f6rt, denen Hintergrundaktivit\u00e4t explizit erlaubt ist (weil die Positions\u00fcberwachung per GPS zu dieser Art Funktionen geh\u00f6rt, f\u00fcr die es erlaubt ist) funktionierte der Ankeralarm pl\u00f6tzlich nur noch ca. 5 Stunden lang (ja ich f\u00fchre Echttests durch die mehr als 8 Stunden dauern). Gro\u00dfe Verwunderung machte sich bei mir breit, denn der Algorithmus den ich daf\u00fcr implementiert hatte lief noch unter iOS 5 perfekt den ganzen Tag hindurch beliebig lange.<\/p>\n<p>Nun muss ich dazu sagen, dass Apple f\u00fcr Hintergrund GPS Nutzung eine spezielle Art vorgibt, wie das zu funktionieren hat. Der <strong>CLLocationManger<\/strong> (des CoreLocation Framework) muss daf\u00fcr in exakt dem Moment, in dem die App in den Hintergrund geht seinen <strong>CLLocationMangerDelegate<\/strong> gesetzt haben. Der Delegate muss die Methode <strong>didUpdateLocations<\/strong> implementieren, die dann aufgerufen wird sobald der CLLocationManager eine neue Position ermittelt hat. Beim in-den-Hintergrund-gehen muss man (auch wenn CLLocationManager zuvor schon Positionen \u00fcbermittelt hat also aktiv war) <u>erneut<\/u> <strong>startUpdatingLocation<\/strong> aufrufen in einem Extrathread. Und das ist unter iOS 6 definitiv neu &#038; leider notwendig.<\/p>\n<p>Soweit so unkompliziert. Im Hintergrund wird nun obwohl der Rest der App &#8222;schl\u00e4ft&#8220; (z.B. s\u00e4mtliche NSTimer) einzig <strong>didUpdateLocations<\/strong> noch aufgerufen. Wie genau das passiert ist mir immer noch unklar, denn der Aufruf im Hintergrund unterliegt so einigen Beschr\u00e4nkungen. So ist es z.B. nicht m\u00f6glich ein Audio auszugeben im Hintergrund. Zumindest nicht innerhalb dieses <strong>didUpdateLocations<\/strong>-Aufruf und das obwohl ich auch das Hintergrundflag f\u00fcr Audio gesetzt hatte. F\u00fcr den Ankeralarm war das leider keine L\u00f6sung. Ich brauche unbedingt Audiofeedback f\u00fcr den Nutzer (siehe Screenshot 3).<\/p>\n<p>Ein Workaround stellen sogenannte Backgroundtasks dar, die man sobald die App in den Hintergrund geht anmelden kann, und die je nachdem ca. 10 bis 20 Minuten lang laufen d\u00fcrfen, dann werden diese aber beendet. Einen solchen Backgroundtask hatte ich unter iOS 5 eingerichtet, dieser hatte seinen eigenen NSTimer und war in der Lage z.B. alle 10 Sekunden ein Audio abzuspielen (z.B: einen &#8222;Beep&#8220;), was in <strong>didUpdateLocations<\/strong> ja nicht m\u00f6glich war. So konnte man gut erkennen, ob im Hintergrund noch alles l\u00e4uft, weil man es ja auch h\u00f6ren kann per Audiofeedback.<\/p>\n<p>In diesem Backgroundtask, der wie gesagt Laufzeitbegrenzt ist auf wenige Minuten, habe ich dann regelm\u00e4\u00dfig die aktualisierte Positionsinformation ausgewertet und verarbeitet (z.B. ein Protokoll der Positions\u00e4nderungen auf Disk geschrieben). Wurde eine kritische Positions\u00e4nderung festgestellt, so l\u00f6ste dieser Backgroundtask den Alarm aus. Damit der Task jedoch nicht irgendwann einfach endet, habe ich ihn immer nach einem festen Zeitintervall selbst beendet und sofort einen neuen angelegt. Bei dem lief der Zeitcountdown dann wieder von Neuem los. Und genau dieses Vorgehen sollte sich als problematisch erweisen in iOS 6.<\/p>\n<h3>3 Tage Debugging sp\u00e4ter&#8230;<\/h3>\n<p>Ich bin nunmehr zu folgender Erkenntnis gelangt: Man kann nur noch <strong>maximal 64 Backgroundtasks in Folge<\/strong> erzeugen. Danach wird die gesamte App suspended und sogar so drastisch angehalten, dass sie abst\u00fcrzt. In den Screenshots oben kann man sehen, wie ich dem Problem auf die Spur gekommen bin. Nachdem ich festgestellt hatte, dass meine Hintergrundtasks nicht mehr unbegrenzt weiterlaufen, habe ich zun\u00e4chst versucht an die Consolenmeldungen des Device zu gelangen, denn dort geben ich in Log-Statements Debuginfos aus. Problem war jedoch, sobald die App sich aufgehangen hatte enthielt das Log die relevanten Eintr\u00e4ge nicht mehr (bzw. man hat es zu sp\u00e4t mitbekommen und die Logeintr\u00e4ge neuer Ereignisse haben die alten \u00fcberschrieben).<\/p>\n<p>Der Ausweg waren LocalNotifications, denn die sind noch da, selbst wenn das Consolenlog schon l\u00e4ngst weg ist und sie werden vom iOS verwaltet, das ja weiterhin einwandfrei lief. Ich hab einfach zum Debuggen als die BackgroundtaskId&#8217;s gr\u00f6\u00dfer als 62 wurden eine LocalNotification ausgegeben (Screen ganz rechts). Und siehe da, es scheint eine harte Grenze an erlaubten Backgroundtasks zu geben. Auf allen Ger\u00e4ten mit denen ich unter iOS 6 getestet habe gab es diese Grenze, denn alle zeigten die zwei LocalNotifications f\u00fcr id 63 und 64 und danach reagierte die App nicht mehr. Wohlgemerkt nach deutlich \u00fcber 8 Stunden erfolgreicher Laufzeit. Das Consolen-Log sah dann so aus:<\/p>\n<p><code style=\"font-size:10px;\"><br \/>\nAug  6 07:43:35 dev-pod5 Nautical[16452] &lt;Warning&gt;: EXECUTING BACKGROUND TASKS IN INACTIVE\/BACK<br \/>\nAug  6 07:43:35 dev-pod5 Nautical[16452] &lt;Warning&gt;: MONITORING IS: ON<br \/>\nAug  6 07:43:35 dev-pod5 Nautical[16452] &lt;Warning&gt;: VARIO FREQUENCY: 51 %<br \/>\nAug  6 07:43:35 dev-pod5 Nautical[16452] &lt;Warning&gt;: CHECKING BATTERY LEVEL: 0.90<br \/>\nAug  6 07:43:35 dev-pod5 Nautical[16452] &lt;Warning&gt;: TIMEINTERVAL: 53.60 SECONDS<br \/>\nAug  6 07:43:35 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #3: RESPAWNING<br \/>\n<b>Aug  6 07:43:35 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #1: BACKGROUND TASK 64 STILL FINE (TIME REMAINING: inf MINUTES AND 0 SECONDS  )<\/b><br \/>\nAug  6 07:43:35 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #2: APPLICATION STATE = 2<br \/>\nAug  6 07:43:35 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #2: SPAWNING NEW TIMER BECAUSE WE ARE BACKGROUND ...<br \/>\nAug  6 07:43:36 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #4: BG TASK #64 FINE (TIME REMAINING: inf MINUTES AND 0 SECONDS  )<br \/>\nAug  6 07:43:36 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #4: BG TASK #64 ELAPSED: 15 MINUTES  )<br \/>\nAug  6 07:43:36 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #4: BG TASK REACHED END OF LIFE, CLOSING OLD BG TASK #64...<br \/>\nAug  6 07:43:36 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #4: BG TASK CLOSED. SPAWNING NEW BG TASK...<br \/>\nAug  6 07:43:36 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #1: SPAWNING NEW BACKGROUND TASK WHILE BACKGROUND ...<br \/>\nAug  6 07:43:45 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #3: TIMER FIRED<br \/>\nAug  6 07:43:45 dev-pod5 Nautical[16452] &lt;Warning&gt;: EXECUTING BACKGROUND TASKS IN INACTIVE\/BACK<br \/>\nAug  6 07:43:45 dev-pod5 Nautical[16452] &lt;Warning&gt;: MONITORING IS: ON<br \/>\nAug  6 07:43:45 dev-pod5 Nautical[16452] &lt;Warning&gt;: VARIO FREQUENCY: 51 %<br \/>\nAug  6 07:43:45 dev-pod5 Nautical[16452] &lt;Warning&gt;: CHECKING BATTERY LEVEL: 0.90<br \/>\nAug  6 07:43:45 dev-pod5 Nautical[16452] &lt;Warning&gt;: TIMEINTERVAL: 43.55 SECONDS<br \/>\nAug  6 07:43:45 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #3: RESPAWNING<br \/>\nAug  6 07:43:45 dev-pod5 Nautical[16452] &lt;Warning&gt;: ATTENTION #1: SPAWNING NEW BACKGROUND TASK WHILE BACKGROUND ...<br \/>\nAug  6 07:43:49 dev-pod5 kernel[0] &lt;Debug&gt;: 1269100.775 IO80211AWDLPeerManager::setAwdlQuiet flags 5 quiet -&gt; 0<br \/>\nAug  6 07:43:49 dev-pod5 kernel[0] &lt;Debug&gt;: 1269100.776012 wlan.A[63085] AppleBCMWLANProximityInterface::setSYNC_ENABLED():  ON<br \/>\nAug  6 07:43:49 dev-pod5 kernel[0] &lt;Debug&gt;: IO80211AWDLMulticastPeer::queuePacket ff:ff:ff:ff:ff:ff alllocate queue for ac 0<br \/>\nAug  6 07:43:51 dev-pod5 kernel[0] &lt;Debug&gt;: IO80211AWDLPeerManager::doMonitorTimer browsing device idle for txQUni 0(ms) txQMulti 2295(ms) statechange 57290215(ms) serviceupdate 159858095(ms)  pkt 1 -&gt; quiet<br \/>\nAug  6 07:43:51 dev-pod5 kernel[0] &lt;Debug&gt;: 1269103.095 IO80211AWDLPeerManager::setAwdlQuiet flags 3 quiet -&gt; 1<br \/>\nAug  6 07:43:51 dev-pod5 kernel[0] &lt;Debug&gt;: 1269103.095600 wlan.A[63086] AppleBCMWLANProximityInterface::setSYNC_ENABLED():  OFF<br \/>\nAug  6 07:43:57 dev-pod5 backboardd[15262] &lt;Notice&gt;: Posting 'com.apple.iokit.hid.displayStatus' notifyState=1<br \/>\nAug  6 07:43:57 dev-pod5 kernel[0] &lt;Debug&gt;: set_crc_notification_state 0<br \/>\nAug  6 07:43:57 dev-pod5 backboardd[15262] &lt;Notice&gt;: MultitouchHID: detection mode: 255-&gt;0 (deferring until bootloaded)<br \/>\nAug  6 07:43:57 dev-pod5 backboardd[15262] &lt;Notice&gt;: MultitouchHID: device bootloaded<br \/>\nAug  6 07:43:57 dev-pod5 backboardd[15262] &lt;Notice&gt;: MultitouchHID: detection mode: 0-&gt;0<br \/>\nAug  6 07:43:57 dev-pod5 kernel[0] &lt;Debug&gt;: ALS: AppleARMBacklight::handleMessageGated - framebufferState -&gt; 1<br \/>\nAug  6 07:43:57 dev-pod5 kernel[0] &lt;Debug&gt;: ALS: AppleARMBacklight::setBacklightEnableGated 1 (set level to 0x644)<br \/>\nAug  6 07:43:58 dev-pod5 profiled[16600] &lt;Notice&gt;: (Note ) profiled: Service starting...<br \/>\nAug  6 07:43:58 dev-pod5 profiled[16600] &lt;Notice&gt;: (Note ) profiled: Recomputing passcode requirement message<br \/>\nAug  6 07:44:01 dev-pod5 kernel[0] &lt;Debug&gt;: launchd[16603] Builtin profile: syncdefaultsd (sandbox)<br \/>\n<b>Aug  6 07:44:53 dev-pod5 backboardd[15262] &lt;Warning&gt;: com.noxymo.seewetterpro failed to resume in time<\/b><br \/>\n<b>Aug  6 07:44:53 dev-pod5 backboardd[15262] &lt;Warning&gt;: Forcing crash report of Nautical[16452]...<\/b><br \/>\n<b>Aug  6 07:44:54 dev-pod5 backboardd[15262] &lt;Warning&gt;: Finished crash reporting.<\/b><br \/>\nAug  6 07:44:54 dev-pod5 ReportCrash[16604] &lt;Error&gt;: libMobileGestalt copySystemVersionDictionaryValue: Could not lookup ReleaseType from system version dictionary<br \/>\n<b>Aug  6 07:44:54 dev-pod5 com.apple.launchd[1] (UIKitApplication:com.noxymo.seewetterpro[0x5626][16452]) &lt;Notice&gt;: (UIKitApplication:com.noxymo.seewetterpro[0x5626]) Exited: Killed: 9<\/b><br \/>\n<b>Aug  6 07:44:54 dev-pod5 backboardd[15262] &lt;Warning&gt;: Application 'UIKitApplication:com.noxymo.seewetterpro[0x5626]' exited abnormally with signal 9: Killed: 9<\/b><br \/>\n<\/code><\/p>\n<p>Mein finaler Workaround ist nun der, dass ich die maximal erlaubte Laufzeit eines Backgroundtask tats\u00e4chlich auch maximal ausnutze und nicht wie in dem Log ersichtlich nach 15 Minuten pauschal den Task beende und durch einen neuen ersetze. Das hatte ich aus Vorsicht getan, um nicht unerwartet beendet zu werden. Man kann aber jederzeit pr\u00fcfen, wie lange einem noch erlaubt wird diesen BGTask laufen zu lassen. So kann man rechtzeitig vor Beendigung reagieren. Oft sind deutlich mehr als der \u00fcblichen 10 Minuten erlaubt, im Log steht dort sogar &#8222;inf&#8220; f\u00fcr <strong>infinite<\/strong>, das t\u00e4uscht jedoch, denn dieser Wert kann sich urpl\u00f6tzlich verwandeln in einen echten Countdown von verbleibenden 10 Minuten. 10 * 64 Minuten sind aber zumindest schonmal 10 Stunden und 40 Minuten Laufzeit, das ist f\u00fcr den Ankeralarm durchaus ausreichend. Durch Ausdehnen der Intervalle auf das Maximum ist aber deutlich mehr erreichbar und das ist was ich jetzt getan habe.<\/p>\n<p>Insgesamt war das ein recht aufw\u00e4ndig zu debuggendes Problem, und die Zahl 64 als hartes Limit scheint kein Zufall zu sein, man fragt sich dennoch warum das sein muss. Problem zumindest vorerst f\u00fcr iOS 6 gel\u00f6st! Nun bin ich gespannt, was in iOS 7 wohl wieder alles nicht gehen wird&#8230; :-)<\/p>\n<p><strong>\u00dcbrigens:<\/strong><br \/>\nHinweise die mir bei der L\u00f6sung des Problems geholfen haben waren u.a. diese StackOverflow entries &#038; links.<\/p>\n<ul>\n<li><a href=\"http:\/\/stackoverflow.com\/questions\/5323634\/ios-application-executing-tasks-in-background\">http:\/\/stackoverflow.com\/questions\/5323634\/ios-application-executing-tasks-in-background<\/a><\/li>\n<li><a href=\"http:\/\/www.raywenderlich.com\/29948\/backgrounding-for-ios\">http:\/\/www.raywenderlich.com\/29948\/backgrounding-for-ios<\/a><\/li>\n<li><a href=\"http:\/\/developer.apple.com\/library\/ios\/#documentation\/iphone\/conceptual\/iphoneosprogrammingguide\/ManagingYourApplicationsFlow\/ManagingYourApplicationsFlow.html\"> App States and Multitasking (Apple Dokumentation)<\/a><\/li>\n<li><a href=\"https:\/\/developer.apple.com\/support\/technical\/submit\/\">https:\/\/developer.apple.com\/support\/technical\/submit\/<\/a><\/li>\n<li><a href=\"http:\/\/stackoverflow.com\/questions\/9840911\/how-keep-nstimer-when-application-entering-background\">http:\/\/stackoverflow.com\/questions\/9840911\/how-keep-nstimer-when-application-entering-background<\/a><\/li>\n<li><a href=\"http:\/\/stackoverflow.com\/questions\/15092016\/how-to-run-nstimer-in-background-and-sleep-in-ios\">http:\/\/stackoverflow.com\/questions\/15092016\/how-to-run-nstimer-in-background-and-sleep-in-ios<\/a><\/li>\n<li><a href=\"http:\/\/stackoverflow.com\/questions\/8415870\/scheduled-nstimer-when-app-is-in-background\">http:\/\/stackoverflow.com\/questions\/8415870\/scheduled-nstimer-when-app-is-in-background<\/a><\/li>\n<\/ul>\n<p>Ich habe auch einen Prototyp app gebaut, die das Problem isolierte. Eventuell pack&#8216; ich die irgendwann mal wenn ich wieder Zeit habe auf github.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Derzeit bin ich gerade dabei meine App Seewetter Pro (im AppStore, auf Facebook, bei Twitter) zu aktualisieren. Ein ganz besonderes Feature dieser App ist u.a. der Ankeralarm, eine Funktion, die mit Hilfe des GPS eines iPhone\/iPad die Position eines Segelbootes vor Anker \u00fcberwacht. Damit diese \u00dcberwachung \u00fcber mindestens volle 8 Stunden funktioniert, muss die \u00dcberwachung &hellip; <a href=\"https:\/\/www.thetawelle.de\/?p=2597\" class=\"more-link\"><span class=\"screen-reader-text\">\u201eSeewetter Pro: CLLocationManager &#038; Background Location Tracking\u201c <\/span>weiterlesen<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17,18,88,80,59],"tags":[],"class_list":["post-2597","post","type-post","status-publish","format-standard","hentry","category-best-practice","category-bremen","category-coding","category-crazeekywf","category-iphone"],"_links":{"self":[{"href":"https:\/\/www.thetawelle.de\/index.php?rest_route=\/wp\/v2\/posts\/2597","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.thetawelle.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.thetawelle.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.thetawelle.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.thetawelle.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2597"}],"version-history":[{"count":0,"href":"https:\/\/www.thetawelle.de\/index.php?rest_route=\/wp\/v2\/posts\/2597\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.thetawelle.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2597"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.thetawelle.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2597"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.thetawelle.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2597"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}