Commit cac11bc1 by Sebastián Katzer

Schedule notifications with support for custom intervals on iOS

parent ca85d768
......@@ -59,7 +59,7 @@
<header-file src="src/ios/APPLocalNotification.h" />
<source-file src="src/ios/APPLocalNotification.m" />
<!-- <header-file src="src/ios/APPLocalNotificationOptions.h" />
<header-file src="src/ios/APPLocalNotificationOptions.h" />
<source-file src="src/ios/APPLocalNotificationOptions.m" />
<header-file src="src/ios/UNUserNotificationCenter+APPLocalNotification.h" />
......@@ -69,7 +69,7 @@
<source-file src="src/ios/UNMutableNotificationContent+APPLocalNotification.m" />
<header-file src="src/ios/UNNotificationRequest+APPLocalNotification.h" />
<source-file src="src/ios/UNNotificationRequest+APPLocalNotification.m" /> -->
<source-file src="src/ios/UNNotificationRequest+APPLocalNotification.m" />
</platform>
......
......@@ -35,8 +35,8 @@
// Request permission to show notifications
- (void) request:(CDVInvokedUrlCommand*)command;
//// Schedule set of notifications
//- (void) schedule:(CDVInvokedUrlCommand*)command;
// Schedule notifications
- (void) schedule:(CDVInvokedUrlCommand*)command;
//// Update set of notifications
//- (void) update:(CDVInvokedUrlCommand*)command;
//// Cancel set of notifications
......
......@@ -21,27 +21,21 @@
* @APPPLANT_LICENSE_HEADER_END@
*/
#import <UserNotifications/UNNotificationSound.h>
#import <UserNotifications/UNNotificationRequest.h>
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@import UserNotifications;
@interface APPLocalNotificationOptions : NSObject
- (id) initWithDict:(NSDictionary*)dict;
@property (readonly, getter=id) NSNumber* id;
@property (readonly, getter=identifier) NSString* identifier;
@property (readonly, getter=title) NSString* title;
@property (readonly, getter=subtitle) NSString* subtitle;
@property (readonly, getter=badge) NSNumber* badge;
@property (readonly, getter=text) NSString* text;
@property (readonly, getter=sound) UNNotificationSound* sound;
@property (readonly, getter=userInfo) NSDictionary* userInfo;
@property (readonly, getter=id) NSNumber* id;
@property (readonly, getter=identifier) NSString* identifier;
@property (readonly, getter=title) NSString* title;
@property (readonly, getter=subtitle) NSString* subtitle;
@property (readonly, getter=badge) NSNumber* badge;
@property (readonly, getter=text) NSString* text;
@property (readonly, getter=sound) UNNotificationSound* sound;
@property (readonly, getter=userInfo) NSDictionary* userInfo;
// If it's a repeating notification
- (id) initWithDict:(NSDictionary*)dict;
- (BOOL) isRepeating;
// how and when to trigger the notification
- (UNNotificationTrigger*) trigger;
@end
......@@ -40,8 +40,11 @@
#pragma mark Initialization
/**
* Initialize the object with the given options when calling on JS side:
* notification.local.add(options)
* Initialize by using the given property values.
*
* @param [ NSDictionary* ] dict A key-value property map.
*
* @return [ APPLocalNotificationOptions ]
*/
- (id) initWithDict:(NSDictionary*)dictionary
{
......@@ -53,10 +56,12 @@
}
#pragma mark -
#pragma mark Attributes
#pragma mark Properties
/**
* The notification's ID.
* The ID for the notification.
*
* @return [ NSNumber* ]
*/
- (NSNumber*) id
{
......@@ -66,7 +71,9 @@
}
/**
* The notification's ID as a string.
* The ID for the notification.
*
* @return [ NSString* ]
*/
- (NSString*) identifier
{
......@@ -74,7 +81,9 @@
}
/**
* The notification's title.
* The title for the notification.
*
* @return [ NSString* ]
*/
- (NSString*) title
{
......@@ -82,7 +91,9 @@
}
/**
* The notification's title.
* The subtitle for the notification.
*
* @return [ NSString* ]
*/
- (NSString*) subtitle
{
......@@ -92,7 +103,9 @@
}
/**
* The notification's message.
* The text for the notification.
*
* @return [ NSString* ]
*/
- (NSString*) text
{
......@@ -100,7 +113,9 @@
}
/**
* The notification's badge number.
* The badge number for the notification.
*
* @return [ NSNumber* ]
*/
- (NSNumber*) badge
{
......@@ -108,14 +123,16 @@
}
/**
* The notification's sound path.
* The sound file for the notification.
*
* @return [ UNNotificationSound* ]
*/
- (UNNotificationSound*) sound
{
NSString* path = [dict objectForKey:@"sound"];
NSString* file;
if ([self stringIsNullOrEmpty:path])
if (!path.length)
return NULL;
if ([path isEqualToString:@"res://platform_default"])
......@@ -132,7 +149,9 @@
}
/**
* The notification's fire date.
* The date when to fire the notification.
*
* @return [ NSDate* ]
*/
- (NSDate*) fireDate
{
......@@ -142,29 +161,41 @@
return [NSDate dateWithTimeIntervalSince1970:timestamp];
}
#pragma mark -
#pragma mark Public
/**
* If it's a repeating notification.
* If the notification shall be repeating.
*
* @return [ BOOL ]
*/
- (BOOL) isRepeating
{
NSString* interval = [dict objectForKey:@"every"];
id every = [dict objectForKey:@"every"];
return ![self stringIsNullOrEmpty:interval];
}
if ([every isKindOfClass:NSString.class])
return ((NSString*) every).length > 0;
#pragma mark -
#pragma mark Methods
return every > 0;
}
/**
* Specify how and when to trigger the notification.
*
* @return [ UNNotificationTrigger* ]
*/
- (UNNotificationTrigger*) trigger
{
return [self isRepeating] ? [self triggerWithDateMatchingComponents] : [self triggerWithTimeInterval];
if ([self isRepeating])
return [self repeatingTrigger];
return [self nonRepeatingTrigger];
}
/**
* The notification's user info dict.
*
* @return [ NSDictionary* ]
*/
- (NSDictionary*) userInfo
{
......@@ -183,18 +214,55 @@
#pragma mark Private
/**
* Returns a trigger based on a custom time interval in seconds.
* Non repeating trigger.
*
* @return [ UNTimeIntervalNotificationTrigger* ]
*/
- (UNTimeIntervalNotificationTrigger*) triggerWithTimeInterval
- (UNTimeIntervalNotificationTrigger*) nonRepeatingTrigger
{
return [UNTimeIntervalNotificationTrigger
triggerWithTimeInterval:[self timeInterval] repeats:NO];
}
/**
* Returns a trigger based on a calendar time.
* Repeating trigger.
*
* @return [ UNNotificationTrigger* ]
*/
- (UNNotificationTrigger*) repeatingTrigger
{
id every = [dict objectForKey:@"every"];
if ([every isKindOfClass:NSString.class])
return [self repeatingTriggerWithDateMatchingComponents];
return [self repeatingTriggerWithTimeInterval];
}
/**
* A trigger based on a calendar time defined by the user.
*
* @return [ UNTimeIntervalNotificationTrigger* ]
*/
- (UNTimeIntervalNotificationTrigger*) repeatingTriggerWithTimeInterval
{
long interval = [[dict objectForKey:@"every"] longValue];
if (interval < 60) {
NSLog(@"time interval must be at least 60 if repeating");
interval = 60;
}
return [UNTimeIntervalNotificationTrigger
triggerWithTimeInterval:interval repeats:YES];
}
/**
* A repeating trigger based on a calendar time defined by the user.
*
* @return [ UNCalendarNotificationTrigger* ]
*/
- (UNCalendarNotificationTrigger*) triggerWithDateMatchingComponents
- (UNCalendarNotificationTrigger*) repeatingTriggerWithDateMatchingComponents
{
NSCalendar* cal = [[NSCalendar alloc]
initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
......@@ -209,7 +277,9 @@
}
/**
* Timeinterval between future fire date and now.
* The time interval between the next fire date and now.
*
* @return [ double ]
*/
- (double) timeInterval
{
......@@ -217,44 +287,49 @@
}
/**
* The notification's repeat interval.
* The repeat interval for the notification.
*
* @return [ NSCalendarUnit ]
*/
- (NSCalendarUnit) repeatInterval
{
NSString* interval = [dict objectForKey:@"every"];
NSCalendarUnit unitFlags = NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond;
NSCalendarUnit units = NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond;
if ([self stringIsNullOrEmpty:interval]) {
return unitFlags;
}
else if ([interval isEqualToString:@"second"]) {
return NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay;
}
else if ([interval isEqualToString:@"minute"]) {
return NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitSecond;
}
else if ([interval isEqualToString:@"hour"]) {
return NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitMinute;
}
else if ([interval isEqualToString:@"day"]) {
if (!interval.length)
return units;
if ([interval isEqualToString:@"second"])
return NSCalendarUnitNanosecond;
if ([interval isEqualToString:@"minute"])
return NSCalendarUnitSecond;
if ([interval isEqualToString:@"hour"])
return NSCalendarUnitMinute;
if ([interval isEqualToString:@"day"])
return NSCalendarUnitHour|NSCalendarUnitMinute;
}
else if ([interval isEqualToString:@"week"]) {
return NSCalendarUnitWeekday|NSCalendarUnitHour|NSCalendarUnitMinute;
}
else if ([interval isEqualToString:@"month"]) {
return NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute;
}
else if ([interval isEqualToString:@"year"]) {
return NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute;
}
return unitFlags;
if ([interval isEqualToString:@"week"])
return NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitWeekday;
if ([interval isEqualToString:@"month"])
return NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitDay;
if ([interval isEqualToString:@"year"])
return NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitDay|NSCalendarUnitMonth;
return units;
}
/**
* Convert relative path to valid sound name attribute.
* Convert an assets path to an valid sound name attribute.
*
* @param [ NSString* ] path A relative assets file path.
*
* @return [ NSString* ]
*/
- (NSString*) soundNameForAsset:(NSString*)path
{
......@@ -263,19 +338,15 @@
}
/**
* Convert resource path to valid sound name attribute.
* Convert a ressource path to an valid sound name attribute.
*
* @param [ NSString* ] path A relative ressource file path.
*
* @return [ NSString* ]
*/
- (NSString*) soundNameForResource:(NSString*)path
{
return [path pathComponents].lastObject;
}
/**
* If the string is empty.
*/
- (BOOL) stringIsNullOrEmpty:(NSString*)str
{
return (!str.length);
}
@end
......@@ -27,13 +27,9 @@
@interface UNMutableNotificationContent (APPLocalNotification)
// Initialize a new local notification
- (id) initWithOptions:(NSDictionary*)dict;
// The options provided by the plug-in
- (APPLocalNotificationOptions*) options;
// Fully configured request to add the notification to the notification center
- (UNNotificationRequest*) request;
// Encode the user info dict to JSON
- (NSString*) encodeToJSON;
@end
......@@ -35,8 +35,11 @@ static char optionsKey;
#pragma mark Init
/**
* Initialize a local notification with the given options when calling on JS side:
* notification.local.add(options)
* Initialize a notification with the given options.
*
* @param [ NSDictionary* ] dict A key-value property map.
*
* @return [ UNMutableNotificationContent ]
*/
- (id) initWithOptions:(NSDictionary*)dict
{
......@@ -49,9 +52,9 @@ static char optionsKey;
}
/**
* Applies the given options when calling on JS side:
* notification.local.add(options)
* Initialize a notification by using the options found under userInfo.
*
* @return [ Void ]
*/
- (void) __init
{
......@@ -65,10 +68,12 @@ static char optionsKey;
}
#pragma mark -
#pragma mark Methods
#pragma mark Public
/**
* The options provided by the plug-in.
* The options used to initialize the notification.
*
* @return [ APPLocalNotificationOptions* ] options
*/
- (APPLocalNotificationOptions*) options
{
......@@ -85,30 +90,15 @@ static char optionsKey;
}
/**
* Get associated option object
*/
- (APPLocalNotificationOptions*) getOptions
{
return objc_getAssociatedObject(self, &optionsKey);
}
/**
* Set associated option object
*/
- (void) setOptions:(APPLocalNotificationOptions*)options
{
objc_setAssociatedObject(self, &optionsKey,
options, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
/**
* The notifcations request ready to add to the notification center including
* all informations about trigger behavior.
*
* @return [ UNNotificationRequest* ]
*/
- (UNNotificationRequest*) request
{
APPLocalNotificationOptions* opts = [self getOptions];
return [UNNotificationRequest requestWithIdentifier:opts.identifier
content:self
trigger:opts.trigger];
......@@ -116,27 +106,55 @@ static char optionsKey;
/**
* Encode the user info dict to JSON.
*
* @return [ NSString* ]
*/
- (NSString*) encodeToJSON
{
NSString* json;
NSData* data;
NSMutableDictionary* obj = [self.userInfo mutableCopy];
[obj removeObjectForKey:@"updatedAt"];
if (obj == NULL || obj.count == 0)
return json;
data = [NSJSONSerialization dataWithJSONObject:obj
options:NSJSONWritingPrettyPrinted
error:NULL];
json = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
return [json stringByReplacingOccurrencesOfString:@"\n"
withString:@""];
}
#pragma mark -
#pragma mark Private
/**
* The options used to initialize the notification.
*
* @return [ APPLocalNotificationOptions* ]
*/
- (APPLocalNotificationOptions*) getOptions
{
return objc_getAssociatedObject(self, &optionsKey);
}
/**
* Set the options used to initialize the notification.
*
* @param [ NSDictionary* ] dict A key-value property map.
*
* @return [ Void ]
*/
- (void) setOptions:(APPLocalNotificationOptions*)options
{
objc_setAssociatedObject(self, &optionsKey,
options, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
......@@ -21,6 +21,8 @@
* @APPPLANT_LICENSE_HEADER_END@
*/
#import "APPLocalNotificationOptions.h"
@import UserNotifications;
@interface UNNotificationRequest (APPLocalNotification)
......
......@@ -21,9 +21,9 @@
* @APPPLANT_LICENSE_HEADER_END@
*/
#import "UNMutableNotificationContent+APPLocalNotification.h"
#import "APPLocalNotificationOptions.h"
#import "UNNotificationRequest+APPLocalNotification.h"
#import "UNMutableNotificationContent+APPLocalNotification.h"
#import <objc/runtime.h>
@import UserNotifications;
......
......@@ -55,66 +55,39 @@ exports.requestPermission = function (callback, scope) {
exec(fn, null, 'LocalNotification', 'request', []);
};
// /**
// * Returns the default settings.
// *
// * @return {Object}
// */
// exports.getDefaults = function () {
// return this._defaults;
// };
// /**
// * Overwrite default settings.
// *
// * @param {Object} defaults
// */
// exports.setDefaults = function (newDefaults) {
// var defaults = this.getDefaults();
// for (var key in defaults) {
// if (newDefaults.hasOwnProperty(key)) {
// defaults[key] = newDefaults[key];
// }
// }
// };
// /**
// * Schedule a new local notification.
// *
// * @param {Object} msgs
// * The notification properties
// * @param {Function} callback
// * A function to be called after the notification has been canceled
// * @param {Object?} scope
// * The scope for the callback function
// * @param {Object?} args
// * skipPermission:true schedules the notifications immediatly without
// * registering or checking for permission
// */
// exports.schedule = function (msgs, callback, scope, args) {
// var fn = function(granted) {
/**
* Schedule notifications.
*
* @param [ Array ] notifications The notifications to schedule.
* @param [ Function ] callback The function to be exec as the callback.
* @param [ Object ] scope The callback function's scope.
* @param [ Object ] args Optional flags how to schedule.
*
* @return [ Void ]
*/
exports.schedule = function (msgs, callback, scope, args) {
var fn = function (granted) {
// if (!granted) return;
if (!granted) return;
// var notifications = Array.isArray(msgs) ? msgs : [msgs];
var notifications = Array.isArray(msgs) ? msgs : [msgs];
// for (var i = 0; i < notifications.length; i++) {
// var notification = notifications[i];
for (var i = 0; i < notifications.length; i++) {
var notification = notifications[i];
// this.mergeWithDefaults(notification);
// this.convertProperties(notification);
// }
this.mergeWithDefaults(notification);
this.convertProperties(notification);
}
// this.exec('schedule', notifications, callback, scope);
// };
this.exec('schedule', notifications, callback, scope);
};
// if (args && args.skipPermission) {
// fn.call(this, true);
// } else {
// this.registerPermission(fn, this);
// }
// };
if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.requestPermission(fn, this);
}
};
// /**
// * Update existing notifications specified by IDs in options.
......@@ -435,55 +408,75 @@ exports.requestPermission = function (callback, scope) {
// this.exec('getTriggered', null, callback, scope);
// };
/**
* The (platform specific) default settings.
*
* @return [ Object ]
*/
exports.getDefaults = function () {
return this._defaults;
};
/**
* Overwrite default settings.
*
* @param [ Object ] newDefaults New default values.
*
* @return [ Void ]
*/
exports.setDefaults = function (newDefaults) {
var defaults = this.getDefaults();
// /**********
// * EVENTS *
// **********/
for (var key in defaults) {
if (newDefaults.hasOwnProperty(key)) {
defaults[key] = newDefaults[key];
}
}
};
// /**
// * Register callback for given event.
// *
// * @param {String} event
// * The event's name
// * @param {Function} callback
// * The function to be exec as callback
// * @param {Object?} scope
// * The callback function's scope
// */
// exports.on = function (event, callback, scope) {
/**
* Register callback for given event.
*
* @param [ String ] event The name of the event.
* @param [ Function ] callback The function to be exec as callback.
* @param [ Object ] scope The callback function's scope.
*
* @return [ Void ]
*/
exports.on = function (event, callback, scope) {
// if (typeof callback !== "function")
// return;
if (typeof callback !== "function")
return;
// if (!this._listener[event]) {
// this._listener[event] = [];
// }
if (!this._listener[event]) {
this._listener[event] = [];
}
// var item = [callback, scope || window];
var item = [callback, scope || window];
// this._listener[event].push(item);
// };
this._listener[event].push(item);
};
// /**
// * Unregister callback for given event.
// *
// * @param {String} event
// * The event's name
// * @param {Function} callback
// * The function to be exec as callback
// */
// exports.un = function (event, callback) {
// var listener = this._listener[event];
/**
* Unregister callback for given event.
*
* @param [ String ] event The name of the event.
* @param [ Function ] callback The function to be exec as callback.
*
* @return [ Void ]
*/
exports.un = function (event, callback) {
var listener = this._listener[event];
// if (!listener)
// return;
if (!listener)
return;
// for (var i = 0; i < listener.length; i++) {
// var fn = listener[i][0];
for (var i = 0; i < listener.length; i++) {
var fn = listener[i][0];
// if (fn == callback) {
// listener.splice(i, 1);
// break;
// }
// }
// };
if (fn == callback) {
listener.splice(i, 1);
break;
}
}
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment