Un tutorial sulle estensioni app iOS 8

Pochi ci avevano provato prima (date un’occhiata a questo), ma è stata Apple con il primo iPhone a definire come dovrebbe essere uno smartphone e un sistema operativo mobile. Apple ha fatto un incredibile passo avanti nell’hardware e nell’esperienza utente. Tuttavia, spesso dimentichiamo che hanno anche definito gli standard di come dovrebbe funzionare un sistema operativo mobile e come dovrebbero essere realizzate le applicazioni di uno smartphone.

Costruire muri di cemento tra le applicazioni, rendendole completamente isolate e ignare l’una dell’altra, era il metodo migliore per tenerle al sicuro e proteggere i dati. Tutte le attività erano strettamente monitorate da iOS, e c’era solo una manciata di azioni che un’applicazione avrebbe potuto fare al di fuori del suo ambito.

“L’astinenza è la migliore protezione!” – ma dov’è il divertimento in questo?

Ci è voluto un po’ di tempo; troppo tempo se me lo chiedete, ma con iOS 8 Apple ha deciso di divertirsi un po’. iOS 8 ha introdotto un nuovo concetto chiamato App Extensions. Questa nuova funzione non ha abbattuto i muri tra le applicazioni, ma ha aperto alcune porte fornendo un contatto delicato ma tangibile tra alcune app. L’ultimo aggiornamento ha dato agli sviluppatori iOS la possibilità di personalizzare l’ecosistema iOS, e siamo ansiosi di vedere anche questa strada aprirsi.

Cosa sono le App Extensions di iOS 8 e come funzionano?

In termini semplici, iOS 8 App Extensions fornisce un nuovo metodo di interazione con la vostra applicazione, senza avviarla o mostrarla sullo schermo.

Come previsto, Apple ha fatto in modo di stare in cima a tutto, quindi ci sono solo una manciata di nuovi punti di ingresso che la vostra applicazione può fornire:

  • Oggi (chiamato anche widget) – un’estensione visualizzata nella vista Oggi del Centro Notifiche mostra brevi informazioni e permette l’esecuzione di compiti rapidi.
  • Share – un’estensione che permette alla tua app di condividere contenuti con gli utenti sui social network e altri servizi di condivisione.
  • Action – un’estensione che permette di creare pulsanti d’azione personalizzati nel foglio Action per permettere agli utenti di visualizzare o trasformare contenuti provenienti da un’app host.
  • Photo Editing – un’estensione che permette agli utenti di modificare una foto o un video all’interno dell’app Photos.
  • Document Provider – un’estensione usata per permettere ad altre app di accedere ai documenti gestiti dalla tua app.
  • Custom Keyboard – un’estensione che sostituisce la tastiera di sistema.

Le estensioni delle app non sono applicazioni indipendenti. Forniscono funzionalità estese dell’app (a cui si può accedere da altre app, chiamate app host) che sono destinate ad essere efficienti e concentrate su un singolo compito. Hanno un proprio binario, una propria firma del codice e un proprio insieme di elementi, ma vengono consegnati tramite l’App Store come parte del binario dell’app contenente. Un’app (contenente) può avere più di un’estensione. Una volta che l’utente installa un’app che ha delle estensioni, queste saranno disponibili in tutto iOS.

Guardiamo un esempio: Un utente trova una foto usando Safari, colpisce il pulsante di condivisione e sceglie l’estensione della tua applicazione per la condivisione. Safari “parla” con il framework iOS Social, che carica e presenta l’estensione. Il codice dell’estensione viene eseguito, passa i dati utilizzando i canali di comunicazione istanziati dal sistema, e una volta che il compito è finito – Safari elimina la vista dell’estensione. Subito dopo, il sistema termina il processo, e la tua applicazione non è mai stata visualizzata sullo schermo. Eppure ha completato una funzione di condivisione delle immagini.

iOS, utilizzando la comunicazione interprocesso, è responsabile di garantire che l’app host e un’estensione dell’app possano lavorare insieme. Gli sviluppatori usano API di alto livello fornite dal punto di estensione e dal sistema, quindi non devono mai preoccuparsi dei meccanismi di comunicazione sottostanti.

Ciclo di vita

Le estensioni di app hanno un ciclo di vita diverso dalle app iOS. L’app host dà il via al ciclo di vita dell’estensione come risposta all’azione dell’utente. Poi il sistema istanzia l’estensione dell’app e stabilisce un canale di comunicazione tra loro. La vista dell’estensione viene visualizzata nel contesto dell’app ospite utilizzando gli elementi ricevuti nella richiesta dell’app ospite. Una volta che la vista dell’estensione è visualizzata, l’utente può interagire con essa. In risposta all’azione dell’utente, l’estensione completa la richiesta dell’app ospite eseguendo/annullando immediatamente il compito o, se necessario, avviando un processo in background per eseguirlo. Subito dopo, l’app ospite elimina la vista dell’estensione e l’utente ritorna al suo contesto precedente all’interno dell’app ospite. I risultati dell’esecuzione di questo processo potrebbero essere restituiti all’app host una volta che il processo è completato. L’estensione di solito viene terminata poco dopo aver completato la richiesta ricevuta dall’app ospite (o inizia un processo in background per eseguirla).

Il sistema apre l’estensione di un’azione dell’utente dall’app ospite, l’estensione visualizza l’UI, esegue un po’ di lavoro e restituisce i dati all’app ospite (se questo è appropriato al tipo di estensione). L’app contenente non è nemmeno in esecuzione mentre la sua estensione è in esecuzione.

Creazione di un’estensione App – Esempio pratico usando l’estensione Today

Le estensioni Today, chiamate anche widget, si trovano nella vista Today del centro notifiche. Sono un ottimo modo per presentare un contenuto aggiornato per l’utente (come mostrare le condizioni meteorologiche) o eseguire compiti rapidi (come segnare le cose fatte nel widget di un’app per la lista delle cose da fare). Devo sottolineare qui che l’inserimento da tastiera non è supportato.

Creiamo un’estensione Today che mostrerà le informazioni più aggiornate dalla nostra app (codice su GitHub). Per eseguire questo codice, assicurati di aver (ri)configurato l’App Group per il progetto (seleziona il tuo Development Team, tieni presente che il nome dell’App Group deve essere unico e segui le istruzioni di Xcode).

Creazione di un nuovo widget

Come abbiamo detto prima, le app extension non sono app indipendenti. Abbiamo bisogno di un’app contenente sulla quale costruiremo l’estensione dell’app. Una volta che abbiamo la nostra app contenente, scegliamo di aggiungere un nuovo target navigando su File -> Nuovo -> Target in Xcode. Da qui scegliamo il modello per il nostro nuovo target per aggiungere una Today Extension.

Nel passo successivo possiamo scegliere il nostro Product Name. Questo è il nome che apparirà nella vista Today del Notification Center. C’è un’opzione per scegliere il linguaggio tra Swift e Objective-C anche in questo passo. Finendo questi passi, Xcode crea un modello Today, che fornisce file di intestazione e implementazione predefiniti per la classe principale (chiamata TodayViewController) con file Info.plist e un file di interfaccia (un file storyboard o .xib). Il file Info.plist, per impostazione predefinita, assomiglia a questo:

Se non vuoi usare lo storyboard fornito dal modello, rimuovi la chiave NSExtensionMainStoryboard e aggiungi la chiave NSExtensionPrincipalClass con il nome del tuo view controller come valore.

Un widget Today dovrebbe:

  • garantire che il contenuto sia sempre aggiornato
  • rispondere in modo appropriato alle interazioni dell’utente
  • eseguire bene (i widget iOS devono usare saggiamente la memoria o saranno terminati dal sistema)

Condivisione dei dati e un contenitore condiviso

L’estensione dell’app e la sua app contenente hanno entrambe accesso ai dati condivisi nel loro contenitore condiviso definito privatamente – che è un modo di comunicazione indiretta tra l’app contenente e l’estensione.

Non vi piace come Apple rende queste cose così “semplici”? 🙂

La condivisione dei dati attraverso NSUserDefaults è semplice e un caso d’uso comune. Per impostazione predefinita, l’estensione e l’app che la contiene usano insiemi di dati NSUserDefaults separati e non possono accedere l’uno al contenitore dell’altro. Per cambiare questo comportamento, iOS ha introdotto App Groups. Dopo aver abilitato gli app group sull’app contenente e sull’estensione, invece di usare usa initWithSuiteName:@"group.yourAppGroupName"] per accedere allo stesso contenitore condiviso.

Aggiornare il widget

Per assicurare che il contenuto sia sempre aggiornato, l’estensione Today fornisce un’API per gestire lo stato di un widget e per gestire gli aggiornamenti del contenuto. Il sistema occasionalmente cattura delle istantanee della vista del widget, così quando il widget diventa visibile, viene visualizzata l’istantanea più recente finché non viene sostituita con una versione live della vista. Una conformazione al protocollo NCWidgetProviding è importante per aggiornare lo stato di un widget prima che venga catturata un’istantanea. Una volta che il widget riceve la chiamata widgetPerformUpdateWithCompletionHandler:, la vista del widget dovrebbe essere aggiornata con il contenuto più recente e il gestore di completamento dovrebbe essere chiamato con una delle seguenti costanti per descrivere il risultato dell’aggiornamento:

  • NCUpdateResultNewData – Il nuovo contenuto richiede il ridisegno della vista
  • NCUpdateResultNoDate – Il widget non richiede l’aggiornamento
  • NCUpdateResultFailed – Si è verificato un errore durante il processo di aggiornamento

Controllo di quando il widget è visibile

Per controllare quando un widget viene visualizzato, usa il metodo setHasContent:forWidgetWithBundleIdentifier: della classe NCWidgetController. Questo metodo ti permette di specificare lo stato del contenuto del widget. Può essere chiamato dal widget o dalla sua app contenente (se è attiva). Potete passare un NO o un YES flag a questo metodo, definendo che il contenuto del widget è pronto o no. Se il contenuto non è pronto, iOS non visualizzerà il vostro widget quando viene aperta la vista Today.

Apertura dell’app contenente dal widget

Il widget Today è l’unica estensione che può richiedere l’apertura della sua app contenente chiamando il metodo openURL:completionHandler:. Per assicurarsi che l’app contenente si apra in un modo che abbia senso nel contesto dell’attività corrente dell’utente, dovrebbe essere definito uno schema URL personalizzato (che sia il widget che l’app contenente possano usare).

 completionHandler:nil];

Considerazioni sull’interfaccia utente

Quando progettate il vostro widget, approfittate della classe UIVisualEffectView, tenendo presente che le viste che devono essere sfocate/vibrate devono essere aggiunte alla contentView e non alla UIVisualEffectView direttamente. I widget (conformi al protocollo NCWidgetProviding) dovrebbero caricare gli stati memorizzati nella cache in viewWillAppear: in modo da corrispondere allo stato della vista dall’ultimo viewWillDisappear: e poi passare senza problemi ai nuovi dati quando arrivano, il che non è il caso di un normale controller della vista (l’UI è impostata in viewDidLoad e gestisce le animazioni e il caricamento dei dati in viewWillAppear). I widget dovrebbero essere progettati per eseguire un compito, o per aprire l’app contenente con un solo tocco. L’inserimento della tastiera non è disponibile all’interno di un widget. Questo significa che qualsiasi UI che richieda l’inserimento di testo non dovrebbe essere usata.

L’aggiunta di scorrimenti in un widget, sia verticali che orizzontali, non è possibile. O più precisamente, aggiungere una vista a scorrimento è possibile ma lo scorrimento non funzionerà. Il gesto di scorrimento orizzontale in una vista di scorrimento nell’estensione Today sarà intercettato dal centro notifiche che causerà lo scorrimento da Today al centro notifiche. Lo scorrimento verticale di una vista a scorrimento all’interno di un’estensione Today sarà interrotto dallo scorrimento della vista Today.

Note tecniche

Qui indicherò alcune cose importanti da tenere a mente quando si crea un’estensione App.

Caratteristiche comuni a tutte le estensioni

I seguenti elementi sono veri per tutte le estensioni:

  • l’oggetto sharedApplication è off limits: Le estensioni dell’app non possono accedere a un oggetto sharedApplication, o usare uno dei metodi relativi a tale oggetto.

  • Camera e microfono sono off limits: Le estensioni delle app non possono accedere alla fotocamera o al microfono del dispositivo (ma questo non è un caso per tutti gli elementi hardware). Questo è il risultato dell’indisponibilità di alcune API. Per accedere ad alcuni elementi hardware nell’estensione dell’app, dovrai controllare se la sua API è disponibile per le estensioni dell’app o meno (con il controllo della disponibilità delle API descritto sopra).

  • La maggior parte delle attività in background sono off limits: Le estensioni delle app non possono eseguire attività in background di lunga durata, eccetto l’avvio di upload o download, di cui si parla più avanti.

  • AirDrop è off limits: Le estensioni delle app non possono ricevere (ma possono inviare) dati usando AirDrop.

Caricamento/scaricamento in background

L’unico compito che può essere eseguito in background è il caricamento/scaricamento, usando il NSURLSession object.

Dopo che il compito di caricamento/scaricamento è iniziato, l’estensione può completare la richiesta dell’app ospite ed essere terminata senza alcun effetto sul risultato del compito. Se l’estensione non è in esecuzione al momento del completamento del compito in background, il sistema lancia l’app contenente in background e viene chiamato il metodo delegato dell’app application:handleEventsForBackgroundURLSession:completionHandler:.

