Category: iOS

LightWeight View Controller

Ho trovato un interessante articolo che volevo condividere con voi, in cui un gruppo di gente molto competente suggerisce una pratica che consente di scrivere un ViewController lightweight nei progetti iOS. I ViewController sono essenziali in ogni progetto iOS e consentono di gestire le interazioni tra le view e i data model in un’applicazione, come descritto dal pattern MVC. Spesso queste classi contengono molto codice, alle volte più di quanto, in effetti, è necessario. L’idea di base suggerita è quella di separare il codice, lì dove possibile, di gestione dei protocolli ai quali il view controller aderisce. L’esempio seguente è basato su un ViewController derivato dalla classe UITableViewController. Ho cercato di personalizzare l’esempio descritto rendendolo ancora più semplice in modo tale da aumentarne l’efficacia.

#pragma mark UITableViewDataSource
 
- (id)itemAtIndexPath:(NSIndexPath *)indexPath {
    return self.items[(NSUInteger) indexPath.row];
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.items.count;
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier
                                                            forIndexPath:indexPath];
    id item = [self itemAtIndexPath:indexPath];
    // Your configuration cell code here
    return cell;
}

Il codice precedente fa riferimento ai metodi del protocollo implementati dal nostro UIViewController per gestire la visualizzazione delle celle di una tabella. Tipicamente questi metodi sono riportati all’interno del ViewController, anche se in realtà hanno a che fare con il datasource (array->NSArray) che contiene i dati della tabella. Proviamo a spostare il codice all’interno della classe che si occupa della gestione degli array:

@interface ArrayDataSource : NSObject 
// your declaration here
@end
 
@implementation ArrayDataSource
 
#pragma mark UITableViewDataSource
 
- (id)itemAtIndexPath:(NSIndexPath *)indexPath {
    return self.items[(NSUInteger) indexPath.row];
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.items.count;
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier
                                                            forIndexPath:indexPath];
    id item = [self itemAtIndexPath:indexPath];
    // Your configuration cell code here
    return cell;
}
@end

Bene, ora il nostro controller comincia a diventare più snello. Creiamo quindi un’istanza del nostro nuovo oggetto, a cui passare l’insieme dei dati che popoleranno la tabella, assieme al codice che si occuperà di configurare il contenuto di ciascuna cella.

- (void)setupTableView {
    TableViewCellConfigureBlock configureCell = ^(TableCell *cell, DataSourceItem *content) {
        self.big.text = content.bigDescription;
        self.small.text = content.smallDescription;
    };
 
    NSMutableArray *ds = [[StoreManager sharedManager] readFromSource];
    self.dataSource = [[ArrayDataSource alloc] initWithItems:ds
                                              cellIdentifier:CellIdentifier
                                          configureCellBlock:configureCell];
 
    self.tableView.dataSource = self.dataSource;
    [self.tableView registerClass:[TableCell class] forCellReuseIdentifier:CellIdentifier];
}

Osserviamo che è possibile riutilizzare il metodo setupTableView, ogni qual volta si desidera visualizzare un array all’interno di una tabella senza preoccuparsi di dover mappare un elemento dell’array (atIndex(i)) verso un elemento della tabella (atIndexPath(i)). Inoltre questo tipo di approccio è riutilizzabile nel contesto della UICollectionView, componente simile alle UITableView.
Nell’esempio di sopra la tabella viene popolata dalla classe StoreManager la quale è volutamente esterna al ViewController in quanto designata al recupero di tali informazioni. Questa ulteriore separazione aumenta la leggerezza del nostro ViewController e inoltre ci consente di poter riutilizzare il codice qualora ad esempio, la nostra sorgente dati non sia un array ma una sorgente core data.

Un altro accorgimento da prendere è quello di separare il codice relativo alla gestione delle view all’interno di classi appositamente designate, piuttosto che implementarlo all’interno del ViewController. Nel nostro esempio, il layout di ogni singola cella è demandato alla classe TableCell la quale al suo interno realizza l’inizializzazione del contenuto di ciascuna cella e la corretta visualizzazione. La Category sulla classe TableCell ci consente di disaccoppiare la logica di definizione del contenuto della cella rispetto a quella di definizione del layout della cella stessa. Riscrivendo la sola Category possiamo cambiare la logica di visualizzazione senza impattare su tutto il resto.

Gli esempi di sopra costituiscono piccoli accorgimenti che possono aiutare lo sviluppo del nostro codice rendendo più snelle quelle classi che maggiormente vengono utilizzate. In questo caso specifico abbiamo parlato di UITableViewControllor che tipicamente racchiude molto del codice dei protocolli cui aderisce. Il suggerimento finale è di non forzare l’utilizzo di questi pattern, ma di utilizzarli lì dove si rende necessario e dove viene naturale. Potete trovare l’articolo originale su www.objc.io, in cui viene fatta un’analisi anche su altri argomenti. Consigliato.
Special thanks to: iGriever

Potete trovare il progetto di esempio di quanto detto finora su GitHub.

iOS Passbook: an introduction

Passbook è una nuova applicazione presente nell'ultimo rilascio iOS 6 della Apple. In sostanza è una sorta di portafoglio virtuale, che consente di memorizzare coupon, biglietti per il cinema, carte di imbarco, biglietti e altro in modo molto semplice e naturale. I passbook sono interattivi, consentono di visualizzare informazioni sugli oggetti memorizzati, ad esempio il credito residuo, oppure le ultime offerte connesse ad un coupon, o altro. I passbook sono time and location enabled, ovvero è possibile attivarli in prossimità di un punto geografico preciso oppure in un momento ben preciso. Sono memorizzati in locale al device e contengono informazioni essenziali attraverso le quali un'utente può fruire di un oggetto ben preciso. I passbook possono essere aggiornati dinamicamente attraverso il servizio di Push Notification messo a disposizione da Apple, comunicando così variazioni o aggiornamenti in tempo reale. Possono essere distribuiti via email, via web o tramite applicazione.
Per veder decollare del tempo, visto che anche le aziende dovranno supportarlo. L'ultima ad averlo integrato nella propria applicazione è l'app di booking.com….non c'è bisogno che scriva a chi fa riferimento.

Ad ogni modo, quello che a noi interessa è la parte tecnica, vediamo un pò come si fa a creare un Passbook e cosa serve per renderlo interattivo. In questo articolo vedremo una parte di introduzione per farci un'idea. Per creare un'oggetto Passbook è necessario avere:

  • un'identificativo
  • un pass-style
  • informazioni dell'utente
  • immagini, logo e altro
  • location, data e ora

Pass identifier

E' necessario poter distinguere tra un pass e un'altro per questo sono stati introdotti i pass identifier. In generale un'identifier, da diritto all'applicazione di accedere al Passbook tramite la libreria PassKit di iOS.
Esistono diversi tipi di identifier:

Team Identifier

E' un'identificatore stringa di 10 caratteri rilasciato  sul developer portal. Viene utilizzato per recuperare dalla libreria del passbook quello identificato, nel nostro caso, dal teamIdentifier.

 "teamIdentifier" : "DK9N2M2GK6"

Pass Type Identifier

Utilizzato per definire/identificare una classe o una categoria di passbook. Viene rilasciato sul developer portal.

 "passTypeIdentifier" : "it.devme.coupon"

Serial Number Identifier

Utilizzato in congiunzione al pass type identifeer per identificare univocamente e globalmente ciascun passbook. Viene scelto dallo sviluppatore e deve avere una sintassi ben precisa. Vedremo in seguito un caso d'uso che ne chiarisce il funzionamento.  

 "serialNumber" : "B5BD0271-B90B-400D-8344-36A789714CC8"

Come caso d'uso si pensi ad una compagnia aerea che intende distribuire le proprie carte d'imbarco attraverso l'utilizzo dei PassBook. Per ciascuna carta d'imbarco, viene rilasciato un passbook come segue:

 "passTypeIdentifier" : "it.airdevme.boardingpass"

Ciascun passbook deve poter essere univocamente identificato, dal momento che è assegnato a ciascun viaggiatore. Per poter effettuare tale identificazione, si associa al pass type identifier, il serial number, che per semplicità possiamo considerare come:

 "serialNumber" : "001"
 "serialNumber" : "002" 
 "serialNumber" : "003"

Pass Style

Identificato il modo in cui verrà presentato un passbook dal punto di vista grafico. Esistono diverse tipologie di passbook:

  • Coupons
  • Store cards
  • Boarding passes
  • Event tickets
  • Generic

Ogni diverso stile identifica un diverso modello grafico che rappresenterà il passbook sul dispositivo. Ciascun passbook consentirà di inserire le seguenti informazioni per modellare la sua presentazione:

"coupon" : {
     headerFields : ...
     primaryFields : ...
     secondaryFields : ...
     auxilliaryFields : ...
     backFields : ...
     }

Fields

I Fields o informazioni dell'utente, arrichiscono il passbook presentandolo all'utente secondo le informazioni essenziali che lo stesso dovrà presentare. 

"boardingPass": {
    "headerFields" : [ {
		"key": "gate",
		"label": "GATE",
		"value": "82",
		"changeMessage": "Gate changed to %@" 
		}
	],
    "auxiliaryFields" : [ {
		"label" : "DEPARTS",
	}
	],
	"value" : "2012-05-21T12:20:00-07:00",
	"dateStyle" : "PKDateStyleNone",
	"timeStyle" : "PKDateStyleShort"
	"key" : "departs",
   "transitType" : "PKTransitTypeAir"
}

I dati definiti sopra consentono di modellare la carta d'imbarco del nostro esempio. E' facile pensare che le informazioni minime per una carta di imbarco sono il numero del GATE, la data e ora di partenza, eventuli informazioni che devono essere comunicate al viaggiatore.

Color (Immagini logo e altro)

Consente di attribuire un colore al passbook in tutte le sezioni in cui è consentito. Con riferimento al colore di sfondo, colore di primo piano, un colore per l'header, attribuire un logo, un'immagine nell'header, del testo, etc. In generale ci si riferisce a tutte le informazioni di presentazione del passbook.

Location, Data e Ora

E' possibile attribuire ad un Passbook un numero finito di location, ovvero punti di localizzazione, in cui il passbook sarà attivato notificandolo all'utente. Si pensi ad esempio se l'utente si trova in prossimità di un negozio o altro punto di interesse.  E' possibile definire fino a 10 punti di localizzazione nel pass.json. Allo stesso modo è possibile definire una data e un'ora in cui il passbook sarà attivato, proponendo solo in quel momento il passbook all'utente. E' possibile modificare le informazioni sulla localizzazione e sulla data e l'ora anche dopo avere inizializzate. 

"locations" : [
     {
       "longitude" : -122.3748889,
       "latitude" : 37.6189722
     },
     {
       "longitude" : -122.03118,
       "latitude" : 37.33182
     }
   ],
 "relevantDate" : "2011-12-08T13:00-08:00"

L'esempio di sopra inizializza 2 informazioni di localizzazione ed una per data e ora. Nei prossimi articoli vedremo quali servizi è necessario implementare lato server per interagire con i Passbook e quali lato client per gestirne il loro ciclo di vita.

Stay tuned!

WordPress Themes