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];
Related
I currently am looking to do push notifications for an iOS app. I use PostgresSQL and NodeJS for the backend and as I look online, there doesnt seem to be a direct way to implement PEER TO PEER push notifications, between two devices. Any help from people with experience would be very helpful.
You can segment users you wish to send individual messages to using the Parse SDK. Their service is retiring in January, but you can still host your own MongoDB instance and retain the full functionality of their platform.
Register your logged in user to receive push notifications:
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
Implement the didRegisterForRemoteNotificationsWithDeviceToken method in your AppDelegate
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken {
NSLog(#"didRegisterForRemoteNotifications");
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:newDeviceToken];
if ([PFUser currentUser]) {
[currentInstallation setObject:[PFUser currentUser] forKey:#"user"];
}
[currentInstallation saveInBackground];
}
This creates a pointer to the User's account which we'll use to segment the user with this:
NSString *toUserObjectId = someUser.objectId; //someUser is a 'PFUser' object
// segment the user
PFQuery *innerQuery = [PFUser query];
[innerQuery whereKey:#"objectId" equalTo:toUserObjectId];
PFQuery *query = [PFInstallation query];
[query whereKey:#"user" matchesQuery:innerQuery];
NSDictionary *pushData = #{
#"alert": #"some message",
// #"p": someStringPayload,
#"badge": #"Increment",
#"sound": #"cheering.caf",
};
PFPush *push = [[PFPush alloc] init];
[push setData:pushData];
//[push expireAfterTimeInterval:interval];
[push setQuery:query];
[push sendPushInBackground];
If you're going this route, I'd start a new app on their existing service, then migrate somewhere else. Hope this helps.
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"];
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)
}
}
Using
Parse
Scenario = I have an app that sends messages. When a message is sent to a user it will also send a push notification.
Difficulties = When the app launches it calls
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
if ([PFUser currentUser]) {
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:deviceToken];
[currentInstallation setObject:[PFUser currentUser][#"userID"] forKey:#"userID"];
[currentInstallation saveInBackground];
NSLog(#"INSTALLATION REGISTERED");
}
}
When the user first downloads the app, there will be no currentUser created yet. Therefore, the app checks inside the didRegisterForRemoteNotificationsWithDeviceToken method if the currentUser exists. Since the currentUser does not exist upon starting the app for the first time (they must create an account first), I would like to be able to call didRegisterForRemoteNotificationsWithDeviceToken again after the currentUser has been created.
Question = How do I call didRegisterForRemoteNotificationsWithDeviceToken outside of the AppDelegate class?
What I've tried = This code when I sign up my user
UIApplication *app = [[UIApplication alloc]init];
[app registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound];
This throws an error
*** Assertion failure in -[UIApplication init]
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'There can only be one UIApplication instance.'
Use this
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge|
UIRemoteNotificationTypeAlert|
UIRemoteNotificationTypeSound];
You shouldn't call didRegisterForRemoteNotificationsWithDeviceToken directly as it is part of a delegate protocol. You should save your deviceToken and pass it to the PFInstallation later, something like:
AppDelegate.m
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
self.token = deviceToken;
[self saveInstallation];
}
-(void)saveInstallation{
if ([PFUser currentUser]) {
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:self.token];
[currentInstallation setObject:[PFUser currentUser][#"userID"] forKey:#"userID"];
[currentInstallation saveInBackground];
NSLog(#"INSTALLATION REGISTERED");
}
}
AppDelegate.h
#property(strong) NSData* token;
-(void)saveInstallation;
RegistrationScreen.m
#import "AppDelegate.h"
-(void)yourSaveAction{
// Call this after having a valid PFUser.
[(AppDelegate*)[[UIApplication sharedApplication] delegate] saveInstallation];
}
UIApplication is a singleton and you should call it using sharedApplication.
UIApplication * app = [UIApplication sharedApplication];
[app registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound];
[EDIT]
I made a mistake registerForRemote is a UIApplication method
why are you doing this
UIApplication *app = [[UIApplication alloc]init];
[app registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound];
Instead you should know that UIApplication is a singleton class thus you can do this
[[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
use the above code where you want
Hope this will help you. happy coding :)