Local Notification和Push Notification-- 本地通知和Push通知指南
转载:http://supershll.blog.163.com/blog/static/37070436201262322029602/
本地和Push通知指南
本地通知和push通知是不在前台运行的应用程序让用户知道有关于他们的信息的方法。信息可以是一个消息,一个即将发生的日历事件,或远程服务器上的新数据。当操作系统展示它们时,本地和push通知的表现形式是一样的。他们可以显示一个消息或可以在应用程序图标(icon)上显示标记(badge).它们还可以同时播放一段声音。
push通知在iOS3.0以后和Mac OS X 10.7以后引入。本地通知在iOS4.0以后引用,且在Mac OS X上不可用。
本地通知是UILocalNotification的实例,其有3个属性:
1)Scheduled time:你必须指定操作系统要传送通知的日期;这通过 fire data表示。你可以指定日期通过一个特定的时区以便系统可以调整时间--当用户在旅行时。你还可以请求操作系统来重新schedule通知通过一些常规的间隔(weekly,monthly,等等)
2)Notification Type:这个范畴包括alert message,action button的title,应用图标标记的数量,和要播放的声音。
3)Custom Data:一个Dictionary。
(一)安排Scheduling,注册Registering,和处理Handling 通知Notifications
1,准备自定义的alert sounds:
对于iOS的远程通知,你可以指定一个自定义的声音。声音文件必须在main bundle中。
因为自定义的alert sounds是通过iOS 系统声音设备播放的,因此它们必须是下列格式的一种:
1)Linear PCM
2)MA4(IMA/ADPCM)
3)uLaw
4)aLaw
你可以打包audio data到一个aiff,wav或caf文件。然后,在Xcode中,加入声音文件到工程中as a nonlocalized resource of the application bundle.
你可以使用afconvert工具来转换声音。例如,要转换16-bit linear PCM系统声音 Submarine.aiff到IMA4 audio in a CAF file,使用下面的命令在应用终端:
afconvert /System/Library/Sounds/Submarine.aiff ~/Desktop/sub.caf -d ima4 -f caff -v
你可以检查声音来决定其数据格式,是用QuickTime Player打开还是选择显示Movie Inspector from the Movie Menu。
自定义声音必须少于30秒。如果超过了限制,取而代之的,会播放系统声音。
2,安排本地通知:
创建和安排本地通知需要下面一些简单的步骤:
1)分配并实例化UILocalNotification对象。
2)设置fireDate属性。
如果你为current locale(目前场所)设置了timeZone属性为NSTimeZone对象 ,系统会在设备旅行到不同时区时自动调整fire Date。你还可以安排通知为重复周期(daily,weekly,monthly,等)
3)配置通知的substance:
a)alert有一个alertBody属性来表示消息,动作按钮(action button)的titile(标题)或slider的属性为alertAction;这两个字符串属性都可以国际化。
b)设置应用图标的标记通过 applicationIconBadgeNumber属性
c)指定自定义声音的文件名(在应用mian bundle中的),通过soundName属性;要获得默认系统声音,指定UILocalNotificationDefaultSoundName。声音应该总是陪伴一个警告消息或图标标记;它们不应该通过其他方式播放。
4)作为选项,你可以附加自定义数据,通过customInfo属性。 在userInfo字典中的keys and values 必须是property-list objects. 这里不太懂..
5)安排本地通知 for delivery.
在Listing2-1中的方法创建并安排了一个通知来告知用户一个假设的to-do list。
Listing 2-1 Creating, configuring, and scheduling a local notification
- (void)scheduleNotificationWithItem:(ToDoItem *)item interval:(int)minutesBefore {
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSDateComponents *dateComps = [[NSDateComponents alloc] init];
[dateComps setDay:item.day];
[dateComps setMonth:item.month];
[dateComps setYear:item.year];
[dateComps setHour:item.hour];
[dateComps setMinute:item.minute];
NSDate *itemDate = [calendar dateFromComponents:dateComps];
[dateComps release];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
return;
localNotif.fireDate = [itemDate dateByAddingTimeInterval:-(minutesBefore*60)];
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ in %i minutes.", nil),
item.eventName, minutesBefore];
localNotif.alertAction = NSLocalizedString(@"View Details", nil);
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:item.eventName forKey:ToDoItemKey];
localNotif.userInfo = infoDict;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
}
你可以取消一个特定的scheduled通知通过在应用程序对象上调用cancelLocalNotification:,或通过cancelAllLocalNotifications取消所有的通知.这两个方法都取消当前正在显示的通知消息。
当应用运行于后台时,一些消息,数据或其他对象到达,它们应该立即提供通知--使用UIApplication方法presentLocalNotificationNow:(iOS给应用程序一个限定的时间来运行于后台),Listing2-2举例说明了你可能要如何做,如下所示:
Listing 2-2 Presenting a local notification immediately while running in the background
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"Application entered background state.");
// bgTask is instance variable
NSAssert(self->bgTask == UIInvalidBackgroundTask, nil);
bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
});
}];
dispatch_async(dispatch_get_main_queue(), ^{
while ([application backgroundTimeRemaining] > 1.0) {
NSString *friend = [self checkForIncomingChat];
if (friend) {
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif) {
localNotif.alertBody = [NSString stringWithFormat:
NSLocalizedString(@"%@ has a message for you.", nil), friend];
localNotif.alertAction = NSLocalizedString(@"Read Message", nil);
localNotif.soundName = @"alarmsound.caf";
localNotif.applicationIconBadgeNumber = 1;
[application presentLocalNotificationNow:localNotif];
[localNotif release];
friend = nil;
break;
}
}
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
});
}
3,注册远程通知:
应用程序必须通过Apple Push Notification service(APNs)来注册设备上的操作系统来接收来自应用程序的provider发送的远程通知。注册有3步:
1)应用调用registerForRemoteNotificationTypes:
2)代理方法实现application:didRegisterForRemoteNotificationsWithDeviceToken:来接收device token。
3)它传递device token到它的provider,as a non-object,binary value(作为一个非对象,二进制的value)。
在application,device和APNs和provider之间发生的事情将在后面讲解。
应用程序应该在每一次启动时注册并给其provider current token。它调用registerForRemoteNotificationTypes:来kick off(开始)注册进程。这个方法的参数使用UIRemoteNotificationType 比特掩码--例如,图标比较和声音,但没有警告。在IOS 中,用户可以稍后在系统设置界面修改通知类型。在iOS的Mac OS X中,你都可以通过调用enableRemoteNotificationTypes来重新使能通知类型。如果3种通知类型的任意一种没有被启用,操作系统都将相应的不标记图标,显示警告,或播放声音,甚至他们在通知payload中已经被提供。
如果注册成功,APNs返回一个device token,并且iOS传递token给应用程序代理application:didRegisterForRemoteNotificationDeviceToken:方法。应用应该连接他的provider并传递token,编码为二进制格式.如果在获取token时出现问题,操作系统将通知代理application:didFailToRegisterForRemoteNotificationWithError:方法。NSError对象传递到方法中清楚地描述了错误的原因。错误可能是,错误的aps-enviroment值在provisioning file中。你应该视错误为一个短暂的状态,并且不要尝试解析它。
iOS提示:如果一个蜂窝或Wi-Fi连接不可达,两个代理方法都将不会被调用。对于Wi-Fi连接,这有时发生于设备不能连接APNs的5223端口。如果这发生了,用户可以移动到可以连接的地方。在另外的情况下,连接将会成功并且某个代理函数会被调用。
通过你的应用程序每一次启动时请求token并传递其到provider,你帮助provider确保其拥有你的设备的当前token。如果一个用户其设备到备份状态,它应该至少再重新启动一次应用程序来注册通知。
Listing2-3给出了一个简单的例子,你如何注册远程通知在iOS中。
Listing 2-3 Registering for remote notifications
- (void)applicationDidFinishLaunching:(UIApplication *)app {
// other setup tasks here....
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
}
// Delegation methods
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
const void *devTokenBytes = [devToken bytes];
self.registered = YES;
[self sendProviderDeviceToken:devTokenBytes]; // custom method
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(@"Error in registration. Error: %@", err);
}
4,处理本地和远程通知:
让我们回顾下当操作系统投递通知时可能的场景:
1)通知被通知时,应用没有运行,这时通知被显示,无论是点击通知还是点击应用图标启动程序,系统都会启动应用,并调用其代理application:didFinishLaunchingWithOptions:方法,它传递通知的payload或本地通知对象。
2) 通知被通知时,应用运行于后台,这时如果点击通知,应用调用application:didReceiveRemoteNotification:方法或application:didReceiveLocalNotification:方法,如果点击应用图标,则不会自动执行上面的方法。
3) 通知被投递时应用运行于前台。应用程序直接调用(因为应用在前台)其代理函数application:didReceiveRemoteNotification:方法或application:didReceiveLocalNotification:方法,并传递通知的payload或本地通知对象。
应用可以使用传递进来的远程通知payload或UILocalNotification对象来帮助其设置上下文来处理相关项。理想地,代理做下面的事情来处理本地和远程通知的投递:
1)对于Mac OS X,它应该遵守NSApplicationDelegate协议,并实现applicationDidFinishLaunching:方法和application:didReceiveRemoteNotification:方法。
2)对于iOS,它应该采用UIApplicationDelegate协议并实现application:didFinishLaunchingWithOptions:方法和application:didReceiveRemoteNotification:和application:didReceiveLocalNotification:方法。
iOS提示:你可以决定应用启动时,是用户点击了动作按钮还是通知被投递到一个已经运行的应用,通过检查应用程序的状态。在其代理函数 application:didReceiveRemoteNotification:或application:didReceiveLocalNotification:方法,获取applicationState属性的值并evaluate它。如果值是UIApplicationStateInactive,那就是用户点击了动作按钮,如果是UIApplicationStateActive,那就是应用已经运行于前台。(还有一个Background状态,但是好像不是为iOS准备的)
在Listing2-4中实现了application:didFinishLaunchingWithOptions:方法,处理一个本地通知。它获得相关的UILocalNotification对象通过launch-options字典,使用UIApplicationLaunchOptionsLocalNotificationKey键值。从UILocalNotification对象的userInfo字典中,它访问to-do item。就像例子中显示的,你应该适当地重置应用图标上的标记数字--或移除它,作为处理通知的一部分。
Listing 2-4 Handling a local notification when an application is launched
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *localNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
NSString *itemName = [localNotif.userInfo objectForKey:ToDoItemKey];
[viewController displayItem:itemName]; // custom method
application.applicationIconBadgeNumber = localNotif.applicationIconBadgeNumber-1;
}
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
对于远程通知的实现是类似的,除了使用一个特殊的常量来访问payload:
1)在iOS中,使用UIApplicationLaunchOptionsRemoteNotificationKey来从launch-options字典中访问payload。
2)在MacOSX,使用NSApplicationLaunchRemoteNotificationKey老从userInfo字典中访问payload。
payload本身是一个NSDictionary对象,包含通知的各部分元素--警告消息,标记数字,声音等。它还可以包括应用可以使用的自定义数据,来提供上下文来决定初始化用户界面。
重要:你应该永远不要在通知的payload中定义自定义属性来传送用户数据或任何其他敏感数据。远程通知的投递是不被信任的。
当处理远程通知时(在application:didFinishLaunchingWithOptions:中),应用代理可以执行一个主要的附加任务。应用加载后,代理应该连接其provider并获取waiting data。Listing2-5给出了这个过程的概要。
Listing 2-5 Downloading data from a provider
- (void)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)opts {
// check launchOptions for notification payload and custom data, set UI context
[self startDownloadingDataFromProvider]; // custom method
app.applicationIconBadgeNumber = 0;
// other setup tasks here....
}
注意:客户端程序应该总是异步地与其provider通信或在secondary线程中。
Listing2-6中的代码显示了application:didReceiveLocalNotification:方法的实现,它将在应用运行于前台时执行。这里,应用代理做Listing2-4中同样的工作。它可以直接访问UILocalNotification,因为这是其方法的一个参数。
Listing 2-6 Handling a local notification when an application is already running
- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif {
NSString *itemName = [notif.userInfo objectForKey:ToDoItemKey]
[viewController displayItem:itemName]; // custom method
application.applicationIconBadgeNumber = notification.applicationIconBadgeNumber-1;
}
注意:在iOS中,如果你在远程通知警告显示后解锁设备,操作系统会自动触发警告的action button的操作。(这个行为与短信和日历提醒一致)。这使得“远程通知的相关操作不能引起毁灭性的后果"很重要,用户应该始终能对这些毁灭性的数据操作做出选择。
5,传递Provider当前语言Preference(远程通知):
如果应用没有使用aps字典的loc-key和loc-args属性在客户端侧获取本地化的警告消息,provider需要本地化放入通知payload中的警告消息文本。要做这个,provider需要知道在设备上用户选择的语言。客户端程序应该发送给其provider一个preferred language的标识;这可以是一个canonicalized IETF BCP 47 语言标识,如"en" 或 "fr"
Listing2-7举例说明了获取当前选择的语言和将其提交给provider。在iOS 中,通过NSLocale的preferredLanguages返回的array包含一个object:一个NSString对象,封装了语言代码标识了preferred语言。通过UTF8String转换其为C字符串。
Listing 2-7 Getting the current supported language and sending it to the provider
NSString *preferredLang = [[NSLocale preferredLanguages] objectAtIndex:0];
const char *langStr = [preferredLang UTF8String];
[self sendProviderCurrentLanguage:langStr]; // custom method
}
应用可以每一次在用户改变current locale的时候都将preferred language发送给其provider。要做这个,你可以监听NSCurrentLocaleDidChangeNotification通知,并且在你的通知处理函数中,获取preferred Language的标识并发送其到provider。
如果preferred language不是应用支持的,provider应该本地化消息为一个广泛被使用的语言例如英语或西班牙语。
(二)Apple Push Notification Service
1,一个Push通知和它的路径:

2,Feedback Service:
有时APNs尝试投递通知到一个设备上的应用,但是设备可能重复地拒绝投递,因为没有目标应用。这通常发生于卸载程序后。在这种情况下,APNs会通知provider--通过一个feedback服务。feedback服务为每个重复地投递通知失败的应用程序维持一个设备列表。provider应该获取这个设备列表并停止发送通知给他们。
3,Quality of Service:
APNs包括一个默认的Quality Of Service(QoS)组件,执行存储-转换功能。如果APNs尝试投递通知,但是设备离线,QoS存储通知。其只保持一个通知对于一个设备的每个应用:从provider上收到的最后的到那个应用的通知。当离线的设备又重新连接时,QoS转换存储的通知到设备上。QoS在删除通知之前保持通知一段限定的时间。
4,安全技术:
要使能provider和设备之间的通信,APNs必须为他们开放特定的入口。但是为了确保安全,它必须规范入口点的访问。为了这个目的,APNs请求两种不同的信任级别 for providers,devices和他们的通信. 这被称为 connection trust和token trust。
5,Service-to-Device Connection Trust: TLS
6,Provider-to-Service Connection Trust: TLS
7,Token Generation and Dispersal(传播):


8,Token Trust(Notification):

9,Trust Components:
要支持APNs的安全模式,provider和设备必须持有特定的证书,鉴权Certificate authority(CA)证书或tokens。
1)Provider:
2)Device:
10,通知的payload:
一个通知的payload的最大值是256bit;APNs拒绝超过限制的通知。记住通知的投递是尽力的并且是不保证的。
每一个通知,provider都必须构成一个JSON字典对象并严格遵循RFC 4627.这个字典必须包含一个键值为aps的一个字典。aps字典包含一个或多个下面指定的属性:
1)一个警告消息
2)一个图标标记数字
3)一个要播放的声音。
如果通知被投递到设备时,应用没有运行,系统会显示警告信息,播放声音,标记数字。如果应用正在运行,系统会投递到应用程序代理并将通知作为一个参数。
Provider可以在Apple保留的aps空间外,指定自定义的payload values .自定义的值必须使用JSON结构和简单类型:字典,数组,字符串,数字和布尔值。你不应该包括客户信息作为payload data。取而代之的,使用用来设置上下文或internal metrics的数据。任何通知动作都不应该是毁灭性的,例如删除设备上的数据。
下面列出了payload中,aps字典中的键和期望的值:
1)alert:字符串或字典。如果是字符串,它编程显示两个按钮的alert的消息文本:Close和查看。如果是指定字典,则看下面的解释。
2)badge:number,如果这个属性缺失,图标标记将会被移除。
3)sound:字符串:在应用束(app bundle)内的要播放的声音文件。如果声音文件不存在或其指定的是default,会播放默认声音。
alert属性的子属性:
1)body:消息文本。
2)action-loc-key:字符串或空null:如果指定了一个字符串:警告将显示两个按钮。iOS会获得View的本地化字符串。如果value是空,系统显示一个警告只包括一个按钮,就是OK
3)loc-key:字符串:警告消息字符串在localizable.strings文件的key。The key string can be formatted with %@ and %n$@ specifiers to take the variables specified in loc-args.
4)loc-args:array of strings:取代loc-key中的占位符。
5)launch-image:string:应用束内的图片的文件名;她可能包括扩展名或忽略它。图像用来显示动作按钮被点击时加载应用程序时显示的图像。如果这个属性未被指定,系统或者使用之前的快照,或者使用应用程序的info.plist文件中UILaunchImageFile键键值指定的图像,或使用Default.png。这个属性在iOS4.0以后有效。
11,本地化字符串:
例如:
"alert" : { "loc-key" : "GAME_PLAY_REQUEST_FORMAT", "loc-args" : [ "Jenna", "Frank"] },
当设备收到通知时,它将在当前语言的.lproj文件夹中的Localizable.strings文件中使用"GAME_PLAY_REQUEST_FORMAT"作为键值去查找相关的字符串值。假定当前的Localizable.strings文件中有下面的:“Jenna and Frank have invited you to play Monopoly”.
"GAME_PLAY_REQUEST_FORMAT" = "%@ and %@ have invited you to play Monopoly";
设备将显示警告消息如下:
除了%@格式符,你还可以使用%n$@格式符,n是loc-args中的array的下标(从1开始).例如:
"GAME_PLAY_REQUEST_FORMAT" = "%2$@ and %1$@ have invited you to play Monopoly";设备将显示警告消息如下:"Frank and Jenna have invited you to play Monopoly". (Jenna和Frank换了位置)
12,JSON Payloads的例子:
例子包括空白符和新行;为了更好的性能,provider应该删除空白符和新行字符。
http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/ApplePushService/ApplePushService.html
四,Provisioning and 开发:
http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/ProvisioningDevelopment/ProvisioningDevelopment.html#//apple_ref/doc/uid/TP40008194-CH104-SW1
五,Provider Communication with Apple Push Notification Service:发送通知: 通信端口:2195
通信服务器:gateway.push.apple.com(Production Environment)和 gateway.sandbox.push.apple.com(Development Enviroment)
需要 从mac电脑上导出相应的证书 .p12文件
需要使用TSL或SSL连接到服务器
简单的通知格式为:

