SeguiPrezzi per Amazon.it su iPad

SeguiPrezzi  banner - shield

Da qualche giorno è disponibile su App Store la mia ultima applicazione per iPad: si chiama SeguiPrezzi e consente di seguire i prezzi dei prodotti di Amazon.it che vorremmo acquistare, in modo da non perdere mai un ribasso improvviso.

La cosa interessante è che possiamo risparmiare tantissimo scoprendo, ad esempio, che un prodotto aveva tempo fa, un prezzo molto più basso di quello attuale. Se vogliamo essere avvisati quando un prodotto scende di prezzo, basterà seguirlo, ovvero toccare e accendere la stellina nella sua scheda prodotto. Una notifica push ci avviserà del cambiamento di prezzo e non ci perderemo più nessuno degli sconti che Amazon riserva al mercato italiano.

L’app attualmente costa € 1,59, ma se avete un blog e volete fare una recensione potete chiedermi un codice promo (ovvero codice redeem) per installarla gratis: è sufficiente contattarmi. Ovviamente il fatto che non la pagate non vi obbliga a scrivere una recensione positiva: i complimenti sono graditi quanto le critiche costruttive.

Una curiosità. l’icona dell’app è un insieme di riferimenti allo scopo per cui SeguiPrezzi è stato sviluppato: rappresenta uno scudo per difendersi dalla crisi economica, un segugio che trova i prezzi più bassi, e un fulmine stilizzato come un grafico di prezzi che scendono, ad evocare lo storico prezzi che l’app mette a disposizione.

Controllare che un oggetto sia in un NSArray

Un metodo molto comodo, di quelli accorcia-codice e time-saving, quando si deve controllare che un certo oggetto sia presente in un array è [NSArray containsObject:].

Tipico esempio è quando, dato un array di NSString, dobbiamo controllare che quella fornita dall’utente faccia parte o meno dell’elenco. Ecco come usare quindi il metodo:

1
2
3
4
5
NSString *stringa = @"ciao";
 
NSArray *array = [NSArray arrayWithObjects:@"salve", @"arrivederci", @"ciao", @"addio", nil];
 
BOOL found = [array containsObject:stringa];
NSString *stringa = @"ciao";

NSArray *array = [NSArray arrayWithObjects:@"salve", @"arrivederci", @"ciao", @"addio", nil];

BOOL found = [array containsObject:stringa];

Si risparmia quando molto codice per non dover confrontare a mano ogni elemento dell’array tramite un ciclo for e un test isEqual:.

Tip: aggiornare un feed usando la classe NSXMLParser

Chi ha usato la splendida classe NSXMLParser per ottenere informazioni da un feed RSS o da un XML qualunque potrebbe aver notato che, qualora servisse aggiornare le informazioni facendo chiamate multiple al server che le ospita, il risultato spesso non è aggiornato, specialmente dopo un certo numero di chiamate.

Qualunque sia il motivo, questo accade solo quando l’URL usato è sempre lo stesso. Cosa fare quindi per ottenere informazioni fresche quando l’URL non deve cambiare? È sufficiente aggiungere un parametro GET inesistente, avendo cura di cambiare il valore attribuito in maniera casuale.

Ecco un possibile esempio:

1
NSString *urlString = [NSString stringWithFormat:@"%@?reload=%i", url, arc4random() % 100000];
NSString *urlString = [NSString stringWithFormat:@"%@?reload=%i", url, arc4random() % 100000];

Tip: aggiungere un intervallo di tempo ad un NSDate

Come si può aggiungere un giorno, un mese, un anno ad una data su in Objective-C?

Considerando la data di partenza, che può anche essere la data di oggi, ovvero [NSDate date], immaginiamo di voler aggiungere un mese. Core Foundation ci mette a disposizione la classe NSDateComponents la quale contiene la proprietà month (e non solo questa). Il metodo dateByAddingComponents:components:toDate:options permette di ottenere la data finale in maniera molto semplice.

