Seamless Parse, Node, and iOS integration

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')
, app = express()
, MemStore = express.session.MemoryStore
, Parse = require('node-parse-api').Parse
, fs = require('fs')
, 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"/>
<input type="hidden" name="redir" value="{{redir}}"/><br/> 
<form name="input" action="/users"/>
username:<input type="text" name="username" id="username"/>
password:<input type="password" name="password" id="password"/>
<input type="submit" class="btn-large"/> <br/><br/>
</body>


  <h2>Sign Up<h2/>

<form name="input" action="/users" method="POST"/>
username:<input type="text" name="username" id="username"/>
email:<input type="text" name="email" id="email"/>
password:<input type="password" name="password" id="password"/>
confirm password:<input type="password" name="password_confirm" id="password_confirm"/>
<input type="submit" class="btn-large"/> <br/><br/>
</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})
   }  
  })
});
Take a walk with me 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.
  • Fvck, an 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");
 }
});
Take my hand again.
  • 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. Come with me if you want to live.
  • - 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–2015 Mike Leveton