L’app la cui estensione avvia un compito in background NSURLSession deve avere un contenitore condiviso impostato a cui possono accedere sia l’app contenente che la sua estensione.

Assicurati di creare diverse sessioni in background per l’app contenente e ciascuna delle sue app estensioni (ogni sessione in background dovrebbe avere un identificatore unico). Questo è importante perché solo un processo può usare una sessione in background alla volta.

Action vs. Share

Le differenze tra le estensioni Action e Share non sono completamente chiare dal punto di vista di un programmatore, perché in pratica sono molto simili. Il template di Xcode per l’obiettivo dell’estensione Share usa SLComposeServiceViewController, che fornisce un’interfaccia standard di visualizzazione della composizione che puoi usare per la condivisione sociale, ma non è necessaria. Un’estensione share può anche ereditare direttamente da UIViewController per un design completamente personalizzato, nello stesso modo in cui un’estensione Action può ereditare da SLComposeServiceViewController.

La differenza tra questi due tipi di estensioni è nel modo in cui sono destinati ad essere utilizzati. Con l’estensione Action, puoi costruire un’estensione senza un’interfaccia utente propria (per esempio, un’estensione utilizzata per tradurre il testo selezionato e restituire la traduzione all’applicazione host). L’estensione Share ti permette di condividere commenti, foto, video, audio, link e altro ancora direttamente dall’app host. Il UIActivityViewController guida sia le estensioni Action che Share, dove le estensioni Share sono presentate come icone a colori nella riga superiore e le estensioni action sono presentate come icone monocromatiche nella riga inferiore (Immagine 2.1).

Forbidden APIs

APIs contrassegnate nei file header con la macro NS_EXTENSION_UNAVAILABLE, o macro simili per indisponibilità, non possono essere utilizzate (per esempio: HealthKit e EventKit UI frameworks in iOS 8 non sono disponibili per l’uso in qualsiasi estensione dell’app).

Se state condividendo il codice tra un’app e un’estensione, dovete tenere a mente che anche il riferimento a un’API che non è consentito per l’estensione dell’app porterà al rifiuto della vostra app dall’App Store. Puoi scegliere di affrontare questo ri-fattorizzando le classi condivise in gerarchie, con un genitore comune e diverse sottoclassi per diversi target.Un altro modo è quello di utilizzare il preprocessore di #ifdefcontrolli. Poiché non c’è ancora un condizionale di destinazione incorporato, dovete creare il vostro.

Un altro bel modo per farlo è creare il vostro framework incorporato. Assicuratevi solo che non contenga nessuna API non disponibile per le estensioni. Per configurare l’estensione di un’app per l’utilizzo di un framework incorporato, vai alle impostazioni di compilazione dell’obiettivo e imposta l’impostazione “Require Only App-Extension-Safe API” su Yes. Quando si configura il progetto Xcode, nella fase di compilazione Copy Files, “Frameworks” deve essere scelto come destinazione per il framework incorporato. Se scegliete la destinazione “SharedFrameworks”, la vostra presentazione sarà rifiutata da App Store.

Una nota sulla compatibilità all’indietro

Anche se le estensioni delle app sono disponibili solo da iOS 8, potete rendere la vostra app contenente disponibile per le versioni precedenti di iOS.

Conformità dell’interfaccia umana di Apple

Tenete a mente le linee guida dell’interfaccia umana di iOS di Apple quando progettate un’estensione di app. Dovete assicurarvi che l’estensione della vostra app sia universale, indipendentemente dal dispositivo supportato dalla vostra app contenente. Per garantire che l’estensione dell’app sia universale, utilizzate l’impostazione di costruzione della famiglia di dispositivi mirata in Xcode specificando il valore “iPhone/iPad” (a volte chiamato universale).

Conclusione

Le estensioni delle app hanno sicuramente l’impatto più visibile in iOS 8. Dal momento che il 79% dei dispositivi sta già utilizzando iOS 8 (come misurato dall’App Store il 13 aprile 2015), le estensioni delle app sono caratteristiche incredibili che le app dovrebbero sfruttare. Con la combinazione delle restrizioni dell’API e il modo di condividere i dati tra le estensioni e la loro app contenente, sembra che Apple sia riuscita ad affrontare una delle più grandi lamentele sulla piattaforma senza compromettere il suo modello di sicurezza. Non c’è ancora un modo per le app di terze parti di condividere direttamente i loro dati tra di loro. Anche se questo è un concetto molto nuovo, sembra molto promettente.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.