11

I have built a messaging app that is similar to Snapchat - one user can send another user pictures. I am trying to add push notifications to the app, so that when a message is sent from UserA to UserB, UserB receives a push notification of "New Message from UserA".

I have been researching this for hours now, and I feel like I am very close.

I am trying to use Parse to send push notifications. I would like it to work like this: When UserA sends UserB a message, UserB is also sent a push notification that says "New Message from UserA". I was successfully able to use the Parse website to send a push notification to devices using the application, but am NOT able to successfully send a push notification from within the app (when a user sends a message) to the receiving user's device.

The push notifications are apparently being sent successfully, as my Parse account shows the messages that I have sent. However, no messages actually reaches the intended device and the list of push notifications shows 0 subscribers for each push notification.

And I can click on one of those to see the details.

Also, I am using a Distribution/Production Provisioning Profile & Certificate.

Here's the code that I am using the send the push notification after UserA would send a message to UserB - the message object is the message that has been uploaded to Parse, and the messageRecipients are the users that the message is being sent to:

// Send Push Notification to recipients

NSArray *messageRecipients = [message objectForKey:@"recipientIds"];

PFQuery *pushQuery = [PFInstallation query];
                    [pushQuery whereKey:@"owner" containedIn:messageRecipients];

PFPush *push = [[PFPush alloc] init];
[push setQuery:pushQuery];
[push setMessage:[NSString stringWithFormat: @"New Message from %@!",  [PFUser currentUser].username]];
[push sendPushInBackground];

Here are my AppDelegate.m related methods:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [Parse setApplicationId:@"[This is where my app Id is]"
                  clientKey:@"[This is where client Id is]"];
    [self customizeUserInterface];
    [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound];

    return YES;
}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    [PFPush storeDeviceToken:deviceToken];
    [PFPush subscribeToChannelInBackground:@""];
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
    NSLog(@"Did fail to register for push, %@", error);
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    [PFPush handlePush:userInfo];
}

I have also submitted a post on the Parse.com forums: https://parse.com/questions/sending-a-push-notification-from-one-user-to-another-user

Is there something that I am missing or doing wrong?

EDIT: I am now able to see subscribers in my Parse account, but I am not actually receiving the push notifications on my device. The same goes for when I try to send a push notification test from the Parse website. enter image description here

4
  • Is "owner" stored on your Installation object? And are you sure [message objectForKey:@"recipientIds"] is returning the proper array? Commented Dec 17, 2013 at 6:14
  • Yes, I think it is. I logged the array and it is returning the correct array. Commented Dec 17, 2013 at 6:33
  • And "owner" is in fact in the installation table of your data browser on the Parse website? Commented Dec 17, 2013 at 6:39
  • It is in the Installation table for sure, and the type is string. Commented Dec 17, 2013 at 7:40

5 Answers 5

4

My searches kept coming back to here but nothing here really spelled it out for me. So here's how I got mine working:

In my AppDelegate.m I have:

- (void)applicationDidBecomeActive:(UIApplication *)application
{

    PFUser *currentUser = [PFUser currentUser];
    if (currentUser) {
        //save the installation
        PFInstallation *currentInstallation = [PFInstallation currentInstallation];
        currentInstallation[@"installationUser"] = [[PFUser currentUser]objectId];
        // here we add a column to the installation table and store the current user’s ID
        // this way we can target specific users later

        // while we’re at it, this is a good place to reset our app’s badge count
        // you have to do this locally as well as on the parse server by updating
        // the PFInstallation object
        if (currentInstallation.badge != 0) {
            currentInstallation.badge = 0;
            [currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
                if (error) {
                    // Handle error here with an alert…
                }
                else {
                    // only update locally if the remote update succeeded so they always match
                    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
                    NSLog(@"updated badge");
                }
            }];
        }
    } else {

        [PFUser logOut];
        // show the signup screen here....
    }
}

in the viewController where I send the push I have:

myViewController.h

@property (nonatomic, strong) NSMutableArray *recipients; // declare the array we'll use to store our recipients

myViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.recipients = [[NSMutableArray alloc] init]; // initialize the array we'll use to hold our recipients
}


// in another part of the code (not shown here) we set up a tableView with all of the current user's friends in it
// when the user taps a row in that tableView we add or remove the selected friend from our recipients list
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self.tableView deselectRowAtIndexPath:indexPath animated:NO];

    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
    PFUser *user = [self.friends objectAtIndex:indexPath.row];

    if (cell.accessoryType == UITableViewCellAccessoryNone) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        [self.recipients addObject:user.objectId]; //  user selected a recipient, add them to the array
    }
    else {
        cell.accessoryType = UITableViewCellAccessoryNone;
        [self.recipients removeObject:user.objectId]; //  user de-selected a recipient, remove them from the array
    }

}




