Notificaciones locales en iOS.

0
7263

Cómo gestionar las notificaciones locales en una aplicación iOS.

Índice de contenidos

1. Introducción
2. Entorno
3. Creando notoficaciones locacles

1. Introducción

Bueno amigos, vamos a crear un proyecto para gestionar las notificaciones locales, muchos desarrolladores las usan para recordar al usuario ciertas tareas o simplemente para recordarle que la app está instalada si hace tiempo que no la abre.

2. Entorno

  • Macbook pro core i7 con 8gb RAM
  • SO: Mavericks
  • IDE: Xcode 5.0.2.

3. Creando notoficaciones locacles

Vamos a crear un proyecto nuevo de tipo single view application y vamos a usar Storyboards

Lo primero que vamos a hacer es dejar el proyecto a nuestro gusto así que vamos a crear primero las clases necesarias para nuestra aplicación, primero crearemos un controlador que hereda UITableViewController en el cuál mostraremos nuestras notificaciones, en mi caso lo he llamado IACNotificationsTableViewController.

Una vez creado, en el Storyboard borramos el viewController que viene por defecto y añadimos un UINavigationController, vemos que por defecto nos arrastra un TableViewController por lo que aprovechamos éste y lo linkamos a nuestra clase creada anteriormente (también ponemos un identifier a la celda que viene por defecto )

Ahora vamos a crear otra clase que herede de NSObject que va ser básicamente nuestro manager para las notificaciones locales, en mi caso lo llamo IACNotificationsManager, nuestro Project Navigator quedaría así;

Comencemos con el código, vamos a importar en nuestro controlador de la tabla el manager para poder hacer uso de él, y a continuación creamos nuestras variables para poblar la tabla y manejar las notificaciones;


@interface IACNotificationsTableViewController (){
    
    IACNotificationsManager *manager;
    NSMutableArray          *notifications;
    NSDateFormatter         *formatter;
}

Bien, ahora vamos a crear unos métodos de clase en nuestro manager para gestionar las notificaciones locales, nuestro .h quedaría de esta forma;


#import <Foundation/Foundation.h>

@interface IACNotificationsManager : NSObject

+(NSArray *)allNotifications;

+(void)createNotification:(UILocalNotification *)notification;

+(void)createNotificationWithTitle:(NSString *)title date:(NSDate *)date;

+(void)createNotificationWithTitle:(NSString *)title date:(NSDate *)date buttonTitle:(NSString *)buttonTitle repeatedDaily:(BOOL)repeatedDaily repeatedWeekly:(BOOL)repeatedWeekly;

+(void)cancelAllNotifications;

+(void)cancelNotification:(UILocalNotification *)notification;

@end

y el .m así;


+(NSArray *)allNotifications{
    
    return [[UIApplication sharedApplication]scheduledLocalNotifications];
}

+(void)cancelAllNotifications{
    
    [[UIApplication sharedApplication]cancelAllLocalNotifications];
}

+(void)cancelNotification:(UILocalNotification *)notification{
    
    NSArray *allNotificarions = [self allNotifications];
    
    for (UILocalNotification *localNotification in allNotificarions) {
        
        if ([localNotification isEqual:notification]) {
            
            [[UIApplication sharedApplication]cancelLocalNotification:localNotification];
        }
    }
}

+(void)createNotification:(UILocalNotification *)notification{

    [[UIApplication sharedApplication]scheduleLocalNotification:notification];
}

+(void)createNotificationWithTitle:(NSString *)title
                              date:(NSDate *)date{
    
    [self createNotificationWithTitle:title
                                 date:date
                          buttonTitle:@"Go to App"
                        repeatedDaily:NO
                       repeatedWeekly:NO];

}

+(void)createNotificationWithTitle:(NSString *)title
                              date:(NSDate *)date
                       buttonTitle:(NSString *)buttonTitle
                     repeatedDaily:(BOOL)repeatedDaily
                    repeatedWeekly:(BOOL)repeatedWeekly{
    
    UILocalNotification *localNotification = [[UILocalNotification alloc]init];

    //sonido que emite la notificación
    localNotification.soundName            = UILocalNotificationDefaultSoundName;
    //fecha de alerta
    localNotification.fireDate             = date;
    //título de la Notificación
    localNotification.alertBody            = title;
    //titulo del boton de acción(en caso de estar la aplicación inactiva abre la misma)
    localNotification.alertAction          = buttonTitle;
    //si el usuario pulsta el botón anterior y esta propiedad esta a YES la aplicación se abre, en caso contratio no hace nada.
    localNotification.hasAction            = YES;

    if (repeatedDaily) {
        localNotification.repeatInterval = NSDayCalendarUnit;
    }
    
    if (repeatedWeekly) {
        localNotification.repeatInterval = NSWeekCalendarUnit;
    }
    
    [self createNotification:localNotification];
}