通知不能超过256个字节,并且不能null-terminated。
发送成功与否,只要检查SSLWrite是否成功执行即可。这个成功只表示把通知正常发送到服务器,至于deviceToken是否合法,payload格式是否正确都不保证。
第二种通知格式:

identifier为这个通知的唯一标识符
Expiry表示一个固定UNIX epoch日期,使用秒表示(UTC),何时通知变得无效,并且可以被丢弃。
发送成功与否,可判断服务器返回的包。如果不返回任何内容,则表示没有错误,返回的包格式如下:

第一个字节为8,是固定的命令字节。
第2个字节为状态码
后面为发送通知时给通知指定的唯一标识符。
Status code含义如下:
0表示没有错误发生
1表示Processing Error
2表示Missing Device Token
3表示Missing Topic
4表示Missing Payload
5表示Invalid Token size
6表示Invalid Topic size
7表示Invalid Payload size
8表示Invalid Token
255表示None(Unknown)
Feedback服务:
通信服务器: feedback.push.apple.com 或 feedback.sandbox.push.apple.com
通信端口:2196
需要 .p12文件
需要TSL 或 SSL 连接到服务器
一旦建立了连接,就开始传送数据,你不需要发送任何命令给APNs.
返回包格式为:

前4个字节:表示时间,表示何时APNs确定该设备不可达。表示从(UTC)1970年到现在的秒。你应该使用时间戳来确定在这个时间之后,设备是否已经重新注册了。
接着的2个字节:一般为32,表示后面跟着的deviceToken的长度
GitHub开源包: Redth / APNS-Sharp 地址: https://github.com/Redth/APNS-Sharp
简单用法:
using JdSoft.Apple.Apns.Notifications;
static void Main(string[] args)
{
//你可能需要编辑的变量:
//---------------------------------
//True if you are using sandbox certificate, or false if using production
bool sandbox = true;
//这里放置deviceTokens,例如从数据库中读出,这里写死了两个
List<String> deviceTokens=new List<string>();
deviceTokens.Add("867beb4efeef59904b0c1b5ba824c9e949b93e389987cd81666c696a7161cf58");
deviceTokens.Add("ab593e0603369343732b8f7297bc99f1a6f688afd5767972d30b2fe950bc735e");
//Put your PKCS12 .p12 or .pfx filename here.
//Assumes it is in the same directory as your app
string p12File = "c:/cer.p12";
//This is the password that you protected your p12File
// If you did not use a password, set it as null or an empty string
string p12FilePassword = "123";
//要发送通知的数量
int count = 3;
// 发送多个通知之间的等待时间间隔,单位为ms
int sleepBetweenNotifications = 2000;
//Actual Code starts below:
//--------------------------------
string p12Filename = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, p12File);
//最后一个参数为有多少个设备就建多少个连接
NotificationService service = new NotificationService(sandbox, p12Filename, p12FilePassword, deviceTokens.Count);
service.SendRetries = 5; //5 retries before generating notificationfailed event
service.ReconnectDelay = 5000; //5 seconds
service.Error += new NotificationService.OnError(service_Error); //增加委托函数
service.NotificationTooLong += new NotificationService.OnNotificationTooLong(service_NotificationTooLong); //增加委托函数
service.BadDeviceToken += new NotificationService.OnBadDeviceToken(service_BadDeviceToken); //增加委托函数
service.NotificationFailed += new NotificationService.OnNotificationFailed(service_NotificationFailed); //增加委托函数
service.NotificationSuccess += new NotificationService.OnNotificationSuccess(service_NotificationSuccess); //增加委托函数
service.Connecting += new NotificationService.OnConnecting(service_Connecting); //增加委托函数
service.Connected += new NotificationService.OnConnected(service_Connected); //增加委托函数
service.Disconnected += new NotificationService.OnDisconnected(service_Disconnected); //增加委托函数
//The notifications will be sent like this:
// Testing: 1...
// Testing: 2...
// Testing: 3...
// etc...
for (int i = 1; i <= count; i++)
{
//Create a new notification to send
List<Notification> notificationS=new List<Notification>();
foreach (String testDeviceToken in deviceTokens)
{
Notification alertNotification = new Notification(testDeviceToken);
alertNotification.Payload.Alert.Body = string.Format("Testing {0}...", i);
alertNotification.Payload.Sound = "default";
alertNotification.Payload.Badge = i;
notificationS.Add(alertNotification);
}
//Queue the notification to be sent
foreach (Notification alertNotification in notificationS)
{
if (service.QueueNotification(alertNotification))
Console.WriteLine("Notification Queued!");
else
Console.WriteLine("Notification Failed to be Queued!");
}
//Sleep in between each message
if (i < count)
{
Console.WriteLine("Sleeping " + sleepBetweenNotifications + " milliseconds before next Notification...");
System.Threading.Thread.Sleep(sleepBetweenNotifications);
}
}
Console.WriteLine("Cleaning Up...");
//First, close the service.
//This ensures any queued notifications get sent befor the connections are closed
service.Close();
//Clean up
service.Dispose();
Console.WriteLine("Done!");
Console.WriteLine("Press enter to exit...");
Console.ReadLine();
}
...
浙公网安备 33010602011771号