- (void)uploadMessage 
{
    UIImage *newImage = [self resizeImage:self.image toWidth:640.0f andHeight:960.0f];
    NSData *fileData= UIImageJPEGRepresentation(newImage, 1.0);
    NSString *fileName= @"image.jpg";;
    NSString *fileType= @"image";

    PFFile *file = [PFFile fileWithName:fileName data:fileData];

    [file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
        if (error) {
                // Handle error here with an alert…
        }
        else {
            PFObject *message = [PFObject objectWithClassName:@"Messages"];
            [message setObject:file forKey:@"file"];
            [message setObject:fileType forKey:@"fileType"];
            [message setObject:self.recipients forKey:@"recipientIds"];
                  // self.recipients is an NSMutableArray of the objectIds for each 
                  // user the message will go to 
            [message setObject:[[PFUser currentUser] objectId] forKey:@"senderId"];
            [message setObject:[[PFUser currentUser] username] forKey:@"senderName"];
            [message saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
                if (error) {
                    // Handle error here with an alert…
                }
                else {
                    // Everything was successful! Reset UI… do other stuff
                    // Here’s where we will send the push
                    //set our options
                    NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys:
                                          @“Ne messages available!!”, @"alert",
                                          @"Increment", @"badge",
                                          nil];

                    // Now we’ll need to query all saved installations to find those of our recipients 
                    // Create our Installation query using the self.recipients array we already have
                    PFQuery *pushQuery = [PFInstallation query];
                    [pushQuery whereKey:@"installationUser" containedIn:self.recipients];

                    // Send push notification to our query
                    PFPush *push = [[PFPush alloc] init];
                    [push setQuery:pushQuery];
                    [push setData:data];
                    [push sendPushInBackground];
                }
            }];
        }
    }];
}
Sign up to request clarification or add additional context in comments.

7 Comments

If you create a new user while logging in, application will crash. You have to check that [PFUser currentUser] is not nil in applicationDidBecomeActive and then set currentInstallation[@"installationUser"] in didLoginUser
@NikolayDerkach Nice catch, fixed my answer to check for currentUser
@DelightedD0D [[PFUser currentUser]objectId] getting null value. any idea about :(
@BhaveshNai are you sure have a user logged in? Have you called [PFUser logInWithUsernameInBackground:@"myname" password:@"mypass"......]
@DelightedD0D Thanks for reply. one more thing , how to send multiple user notification like group discussion. Thanks in advance :)
|
3

I think I have the answer. I encountered the same problem and I just solved it.

When you are setting value to owner, you should use "[PFUser currentUser].objectId", instead of [PFUser currentUser]. The latter gives you a pointer to owner, but we need a string to set Push query within this situation.

When we first set owner to Installation, we should set objectId as string to owner instead of just [PFUser currentUser] like below.

[currentInstallation setObject:[PFUser currentUser].objectId forKey:@"owner"];

And later we could set the owner (string) to our pushQuery.

5 Comments

Hey Nic, thanks. When I change it to that, I get this error: "Error: invalid type for key owner, expected *_User, but got string".
You have that error because you trying to assign a string into a non-string column "owner"; You can drop the "owner" column in parse and created another column like "userId", and most importantly, give it a string type. And make sure your device saves PFInstallation with new userId string.
I got stuck here also: if you create a field with the wrong data type, you need to drop the column before you can attempt to insert new data.
I'm getting stuck here as well, I am storing a user id in my installation table as a string, and the passing that to my Push query. It appears to show up in the push browser online but just like in the original question shows that 0 were sent. is there any way to test the Push query?
@user379468 Did you assign the exact userId to pushQuery or just the [PFUser currentUser]? It's different. And there is another way to test that. Try push from Parse push console. Choose segment and select userId column from your installation and "equals" your userId (the user you want to send).
0

Given what you've posted so far, I'm not sure why your code isn't working for you. It seems as if either your Installation table doesn't contain an "owner" column or it contains an "owner" column with values/types other than those contained in your [message objectForKey:@"recipientIds"] array; but, just in case you need/want to try a back-up method, here's another way to send push notifications from one device to another using Parse:

First subscribe the user/group of users receiving the push to their own unique channel (in addition to the main channel), ex.

[PFPush subscribeToChannelInBackground:taylors_channel];

Then from the device sending the push, set the channel to that of the recipient:

PFPush *push = [[PFPush alloc] init];
[push setChannel:taylors_channel];
[push setMessage:[NSString stringWithFormat: @"New Message from %@!",  [PFUser currentUser].username]];
[push sendPushInBackground];

Additionally, you can also set the same channel for multiple peers or send a message to multiple channels at once using [push setChannels:channel_array];

Comments

0

To elaborate on djshiow's response, once you have saved the current installation as such.

PFQuery * pushQuery = [PFInstallation query];
PFUser * userReceivingPush; 
[pushQuery whereKey:@"owner" equalTo:userReceivingPush]; 

NSString * alert = [NSString stringWithFormat:@"You have a new message from %@!", [PFUser currentUser].username];
NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys:
                                  alert, @"alert",
                                  @"default", @"sound",
                                  @"Increment", @"badge",
                                  nil];
[PFPush sendPushDataToQueryInBackground:pushQuery withData:data block:^(BOOL succeeded, NSError *error) {
                if (!error) {

                }
                else {
                }
            }];

Comments

0

Have you tried storing the owner in the installation. ie.

- (void)application:(UIApplication *)application
        didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{

    PFInstallation *currentInstallation = [PFInstallation currentInstallation];
    [currentInstallation setDeviceTokenFromData:deviceToken];
    [currentInstallation setObject:[PFUser currentUser] forKey:@"owner"];
    [currentInstallation saveInBackground];
}

From: click here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.