Bien, repasemos, tenemos que hay un método (allNotifications) que nos devuelve un array con todas las Notificaciones locales que tiene nuestra App pendientes , (cancelAllNotifications) para cancelarlas todas, (cancelNotification:notification) para cancelar una notificación específica, (createNotification:notification) para crear una notificación, y un par de métodos más para ahorrarnos tiempo cuando las queramos crear. Como veis he comentado las propiedades que tienen las UILocalNotifications para que quede más claro qué hace cada una, tener en cuenta que el sonido que emite la notificación puede ser totalmente personalizado, yo he puesto el que viene por defecto en el sistema, también es posible que se emita la notificación diariamente o semanalmente.
Como veis las posibilidades son muchas.

Ahora vamos a crear una vista y su controlador para mostrar de forma modal los parámetros a elegir de nuestra notificación, esta vez creamos una clase que herede de un UIViewController, y los llamamos IACAddNotificationViewController, añadimos la vista al Storyboard (y la linkamos) y los elementos necesarios dentro de la misma vista;

Mediante el Assistant editor unimos los Outlets a nuestro .h con la tecla control pulsada.

Nuestro .h de la clase IACAddNotificationViewController quedaría de esta forma;


#import <UIKit/UIKit.h>

@interface IACAddNotificationViewController : UIViewController

@property (weak, nonatomic) IBOutlet UITextField  *txfNotificationTitle;
@property (weak, nonatomic) IBOutlet UISwitch     *swtDaily;
@property (weak, nonatomic) IBOutlet UISwitch     *swtWeekly;
@property (weak, nonatomic) IBOutlet UIDatePicker *datePickerNotification;

- (IBAction)saveNotification:(id)sender;

@end

Una vez terminado importamos la nueva clase al controlador de tabla.

Vamos a intentar mantener limpio nuestro código siguiendo la regla de que cada método ha de hacer una sola cosa, por lo que no vamos a cargar nuestro ViewDidLoad del controlador de tabla , vamos hacer que quede más o menos de esta forma:


- (void)viewDidLoad{
    
    [super viewDidLoad];
    
    // botones
    [self setUpButtons];
    // modelo
    [self loadModel];
    //common
    [self setUpCommonElements];
}

-(void)setUpButtons{

    self.navigationItem.leftBarButtonItem = self.editButtonItem;
    
    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNotification:)];
    self.navigationItem.rightBarButtonItem = addButton;
    
}
-(void)loadModel{

    notifications = [IACNotificationsManager allNotifications].mutableCopy;
    [self.tableView reloadData];
}

-(void)setUpCommonElements{
    formatter = [NSDateFormatter new];
    formatter.dateFormat = @"dd/MM/yyyy HH:mm:ss";
}

Bien, como veis hemos añadido un botón en la parte izquierda de edición y otro en la parte derecha de nuestra barra de navegación, con su @selector que vamos a usar para abrir nuestra ventana modal;


-(void)insertNotification:(id)sender{
    
    IACAddNotificationViewController *addVC = [self.storyboard instantiateViewControllerWithIdentifier:@"IACAddNotificationViewController"];
    [self presentViewController:addVC animated:YES completion:NULL];
    
}

Como veis le hemos llamado a través del Storyboard, para que esta llamada funcione tenemos que decirle al Storyboard el ID que tiene el controlador;

Ahora en nuestro controlador de tabla vamos a empezar a darle los datos que necesita nuestra tabla en los métodos delegados del Data Source;


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    // Return the number of rows in the section.
    return notifications.count;
}

En el método de pintado de las celdas, vamos ha escribir lo siguiente;


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    UILocalNotification * localNotification = [notifications objectAtIndex:indexPath.row];

    cell.textLabel.text       = localNotification.alertBody;
    cell.detailTextLabel.text = [formatter stringFromDate:localNotification.fireDate];
    
    return cell;
}