Ecco il codice completo, usando la data di oggi come data di partenza:

1
2
3
NSDateComponents *components = [[NSDateComponents alloc] init];
components.month = 1;
NSDate *oneMonthFromNow = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:[NSDate date] options:0];
NSDateComponents *components = [[NSDateComponents alloc] init];
components.month = 1;
NSDate *oneMonthFromNow = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:[NSDate date] options:0];

Creare un popover: una roadmap

Uno degli elementi d’interfaccia più utilizzati su iPad è il popover, elemento che sostituisce i menu a tendina e che discende spesso da un UIBarButtonItem di una toolbar (anche se è possibile collegarlo a qualsiasi elemento dell’interfaccia tramite il metodo presentPopoverFromRect:).

Per utilizzare al meglio un popover è necessario progettarlo con le seguenti caratteristiche

  1. UIPopoverController dev’essere una proprietà sintetizzata nel viewController, di modo da dismetterlo in qualunque momento senza problemi.
  2. Al popover dev’essere associato un protocollo (vedi qui come fare), per fare in modo che il viewController che ha creato il popover (viewController che sarà il suo delegato) riceva informazioni quando viene toccato un pulsante o simile del popover.
  3. Se il popover contiene una tabella, ma anche in altre situazioni, il viewController relativo potrà autodefinire la propria dimensione, di modo che il popover si regoli di conseguenza. Per questo si usa la proprietà del viewController self.contentSizeForViewInPopover, che è un CGSize.
  4. Al tocco del UIBarButtonItem si associa alla proprietà di tipo UIPopoverController un popover che contiene la vista da mostrare:
    1
    2
    
    myPopoverViewController = [[PopoverViewController alloc] initWithNibName:@"PopoverViewController" bundle:nil];
    popoverController = [[UIPopoverController alloc] initWithContentViewController:PopoverViewController];
    myPopoverViewController = [[PopoverViewController alloc] initWithNibName:@"PopoverViewController" bundle:nil];
    popoverController = [[UIPopoverController alloc] initWithContentViewController:PopoverViewController];
  5. Al momento della presentazione del popover si può anche scegliere la direzione della freccia verso cui deve aprirsi:
    1
    
    [popoverController presentPopoverFromBarButtonItem:popoverButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    [popoverController presentPopoverFromBarButtonItem:popoverButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
  6.  Per dismettere il popover si può chiamare semplicemente il seguente metodo
    [popoverController dismissPopoverAnimated:YES];

    ma sfruttando il protocollo creato appositamente, si può chiamare il metodo di protocollo sul delegato e dismettere il popover nell’implementazione di quel metodo. Passando degli argomenti si può decidere di compiere un azione a seconda dell’argomento.

 

Determinare se l’app è stata aperta con una notifica push

Se un app non attiva ha ricevuto una notifica, l’utente potrebbe scegliere di aprire l’app stessa dalla notifica, anziché dall’icona sulla schermata Home. Per fare questo è sufficiente usare questo codice nel metodo application:didFinishLaunchingWithOptions dell’AppDelegate.m:

1
2
3
4
if ([launchOption objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey] != nil){
   //app è stata aperta da una notifica push
 
}
if ([launchOption objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey] != nil){
   //app è stata aperta da una notifica push

}

Questo può servire, ad esempio, ad aprire una particolare vista dell’app solo quando si è ricevuta una notifica, come ad esempio la pagina delle news se la notifica avvisava della pubblicazione di una nuova notizia.

Stesso discorso vale per le notifiche locali.

Mostrare una notifica push ad applicazione aperta

Le notifiche push appaiono sullo schermo quando l’applicazione che le riceve non è attiva, ma non accade nulla in automatico quando l’applicazione è attiva. Questo perché si possa decidere autonomamente se mostrare o meno il testo della notifica, come mostrarlo e come tener conto del valore del badge.

Se volessimo ad esempio visualizzare il testo della notifica in un alert in base alla notifica ricevuta ad app aperta, allora basterà usare il metodo application:didReceiveRemoteNotification: nell’AppDelegate.m e usando il dizionario UserInfo per accedere alle informazioni della notifica. Ad esempio testo dell’allerta è disponibile con queste chiavi:

1
NSString *message = [userInfo objectForKey:@"aps"] valueForKey:@"alert"];
NSString *message = [userInfo objectForKey:@"aps"] valueForKey:@"alert"];

