Mahir Mahir - 9 days ago 5
iOS Question

CloudKit for sending push notifications through cron jobs?

I'm creating a college dining menu app, in which I need to send push notifications based on the daily menus. Originally, I was planning on storing user data in a database through Heroku and using cron jobs to compare the data in the database with the daily menus and send appropriate notifications to users.

After the news on Cloudkit, however, I thought I could use that instead to manage the server-related part of my code. After closer inspection, though, it seems like Cloudkit is currently capable of storing the data, but does not allow us to write server-side code.

I'm wondering if I interpreted this limitation correctly, or if I can, in fact, schedule a database on CloudKit to compare its data to the online menus each day and send appropriate push notifications.

Answer

It seems incredible you wouldn't use or consider Parse.com for something like this ...

(If not Parse, some other bAAs with a similar feature set.)

NOTE - Parse is now at back4app.com.

1) Parse is the easiest possible way to do push with an iOS app

2) the whole idea of Parse is that you have cloud code, and it's incredibly simple,

https://parse.com/docs/cloud_code_guide

you can have cloud code routines, and, you can have "cron" routines that go off regularly. it's why everyone's using Parse!

Note that it is super-easy to call "cloud functions" from iOS, with Parse.

Here's an example,

-(void)tableView:(UITableView *)tableView
        commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
        forRowAtIndexPath:(NSIndexPath *)indexPath
    {
    int thisRow = indexPath.row;
    PFUser *delFriend = [self.theFriends objectAtIndex:thisRow];

    NSLog(@"you wish to delete .. %@", [delFriend fullName] );

    // note, this cloud call is happily is set and forget
    // there's no return either way. life's like that sometimes

    [PFCloud callFunctionInBackground:@"clientRequestFriendRemove"
            withParameters:@{
                            @"removeThisFriendId":delFriend.objectId
                            }
            block:^(NSString *serverResult, NSError *error)
            {
            if (!error)
                {
                NSLog(@"ok, Return (string) %@", serverResult);
                }
            }];

    [self back];    // that simple
    }

Notice I'm calling a cloud function "clientRequestFriendRemove". So that's just a piece of cloud code I wrote and which is on our Parse account, in fact here it is

Parse.Cloud.define("clientRequestHandleInvite", function(request, response)
{
// called from the client, to accept an invite from invitorPerson

var thisUserObj = request.user;
var invitorPersonId = request.params.invitorPersonId;
var theMode = request.params.theMode;

// theMode is likely "accept" or "ignore"

console.log( "clientRequestAcceptInvite called....  invitorPersonId " + invitorPersonId + " By user: " + thisUserObj.id );
console.log( "clientRequestAcceptInvite called....  theMode is " + theMode );

if ( invitorPersonId == undefined || invitorPersonId == "" )
  {
  response.error("Problem in clientRequestAcceptInvite, 'invitorPersonId' missing or blank?");
  return;
  }

var query = new Parse.Query(Parse.User);
query.get(
  invitorPersonId,
    {
    success: function(theInvitorPersonObject)
      {
      console.log("clientRequestFriendRemove ... internal I got the userObj ...('no response' mode)");

      if ( theMode == "accept" )
        {
        createOneNewHaf( thisUserObj, theInvitorPersonObject );
        createOneNewHaf( theInvitorPersonObject, thisUserObj );
        }

      // in both cases "accept" or "ignore", delete the invite in question:
      // and on top of that you have to do it both ways

      deleteFromInvites( theInvitorPersonObject, thisUserObj );
      deleteFromInvites( thisUserObj, theInvitorPersonObject );

      // (those further functions exist in the cloud code)

      // for now we'll just go with the trick of LETTING THOSE RUN
      // so DO NOT this ........... response.success( "removal attempt underway" );
      // it's a huge problem with Parse that (so far, 2014) is poorly handled:
      // READ THIS:
      // parse.com/questions/can-i-use-a-cloud-code-function-within-another-cloud-code-function
      },
    error: function(object,error)
      {
      console.log("clientRequestAcceptInvite ... internal unusual failure: " + error.code + " " + error.message);
      response.error("Problem, internal problem?");
      return;
      }
    }
  );

}
);

(Fuller examples ... http://stackoverflow.com/a/24010828/294884 )

3) it's trivial to make Push happen from the cloud code in Parse, again this is why "everyone uses it"

For example, here's a Parse cloud code fragment that relates to Push ...

function runAPush( ownerQueryForPush, description )
// literally run a push, given an ownerQuery
// (could be 1 to millions of devices pushed to)
    {
    var pushQuery = new Parse.Query(Parse.Installation);
    pushQuery.matchesQuery('owner', ownerQueryForPush);
    Parse.Push.send
        (
        {
        where: pushQuery,
        data:
            {
            swmsg: "reload",
            alert: description,
            badge: "Increment",
            title: "YourClient"
            }
        },
        {
        success: function()
{ console.log("did send push w txt message, to all..."); },
        error: function(error)
{ console.log("problem! sending the push"); }
        }
        );
    }

4) it's unbelievably easy to do everything relating to your food database, on a nosql environment. nothing could be easier than the Parse approach

5) you get the whole back end for free (for adding foods, whatever) - normally months of work

6) finally i guess Parse is quite free (until you have so many users you'd be making a fortune anyway)

So, I can't imagine doing what you say any other way - it would be a nightmare otherwise. Hope it helps