Bien si ahora compilamos y ejecutamos vemos que se nos muestra una tabla vacía y si presionamos el botón + se nos abre modalmente la vista para añadir los parámetros de nuestras notificaciones, vamos a hacer que los switch aparezcan por defecto apagados, vamos al Storyboard, seleccionamos los switch y en el inspector de atributos le indicamos OF.

Si vamos al .m de nuestro controlador AddNotification tendríamos que tener un método linkado a un IBAction del botón que hemos añadido antes, en mi caso la acción la he llamado (saveNotification:), en esta acción vamos a guardar la notificación y cerrarla vista modal, vamos a ello;


- (IBAction)saveNotification:(id)sender {
    
    [IACNotificationsManager createNotificationWithTitle:self.txfNotificationTitle.text
                                                    date:self.datePickerNotification.date
                                             buttonTitle:@"ok"
                                           repeatedDaily:self.swtDaily.isOn
                                          repeatedWeekly:self.swtWeekly.isOn];
    [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL];
    
}

Primero hemos importado nuestro manager y después hemos llamado a un método de clase del mismo para crear la notificación en base a los controles de la interfaz, después le decimos que se esconda la pantalla modal.

Pero esta acción no actualiza la tabla, así que vamos hacer un delegado para que nuestra tabla se actualice cuando se cierre la vista modal, en nuestra .h del controlador de la vista modal incluimos el delegado siguiendo la conveniencia de cocoa;


@class IACAddNotificationViewController;

@protocol IACAddNotificationViewControllerDelegate 

-(void)IACAddNotificationViewControllerDidEndEditing:(IACAddNotificationViewController *)IACAddNotificationViewController;

@end

@property (weak, nonatomic) id<IACAddNotificationViewControllerDelegate>delegate;

Y en el método de guardar la notificación incluimos el siguiente mensaje en el bloque completion;


- (IBAction)saveNotification:(id)sender {

    
    [IACNotificationsManager createNotificationWithTitle:self.txfNotificationTitle.text
                                                    date:self.datePickerNotification.date
                                             buttonTitle:@"ok"
                                           repeatedDaily:self.swtDaily.state
                                          repeatedWeekly:self.swtWeekly.state];
    [self.presentingViewController dismissViewControllerAnimated:YES completion:^{
        [self.delegate IACAddNotificationViewControllerDidEndEditing:self];
    }];
    
}

Ahora en la vista de tabla cuando inicializamos el AddViewController le indicamos que nosotros somos su delegado e implementamos su método;


-(void)insertNotification:(id)sender{
    
    IACAddNotificationViewController *addVC = [self.storyboard instantiateViewControllerWithIdentifier:@"IACAddNotificationViewController"];
    addVC.delegate = self;
    [self presentViewController:addVC animated:YES completion:NULL];
    
}

-(void)IACAddNotificationViewControllerDidEndEditing:(IACAddNotificationViewController *)IACAddNotificationViewController{
    [self loadModel];
}


Ahora solo falta incluir en el delegado de la aplicación (AppDelegate.m) el método para recibir las notificaciones locales, en caso de estar la aplicación activa se ejecutara (application:didReceibeLocalNotification:) y en caso de estar inactiva (application:didFinishLaunchWithOptions), en este caso recuperamos la notificación en el diccionario que nos pasa el método;


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    UILocalNotification *localNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    
    if (localNotification) {
    
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:localNotification.alertBody
                                                            message:@""
                                                           delegate:nil
                                                  cancelButtonTitle:NSLocalizedString(@"OK", nil)
                                                  otherButtonTitles:nil];
        [alertView show];
    }
    
    
    return YES;
}

-(void) application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification{
    
         UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:localNotification.alertBody
                                                            message:@""
                                                           delegate:nil
                                                  cancelButtonTitle:NSLocalizedString(@"OK", nil)
                                                  otherButtonTitles:nil];
        [alertView show];
}

Ahora si ejecutamos la aplicación podremos crear notificaciones locales para hacer las pruebas.

Bien vamos a implementar el borrado de cierta notificación desde la vista de la tabla, como visteis insertamos un botón de edición en el lado izquierdo de la barra de navegación, y vamos a implementar los métodos delegados del tableView para la edición de las celdas de esta forma;


- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
    return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        
        UILocalNotification *localNotification = [notifications objectAtIndex:indexPath.row];
        [IACNotificationsManager cancelNotification:localNotification];
        [notifications removeObjectAtIndex:indexPath.row];
        
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }   
}


Ahora ya podemos borrar las notificaciones manualmente.

Podéis descargar el proyecto aquí;

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad