tagabek
tagabek

Reputation: 1954

Sending a Push Notification from one user to another user with Parse

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

Upvotes: 11

Views: 16198

Answers (5)

djshiow
djshiow

Reputation: 1033

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

Upvotes: 0

Wesley Smith
Wesley Smith

Reputation: 19571

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];
                }
            }];
        }
    }];
}

Upvotes: 4

Nic Huang
Nic Huang

Reputation: 291

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.

Upvotes: 3

Logan
Logan

Reputation: 53142

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 {
                }
            }];

Upvotes: 0

Lyndsey Scott
Lyndsey Scott

Reputation: 37300

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];

Upvotes: 0

Related Questions