i recently set up push notifications for my app using Parse. I have been able to get the notifications and the data sent but now i want to get the channel to which the notification has been sent. Is there any way to do so in ios?
Have you checked this guide? It's really helpful.
https://www.parse.com/tutorials/ios-push-notifications
Here's a quick summary. Create development and production certificates and then attach them to your developer account for the app that you want to be able to send pushes to. After attaching these certificates redownload them, change them to .p12 files w Keychain Access and then upload them to Parse.com. In Xcode make sure to go into your account via preferences and refresh it so you'll have an updated provisioning profile. I'd do this in the stable version of Xcode and not the beta.
Finally after doing all that you can start coding. In your app delegate attach this code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes
categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
...
}
Please note that the following code demonstrates how to add a user to a channel... This is Parse's default code and I actually suggest tweaking it a bit. They set it up so that your channels will be reset to global every time the application is launched. I'm not sure that you even need to invoke the method "registerUserNotificationSetting" at all after attaching the device's token to the backend. Take not this method will generate an API request...
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:deviceToken];
// This is Parse's default code
currentInstallation.channels = #[ #"global" ];
// I would change it to something like this
if (currentInstallation.channels.count == 0) {
currentInstallation.channels = #[ #"global" ];
}
[currentInstallation saveInBackground];
}
Finally the last bit of code just deals with what the application does if it receives a notification while in the foreground:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[PFPush handlePush:userInfo];
}
Good luck!
When you send the notification just add the channel name to the notifications data.
For example:
PFPush *push = [[PFPush alloc] init];
[push setChannel:#"my_channel"];
NSDictionary *data = #{#"alert":#"Hi there.",#"channel":#"my_channel"};
[push setData:data];
[push sendPushInBackground];
When you receive the notification you can simple get the channel from the payload/userInfo:
NSString *channel = payload[#"channel"];
Related
I have an app that I'm building, and I'm trying to keep track of a subscription using Cloudkit. I have a RecordType called Notifications with one field of type String called NotificationText. For some reason, when I add a new record, the app does not receive it. Here's what I have done so far:
Registered for Cloudkit in the "Capabilities" section of the app.
Added the Required background modes key to the info.plist file for remote-notifications
Saved the Subscription to the database using:
CKSubscription *subscription = [[CKSubscription alloc]
initWithRecordType:#"Notifications"
predicate:[NSPredicate predicateWithValue:YES]
options:CKSubscriptionOptionsFiresOnRecordCreation];
CKNotificationInfo *notificationInfo = [CKNotificationInfo new];
notificationInfo.alertLocalizationKey = #"NotificationText";
notificationInfo.shouldBadge = YES;
notificationInfo.soundName = #"";
subscription.notificationInfo = notificationInfo;
[publicDB saveSubscription:subscription
completionHandler:^(CKSubscription *subscription, NSError *error) {
if (error)
[self handleError:error];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"sub"];
}
];
Requested permission from the user to send push notifications using:
UIApplication *application = [UIApplication sharedApplication];
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:nil];
[application registerUserNotificationSettings:notificationSettings];
[application registerForRemoteNotifications];
Implemented the -(void)application:(UIApplication *)application didReceiveRemoteNotification:(non null NSDictionary *)userInfo fetchCompletionHandler:(non null void (^)(UIBackgroundFetchResult))completionHandler; in the AppDelegate.m file.
Now I go into the Cloudkit Dashboard and create a new record of RecordType Notifications, and nothing happens. Am I doing something wrong? Am I missing something?
After much searching and banging my head against a wall, I found my problem.
PUSH NOTIFICATIONS DO NOT WORK ON SIMULATORS!
I hooked up an iPod to the app and, bang! Notification was received.
I am trying to send and receive push notifications, so when, for example, a certain switch is turned off, another phone with the same app is notified that this has happened by sending an alert message.
The push notifications are being sent successfully to Parse as my account shows all of them sent by my app. However, no messages actually reach the intended device. The list of notifications sent by my app show 0 "Pushes sent" each.
I have sent push notifications from Parse itself to my device and I do receive those correctly, so I don't think it's a problem with the configuration for the notifications.
I followed the Parse guide to configure these but I'll post my code anyways:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Parse setApplicationId:#"****My app id****"
clientKey:#"****My key****"];
UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes
categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// Store the deviceToken in the current installation and save it to Parse.
[PFPush storeDeviceToken:deviceToken];
[PFPush subscribeToChannelInBackground:#""];
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setObject:[PFUser currentUser] forKey:#"owner"];
[currentInstallation saveInBackground];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[PFPush handlePush:userInfo];
}
Inside my method where my switch changes state:
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addUniqueObject:#"HI" forKey:#"try"];
[currentInstallation saveInBackground];
PFPush *push = [[PFPush alloc] init];
[push setChannel:#"HI"];
[push setMessage:#"I am sending a push notification!"];
[push sendPushInBackground];
As shown below, Parse receives my push, but has 0 "Pushes sent". There's only 1 subscriber when I send the push from Parse itself, where my iphone does receive it.
So basically, how can I get Parse to send that push notification I'm sending from my app in the first place back to my device? I've spent hours researching so please help!!
As mentioned by Paulw11, On first device, you need to register for channel "Hi"
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addUniqueObject:#"Hi" forKey:#"channels"];
[currentInstallation saveInBackground];
On second device, where you have change switch part
PFPush *push = [[PFPush alloc] init];
[push setChannel:#"Hi"];
[push setMessage:#"I am sending a push notification!"];
[push sendPushInBackground];
I am building an app that will implement a Parse backend to send push notifications. Users will be able to send other users a message contained in a push notification. I have been playing around with this and when I send it registers on the Parse website fine but only about 50% of the messages sent are being received on the device. Does anyone else have this problem? I know Push Notifications are not guaranteed but a 50% success rate? Any ideas?
Thanks
code below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Parse setApplicationId:#"***"
clientKey:#"****"];
UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes
categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:deviceToken];
currentInstallation.channels = #[ #"global" ];
[currentInstallation saveInBackground];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
[PFPush handlePush:userInfo];
}
- (void)notifyFriendAtIndex:(NSInteger)index completionHandler:(userCompletionHandler)handler
{
PFQuery *pushQuery = [PFInstallation query];
[pushQuery whereKey:#"deviceType" equalTo:#"ios"];
NSString *pushMessage = [NSString stringWithFormat:#"From %# HI!", self.currentUser.username];
NSDictionary *pushData = #{#"alert": pushMessage, #"badge": #0, #"sound": #"notify.wav"};
PFPush *push = [[PFPush alloc] init];
[push setQuery:pushQuery];
[push setData:pushData];
[push sendPushInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
handler(NO, error);
}
else {
handler(YES, nil);
}
}];
}
I have not experienced any major problems in the past with Parse's Push Notification delivery. One of my apps has about 32,000 registered devices and seems to get near 100% delivery.
I also have a couple of chat apps that use Parse as the messaging and push back-end. Users can subscribe to a particular chat room and get Push Notification through Segmentation. One of those apps just launched and so far the push notifications are coming through 100%. The only thing I've noticed is a slight delay sometimes. Maybe a minute or two, but I think that could be because of a slow wifi connection.
I also use PushWoosh for general Broadcast Push Notifications. They are great, but sometimes PushWoosh takes longer to deliver. Parse is usually faster so the bottom line is I think Parse's Push Notifications are reliable.
In my experience, i concluded that most is due the main push notification deliverer ( Apple for iOS, Google for Android ). I'm saying this because we have an app in Parse that send push notification on both iOS and Android, and some days we don't receive any push notification, some others a few or all. All this without changing any implementation. In past, i used another push notification service called Urban Airship (that rely on the proper push notification deliverers), and there was the same problem, some days "almost" ok, some other no work.
Just check if your testing device are correctly registered in the "Installation" table with proper deviceToken set. We send the push notification server side through channels. I write here the code maybe it could help you:
var dataStruct ={
alert: "Hi there!",
my_additional_info: "normal chat message"
};
Parse.Push.send({
channels: ["your_custom_registered_installation_channel_device"],
data: dataStruct
}).then(function() {
promise.resolve();
}, function(error) {
promise.reject(error);
});
So normally it depends by the day, but if you have this statistic (50%) maybe the installation is not always registered/updated when your iPhone register for push notification
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
// Save/update device tocken
[currentInstallation setDeviceTokenFromData:deviceToken];
// store your channel
[currentInstallation setChannels:#["channel_123"]];
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
// Do your stuff
}];
}
Hi I have an app that I have set up with push notifications and they are working. My last step is that when a user opens the app and is asked if they want to allow push notification, I want to set a pointer from that user to the installation id associated with that phone. I can do that using this
PFInstallation *currentInstalation = [PFInstallation currentInstallation];
NSString *installationObjectId = currentInstalation.objectId;
[currentUser setObject:installationObjectId forKey:#"installationString"];
[currentUser setObject:currentInstalation forKey:#"installation"];
Here is a pic of part of my user class to clarify
But I don't want to make this save every time the user opens the app, I just want to do it the once if it has not been set yet. So I was going to use this if statement to check if it had been set yet
if (![currentUser[#"installationString"] isEqual:installationObjectId]) {
//save it here
}
But the problem comes if the user taps, "don't allow push notifications" because then there is no installation object set, so the installation object for that phone/ user is null and the above if statement gives the error below
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: 'Can't use nil for keys or values on PFObject. Use NSNull for values.'
And the app fails. Is there another/ better way to check if the pointer has been set, so that if the user taps "don't allow" and then reopens the app it won't quit out.
Thanks for the help in advance I really appreciate it!!!
EDIT
APP DELAGTE CODE
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)]) {
UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes
categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
} else {
// Register for Push Notifications before iOS 8
[application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeSound)];
}
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:deviceToken];
[currentInstallation saveInBackground];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[PFPush handlePush:userInfo];
}
And I have it set this way because I have a cloud function that send pushes to user and and get a pointer from the user to the installation id to send the push, maybe I could consider flipping that?
And if the user clicks don't allow. Doesn't Parse.com not save an installation id for that device?
Thanks
This should help PFInstallation *currentInstalation = [PFInstallation currentInstallation];
NSString *installationObjectId = currentInstalation.objectId;
[currentUser setObject:installationObjectId forKey:#"installationString"];
[currentUser setObject:currentInstalation forKey:#"installation"];
SWIFT:
var newUser = PFUser()
var currentInstallation = PFInstallation.currentInstallation()
let installationObjectId: NSString = currentInstallation.installationId
newUser.setObject(installationObjectId, forKey: "installationString")
newUser.setObject(currentInstallation, forKey: "installation")
newUser.saveInBackgroundWithBlock { (success, error) -> Void in
if error == nil {
print("success")
} else {
print(error)
}
}
I was wondering if there is a way to swap out the alertview style notification that happens when a user is inside the app when the notification occurs, to the banner notification style (which one would get if another app is active while receiving the push).
I am using the recommended Parse Push Notifications configuration as the following, but am unsure where I could make these changes (or that if its possible) - thanks for your help!
//In didFinishLaunchingWithOptions
[application registerForRemoteNotificationTypes: UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound];
//In appDelegate.m
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken {
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:newDeviceToken];
[currentInstallation saveInBackground];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[PFPush handlePush:userInfo];
}
You can't. You need to create and configure your own view and display it (explicitly add and remove it as a subview when you want is on and off screen). This means not calling handlePush: and instead running your own code to display your own view.