Il nuovo valore per il badge è disponibile alla chiave @”badge”.

Dal momento che una notifica può anche non avere testo, è bene controllare che il testo ci sia prima di utilizzarlo se questa eventualità può verificarsi in base al progetto dell’app. Ecco un modo per fare questa verifica:

1
2
3
4
5
id notification = [[userInfo objectForKey:@"aps"] valueForKey:@"alert"];
if ([notification isKindOfClass:[NSString class]]){
   //la notifica ha testo
 
}
id notification = [[userInfo objectForKey:@"aps"] valueForKey:@"alert"];
if ([notification isKindOfClass:[NSString class]]){
   //la notifica ha testo

}

Tip: quanti giorni tra due date?

Calcolare il numero di giorni che intercorrono fra una data e un’altra può servire a vari scopi, non ultimo quello di costruire un grafico basato sul calendario.

Supponendo di avere a disposizione già le date in un oggetto NSDate, questo calcolo è molto semplice se si prende, ad esempio, come punto di riferimento l’istante attuale e si calcolano il numero di secondi fra le due date e adesso tramite la funzione timeIntervalSinceNow.

Il numero di giorni sarà quindi:

1
2
3
4
NSTimeInterval startDiff = [startDate timeIntervalSinceNow];
NSTimeInterval endDiff = [endDate timeIntervalSinceNow];
NSTimeInterval dateDiff = [startDiff - endDiff];
float days = dateDiff / (3600. * 24.);
NSTimeInterval startDiff = [startDate timeIntervalSinceNow];
NSTimeInterval endDiff = [endDate timeIntervalSinceNow];
NSTimeInterval dateDiff = [startDiff - endDiff];
float days = dateDiff / (3600. * 24.);

Tip: determinare se il display è Retina

Come sappiamo ben 4 dispositivi Apple hanno attualmente un display Retina: iPhone 4 e 4S, il nuovo iPad e iPod touch di quarta generazione. Per molte ragioni potremmo aver bisogno di sapere se il dispositivo in uso dall’utente ha questo tipo di display (ad esempio per effettuare il download di una immagine alla giusta risoluzione).

Per determinare se il display del dispositivo in uso è Retina è sufficiente usare il seguente codice:

1
2
3
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2){
    //Retina display
}
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2){
    //Retina display
}

scale è infatti uguale a 2 proprio perché il display Retina ha una risoluzione esattamente doppia (per ogni lato, e quindi il quadruplo dei pixel) rispetto ad un display standard (che si tratti di iPhone, iPad o iPod touch). È ovviamente necessario controllare (nel blocco if) se il sistema operativo è abbastanza recente da poter rispondere al metodo scale, e questo si fa tramite il metodo respondsToSelector.

Da array a stringa, e viceversa

Inutile spiegare quanto utile può essere il creare un NSArray a partire da una stringa di elementi separati da un carattere speciale (come ad esempio per creare un file CSV) e ottenere il risultato inverso, partendo da un array per ottenere una serie di NSString.

Core Foundation di Apple possiede una coppia di metodi speculari che fanno proprio questo lavoro.

Si tratta di componentsSeparatedByString: che crea un NSArray separando una stringa in componenti tramite la stringa specificata.

La funzione inversa è componentsJoinedByString: che prende un NSArray e lo trasforma in una stringa in cui gli elementi dell’array sono concatenati dalla stringa specificata.