{"id":11083,"date":"2014-11-28T14:22:14","date_gmt":"2014-11-28T13:22:14","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=11083"},"modified":"2016-02-03T17:13:28","modified_gmt":"2016-02-03T16:13:28","slug":"cllocationmanager-e-ios8","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/cllocationmanager-e-ios8\/","title":{"rendered":"CLLocationManager e iOS8"},"content":{"rendered":"<p><strong>TL;DR<\/strong> Alla fine dell&#8217;articolo trovate un progetto completo con una classe per <strong>risolvere i pi\u00f9 comuni problemi con CLLocationManager e iOS8<\/strong> \ud83d\ude42<\/p>\n<p>Generalmente le nuove versioni del sistema di iOS sono retro-compatibili, potete prendere del codice scritto per iOS(x) compilarlo con iOS(y) con x &lt; y e tutto generalmente funziona senza problemi.<\/p>\n<p>In qualche caso purtroppo non \u00e8 cos\u00ec, e il nuovo\u00a0CLLocationManager ne \u00e8 un esempio. Molti infatti riscontrano un problema con le loro app passando ad Xcode 6 (e quindi all&#8217;sdk di ios8), nell&#8217;utilizzo del\u00a0CLLocationManager che sembra apparentemente \u00a0non funzionare.<\/p>\n<p>Andando a dare un&#8217;occhiata all&#8217;API-diff tra iOS 7.1 e iOS 8.0 (<a href=\"https:\/\/developer.apple.com\/library\/ios\/releasenotes\/General\/iOS80APIDiffs\/frameworks\/CoreLocation.html\" target=\"_blank\">qui<\/a>) si notano due nuovi metodi:<\/p>\n<pre class=\"theme:solarized-dark font:inconsolata font-size:15 line-height:20 lang:objc decode:true\">- [CLLocationManager requestAlwaysAuthorization]\r\n- [CLLocationManager requestWhenInUseAuthorization]<\/pre>\n<p>e due nuovi valori per l&#8217;enum\u00a0CLAuthorizationStatus:<\/p>\n<pre class=\"theme:solarized-dark font:inconsolata font-size:15 line-height:20 lang:objc decode:true \">Added kCLAuthorizationStatusAuthorizedAlways\r\nAdded kCLAuthorizationStatusAuthorizedWhenInUse\r\nDeprecated kCLAuthorizationStatusAuthorized<\/pre>\n<p>iOS 8 infatti prevede due nuovi tipi di autorizzazione: possiamo autorizzare un&#8217;app per ricevere informazioni sulla nostra posizione soltanto quando l&#8217;app \u00e8 attiva (foreground) oppure sempre (anche in backgroud) e il vecchio modo di usare il CLLocationManager richiamando semplicemente il metodo startUpdatingLocation non \u00e8 pi\u00f9 sufficiente.<\/p>\n<p>La prima cosa da fare quindi \u00e8 decidere se la vostra app richiede il primo o il secondo tipo di autorizzazione, se state progettando un&#8217;app per tracciare il percorso fatto in auto probabilmente vorrete avere l&#8217;accesso alla posizione anche in background, viceversa \u00e8 sufficiente ottenere questa informazione in foreground.<\/p>\n<p>Fatta questa scelta il primo step \u00e8 quello di aggiungere nel file .plist dell&#8217;applicazione una stringa di testo per spiegare all&#8217;utente *perch\u00e9* volete avere accesso alla sua posizione. Questa stringa verr\u00e0 mostrata dal sistema operativo al momento della richiesta di accesso e nei settings dell&#8217;applicazione<\/p>\n<p>Le chiavi da aggiungere al file plist sono due, a seconda del tipo di permesso che vi serve<\/p>\n<pre class=\"theme:solarized-dark font:inconsolata font-size:15 line-height:20 lang:objc decode:true \">NSLocationWhenInUseUsageDescription\r\n\/\/Oppure\r\nNSLocationAlwaysUsageDescription<\/pre>\n<p>Questo \u00e8 un esempio di plist:<\/p>\n<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_ios8_1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-11086 size-full\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_ios8_1.png\" alt=\"CLLocationManager_ios8_1\" width=\"1466\" height=\"128\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_ios8_1.png 1466w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_ios8_1-300x26.png 300w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_ios8_1-1024x89.png 1024w\" sizes=\"auto, (max-width: 1466px) 100vw, 1466px\" \/><\/a><\/p>\n<p>E questo \u00e8 il risultato visibile nei settings.<\/p>\n<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_explanation.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-11093 size-full\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_explanation.png\" alt=\"CLLocationManager_explanation\" width=\"756\" height=\"624\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_explanation.png 756w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_explanation-300x247.png 300w\" sizes=\"auto, (max-width: 756px) 100vw, 756px\" \/><\/a><\/p>\n<p><strong>ATTENZIONE:<\/strong> Se dimenticate di aggiungere la relativa chiave, anche se tutto il codice \u00e8 corretto, la richiesta di accesso non verr\u00e0 mai mostrata all&#8217;utente e la vostra app non ricever\u00e0 mai la sua posizione.<\/p>\n<p>Specificata quindi la ragione per cui vogliamo ottenere la posizione dell&#8217;utente, possiamo passare a modificare il codice per ottenerla. Per richiedere la posizione siamo abituati a scrivere pi\u00f9 o meno questo tipo di codice<\/p>\n<pre class=\"theme:solarized-dark font:inconsolata font-size:15 line-height:20 lang:objc decode:true \">    locationManager  = [[CLLocationManager alloc] init];\r\n    locationManager.distanceFilter = kCLDistanceFilterNone;\r\n    locationManager.desiredAccuracy = kCLLocationAccuracyBest;\r\n    locationManager.delegate = self;\r\n    [locationManager startUpdatingLocation];<\/pre>\n<p>Ma su iOS8 questo codice non far\u00e0 scattare la richiesta di permesso verso l&#8217;utente, quindi la nostra app non ricever\u00e0 nessuna notifica.<\/p>\n<p>Bisogna infatti attivamente richiedere il permesso all&#8217;utente tramite uno dei \u00a0due metodi:<\/p>\n<pre class=\"theme:solarized-dark font:inconsolata font-size:15 line-height:20 lang:objc decode:true\">-requestAlwaysAuthorization\r\n-requestWhenInUseAuthorization<\/pre>\n<p>E finalmente l&#8217;utente potr\u00e0 accettare o rifiutare la richiesta (notare la stringa di descrizione all&#8217;interno dell&#8217;alert view)<\/p>\n<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_request.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-11102 size-full\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_request.png\" alt=\"CLLocationManager_request\" width=\"974\" height=\"1602\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_request.png 974w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_request-182x300.png 182w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2014\/11\/CLLocationManager_request-622x1024.png 622w\" sizes=\"auto, (max-width: 974px) 100vw, 974px\" \/><\/a><\/p>\n<p>Questi metodi sono stati definiti nell&#8217;skd di iOS 8, quindi se la vostra app \u00e8 compabile anche con iOS 7 dovrete prima di tutto verificare la versione del sistema operativo, viceversa l&#8217;invocazione di questo metodo causer\u00e0 un bel crash a runtime.<\/p>\n<p>\u00c8 possibile (e consigliato) richiedere sempre il permesso, in ogni caso se l&#8217;utente ha gi\u00e0 approvato o rifiutato non vedr\u00e0 apparire il messaggio.<\/p>\n<p>Un secondo aspetto da tenere in considerazione \u00e8 il cambio di stato dei permessi, l&#8217;utente potrebbe aver rifiutato il permesso ma poi grazie ad un nostro ulteriore \u00a0messaggio (&#8220;caro utente se non attivi la localizzazione come faccio a consigliarti i bar qui vicino??&#8221;) \u00a0potrebbe aver deciso di concederci la grazia, quindi la nostra app deve essere reattiva e aggiornarsi di conseguenza.<\/p>\n<p>Questo pu\u00f2 essere ottenuto implementando il metodo <strong>locationManager:didChangeAuthorizationStatus<\/strong> e richiamando il metodo <strong>startUpdatingLocation<\/strong> all&#8217;occorrenza.<\/p>\n<h2>Una classe da poter riutilizzare<\/h2>\n<p>Questa parte di codice \u00e8 spesso molto simile in tutte le applicazioni, quindi si presta bene ad essere <em>copia&amp;incollata<\/em> oppure, ancora meglio, ad essere inglobata in una classe che possa essere riutilizzata in pi\u00f9 progetti.<\/p>\n<p>Ho infatti scritto un piccolo esempio di wrapper attorno alla classe CLLocationManager per gestire i casi pi\u00f9 comuni, trovate il progetto su github a questo indirizzo:\u00a0<a href=\"https:\/\/github.com\/ignazioc\/DevAppLocationManager\" target=\"_blank\">https:\/\/github.com\/ignazioc\/DevAppLocationManager<\/a><\/p>\n<p>L&#8217;idea di base \u00e8 quella di poter usare questo wrapper al posto del normale CLLocationManager per evitare di dover scrivere codice diverso per iOS 7 \/ iOS 8 e soprattutto per evitare di dimenticare tutti quei casi un po&#8217; particolari (cambio di stato, etc) che\u00a0si possono verificare con la gestione dei permessi.<\/p>\n<p>Ho inoltre preferito semplificare un po&#8217; la gestione, usando i block al posto del delegate e gestendo due soli tipi di errori (al posto dei 17 di CoreLocation), gli unici due errori ai quali sono realmente interessato sono &#8220;L&#8217;utente ha rifiutato il permesso&#8221; \u00a0e &#8220;L&#8217;utente non pu\u00f2 darti il permesso&#8221;.<\/p>\n<p>La classe si chiama\u00a0<strong>CLLocationManagerWrapper<\/strong>, non \u00e8 un singleton ed ha semplicemente due metodi:<\/p>\n<pre class=\"theme:solarized-dark font:inconsolata font-size:15 line-height:20 lang:objc decode:true\">- (id)initWithAuthType:(CLLocationManageraAuthType)authType\r\n        filterDistance:(CLLocationDistance)distanceFilter\r\n             accuracy:(CLLocationAccuracy)accuracy\r\n       completionBlock:(void(^)(CLLocation *newLocation, CLLocationManagerError error))compltionBlock;\r\n\r\n- (void)stopUpdate;<\/pre>\n<p>Il primo va utilizzato per inizializzare\u00a0la classe e passare il block che verr\u00e0 eseguito per ogni nuova posizione rilevata, il secondo per interrompere la localizzazione.<\/p>\n<p>Questo \u00e8 un esempio di utilizzo:<\/p>\n<pre class=\"theme:solarized-dark font:inconsolata font-size:15 line-height:20 lang:objc decode:true\">   locationManagerW = [[CLLocationManagerWrapper alloc] initWithAuthType:CLLocationManageraAuthTypeWhenInUse\r\n                                                           filterDistance:kCLDistanceFilterNone\r\n                                                                 accuracy:kCLLocationAccuracyHundredMeters\r\n                                                          completionBlock:^(CLLocation *newLocation, CLLocationManagerError error) {\r\n                                                              if (newLocation != nil) {\r\n                                                                  NSLog(@\"Ricevuta posizione!\");\r\n                                                              } else {\r\n                                                                  NSLog(@\"Found error: %@\", NSStringFromCLLocationManagerError(error));\r\n                                                              }\r\n                                                              \r\n                                                          }];<\/pre>\n<p>Una piccola chiccha&#8230; per evitare di dimenticare di aggiungere la stringa al plist, in attesa che apple sollevi un warnin o un errore, nell&#8217;inizializzazione della classe trovate questa riga, cos\u00ec l&#8217;app non compila fintanto che tutto non sar\u00e0 corretto \ud83d\ude09<\/p>\n<pre class=\"theme:solarized-dark font:inconsolata font-size:15 line-height:20 lang:objc decode:true \">NSAssert([[[NSBundle mainBundle] infoDictionary] valueForKey:@\"NSLocationWhenInUseUsageDescription\"] != nil, @\"Aggiungi la descrizione nel file plist\");<\/pre>\n<p>Alla prossima and &#8220;happy coding&#8221;!<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>TL;DR Alla fine dell&#8217;articolo trovate un progetto completo con una classe per risolvere i pi\u00f9 comuni problemi&#8230;<\/p>\n","protected":false},"author":53,"featured_media":11164,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[8,481],"tags":[1453,1452,1455,1360,1454],"class_list":["post-11083","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-guide-varie","category-risorse-utili","tag-clauthorizationstatus","tag-cllocationmanager","tag-corelocation-framework","tag-ios-8","tag-startupdatinglocation"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/11083","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/53"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=11083"}],"version-history":[{"count":24,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/11083\/revisions"}],"predecessor-version":[{"id":11717,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/11083\/revisions\/11717"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media\/11164"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=11083"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=11083"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=11083"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}