Parse, Node, and iOS

Parse is an SaaS for developers with an intuitive API and clearly-written tutorials. Time-starved start-ups and indie devs looking for a web-to-mobile solution should consider it.

In this two-part tutorial, I'll demo the use of the Node-Parse Library with Objective-C for both persisting a User object, and uploading/editing a File from your web application to your Cocoa app.
You'll be using the Express 3 web framework and Handlebars for templating. This post assumes a working knowledge of both Node.js and Objective-C.
Before beginning make sure you've signed up and created an app at Parse. You can clone the demos created in this tutorial here:
The WebService
Create a new Node.js project topped with these dependencies in your server file. Specify the Handlebars library(hbs):

//declare and load dependancies
var express = require('express')

...

, hbs = require('hbs');
 
//load Parse application, master, and rest api keys
var APP_ID = "<your Parse App-ID>"
, MASTER_KEY = "<your Parse Master Key>"
, parse = new Parse(APP_ID, MASTER_KEY);


{
  "name": "<your app name>"
  , "version": "0.0.1"
  , "dependencies": {
     "express": "",
     "hbs": "",
     "node-parse-api": ""
  }
}

Make sure to run npm install at the root of your project! In layout.hbs, declare the body buffer:

	{{{body}}}
Create two hbs views to go along with your layout - a file signin/signup view and a file-upload view:

  <h2>Sign In<h2/>

<form name="input" action="/sessions" method="POST"/>
...
</body>

  <h2>Sign Up<h2/>

<form name="input" action="/users" method="POST"/>
...
</body>
The index view will also serve as your upload interface:

<form name="input" action="/files" method="POST"/>
<h2>Create A Document<h2/>
<textarea name="specialRequest" rows="20"/></textarea/>
<br/><br/>
<input type="submit" value="submit"/>
<input type="reset"/>
</form>
Back in server.js, require a login before showing the homepage:

function requiresLogin(req, res, next){
  if(req.session.user){
    next();
  }else{
    res.redirect('/signin');
 }
};
Next in server.js, use GET requests for signin/signup and index:

app.get('/', requiresLogin, function(req, res){
  var username = req.session.user.username;
  res.render("index", {name: username} );
});
 
app.get('/signin', function(req, res){
  res.render('signin', {redir: req.query.redir});
});
Followed by a large POST request to handle User sessions:

app.post('/sessions', function(req, res){
  parse.getUser(req.body.username, req.body.password, function(err, resp){
    if (resp){
      req.session.user = resp;
      res.render('index');
  } else {
    res.render('signin', {redir:req.body.redir})
   }  
  })
});
Let's walk through this function:
  • Use Express' body parser req.body to get the submitted username and password and send a request to Parse for User object match.
  • If you get any response, assign it to Express' session helper req.session and render the index view.
  • Error, send the user back to the signin view.
We now have a larger, uglier POST request to handle the creation of a User:

app.post('/users', function(req, res){
  username = req.body.username;
  password = req.body.password;
  password_confirm = req.body.password_confirm;
  email = req.body.email;
  if (password === password_confirm){
    parse.insert('_User', {"username": username, "password": password, "email": email}, function (err, resp) {
      if (resp){
        parse.getUser(username, password, function(errr, user){
          if (user){
            req.session.user = user;
            res.render("index");
           } else {
            console.log('errr' + " " + errr)
            res.render('signin');
          }  
        })
      } else {
        console.log('err' + " " + err);
        res.render('signin');
      }
  });
 } else {
  res.render("signin");
 }
});
Let's go -
  • Pull the username, password, password confirmation, and email from the Express body parser.
  • If the passwords match, post these values to the Parse data browser parse.insert. The underscore before 'User' indicates a special built-in Parse user class.
  • errr, send the user back to the 'signin' view.
  • Then in the callback function function (err, resp) request the newly created User back from the db.
  • If no response, or the passwords don't match, redirect the User object back to the 'signin' view res.render("signin");.
Aside from causing vertigo, this POST request handles file uploads:

app.post('/files', function(req, res){
  data = req.body.specialRequest;
  if (data.length <= 405831){
    var time = new Date().getTime();
    filename = req.session.user.objectId + '_' + time;
    user_id = req.session.user.objectId;
    user_name = req.session.user.username;
    parse.getFileByUser(user_id, 'Terms', function (err, respo){
      if (respo.results.length === 0){
        parse.insertFile(filename, data, 'txt', function(errr, resp){
          fileLink = resp.url;
          parse_name = resp.name;
            parse.insert('Terms', {"user": req.session.user.objectId, "link": fileLink, "orig_name": filename, "parse_name": parse_name}, function (errrr, respon) {
                  res.render("index", {name: user_name} );
        });
      });
      } else {
        object_id = respo.results[0].objectId
        parse.delete('Terms', object_id, function(err, resp){
        parse.insertFile(filename, data, 'txt', function(errr, respo){
          fileLink = respo.url;
          parse_name = respo.name;
            parse.insert('Terms', {"user": req.session.user.objectId, "link": fileLink, "orig_name": filename, "parse_name": parse_name}, function (errrr, respon){
                  res.render("index", {name: user_name} );
           });
        });
      })
      }
   });
  }else{
    res.render('index', {redir:req.body.redir});
  }
}); 
We've entered callback hell. No one thinks of how much blood it costs.
  • - Assign the textfield data and ensure that it has fewer than 400,000 rows as Parse only allows 10MB per upload (data.length <= 405831).
  • - Create a current timestamp and attach it to the User id which is automatically assigned by Parse.
  • - Query Parse to see if this User already has uploaded a File.
  • - If the user has no files, insert the file into the Parse db with insertFile().
  • - In the ensuing callback function, create and insert the metadata for the file with insert().
  • - Finally, render the index view file res.render('index'....
  • - If the user had already pushed a file to Parse, then require two extra steps.
  • - Take the first element of the respo.results array and grab its object id.
  • - You must call delete() to remove the existing entry before calling insertFile() and insert() to push the object and create it in the db respectively.
Now run 'node server.js' in the terminal and go to localhost:4000. After signing up and then submitting a file, go to your data browser at parse.com and ensure that the record persisted.
In part two, you'll create an iOS app and use the data we created in this tutorial to sign in and view the file.
Copyright © 2011–2018 Mike Leveton