Wednesday, November 20, 2013

Storing Centralized Mobile Data with Parse and Kinvey

Storing centralized data is important to mobile apps and simple websites alike but many developers don't have the resources to set up their own middle and back end tiers. Independent mobile developers especially can benefit from cheap and simple storage options that they don't need to manage themselves. I've recently learned about several back end as a service providers that allow developers to skip maintaining their own middle tier and databases and store data straight in a third party through API calls. While some might balk at giving someone else full control over storage, the services can allow for great cost savings over setting everything up yourself. Additionally, most providers support both mobile apps and websites coded in different languages. The usage, pricing and features of the providers varies certainly vary so its helpful to compare to see what might be a good fit. I myself have been seeking a good storage option for some of my Android mobile efforts so I thought I'd take a look at a couple of the options available today.

Parse

Parse is a backend data service that has SDKs for mobile devices and web sites alike. It has a free tier for up to a million data requests or pushes a month and a $199 dollar a month paid tier in addition to an Enterprise tier. It is pretty easy to get up and running.

The first step is to get set up on Parse's website with an account and download the SDK. You can register a project and get your application id and client key that you will need in the code. Once your all set up you are ready to code. You start by initializing Parse with the app id and key:

Parse.initialize(this, "myappid","mykey");
Once initialized you can start accessing data. We'll start by creating a simple user account and signing in:
ParseUser user = new ParseUser();
user.setUsername("Tim");
user.setPassword("Tester");
user.setEmail("tim@test.com");

user.signUpInBackground(new SignUpCallback() {
    @Override
    public void done(ParseException arg0) {
        if (arg0 == null) {
            //sign up was successful
        }
    }
});

Parse methods have synchronous and asynchronous methods so you can also call the standard signUp method if you don't care for multi-threading. Once you have user accounts created you can let users log in. Once again this has synchronous and asynchronous versions:

ParseUser.logInInBackground("Tim", "Tester", new LogInCallback() {
    public void done(ParseUser user, ParseException e) {
        if (user != null) {
            //do work here
        } else {
            //log in failed          }
        }
    }
});   

If you aren't storing the current user and need to get it at a later point you can call getCurrentUser. You can also log them out the current user without having a pointer to them with the logout function:

ParseUser user = ParseUser.getCurrentUser();
ParseUser.logout();

Storing Objects

Next we will look at how data can be stored and retrieved. This can be done with or without user accounts. Objects are created using ParseObject and giving a name for the specific object you want to create. You can then add as many fields to it as you like. The following demonstrates saving user reviews and attaching them to the logged in user:

ParseUser user = ParseUser.getCurrentUser();
ParseObject review1 = new ParseObject("Review");
review1.put("title", "Sam's Restaurant");
review1.put("content", "It was great.");
review1.put("user", user); //this association is optional
review1.save();

ParseObject review2 = new ParseObject("Review");
review2.put("title", "Mary's Diner");
review2.put("content", "Service was slow.");
review2.put("user", user); //this association is optional
review2.save();

We now have two objects called Review associated with the current user. The association allows us to easily retrieve the specific user's records:

ParseQuery query = ParseQuery.getQuery("Review");
query.whereEqualTo("user", user);
List reviews = query.find();
for (ParseObject review : reviews) {
    String title = review.get("title");
    ...
}

If we wanted to further narrow down results from the query we could add additional filters. Retrieved objects can of course be updated by calling save or saveInBackground:

review.put("content", "Not so good."); //update content
review.saveInBackground();

Finally you can delete an object by using delete or deleteInBackground:

review.deleteInBackground();

User Security

Another feature of Parse is the ability to set the security on objects. For example, if we only want to allow the logged in user read and write access to a specific review, we can do this:

ParseUser user = ParseUser.getCurrentUser();
ParseACL acl = new ParseACL();
acl.setReadAccess(user, true);
acl.setWriteAccess(user, true);
review.setACL(acl); //apply the control list to the review
review.save();

You can add as many users to the read and write permissions on an ACL as you like. You can also call setPublicReadAccess(true) or setPublicWriteAccess(true) if you want to allow all users to have one of those permissions.

Browsing Data

A nice feature of Parse is an online data browser allowing you to track your access and your data. You can access it by logging on to the parse home page and choosing Access Dashboard. Once inside you can view users and objects, ACLs, notifications and information on how often data is accessed.

There is lots more to try of course but this is just to demonstrate the basics. Their useful android guide can be found here.

Kinvey

Kinvey offers a lot of similar features to parse but has a more generous pricing structure centered around the number of users. Like Parse it is a great starting point for simple projects. The first step is to register on the Kinvey site and download the SDK. Like Parse, Kinvey supports many different clients such as BackBone, HTML5 and Node.js. Once you have the SDK and your app key and secret, you can create your client object:

String appKey = "xxxx";
String appSecret = "xxxxx";
final Client kinveyClient = new Client.Builder(appKey, appSecret, this.getApplicationContext()).build();

After creating our client, we need to call logout to make sure no default user is logged in:

kinveyClient.user().logout().execute();

Now we can create user's similar to Parse. This will also log the user in which is why its important to call logout first:

kinveyClient.user().create("Tim", "Tester", new KinveyUserCallback() {
    public void onFailure(Throwable t) {
        //log error
    }
    public void onSuccess(User u) {
        //user code here            
    }
});

To log the user in in the future we can call the login function:

kinveyClient.user().login("Tim", "Tester", new KinveyUserCallback() {
    public void onFailure(Throwable t) {
        //log error
    }
    public void onSuccess(User u) {
        //user code here             
    }
}); 

Storing Data

Data is stored in Kinvey by creating objects that extend from GenericJson. You list your fields as private and then can access them like dictionary objects using get and put methods. You can optionally use the Key annotation to specify the exact name of the field you want it to be stored in:

public class ReviewEntity extends GenericJson {
    @Key("Title")
    private String title;
    private String content;
}

To create and save the object, we can do the following:

ReviewEntity review1 = new ReviewEntity();
review1.put("title", "Corner Bistro");
review1.put("content", "Interior was dirty.");
AsyncAppData reviews = kinveyClient.appData("reviewCollection",ReviewEntity.class);
  
reviews.save(review1, new KinveyClientCallback() {
    @Override
    public void onFailure(Throwable e) {
        //log error here
    }

    @Override
    public void onSuccess(ReviewEntity r) {
    }
});

Once an object is saved, a string id is automatically created and can be fetched with get("_id"). You can also specify your own property to map to it by using the @Key("_id") annotation when defining your entity class. This property is helpful if you need to retrieve an object based on its id.

Querying

You can easily fetch data from a collection by using the entities id string in the getEntity call or by creating a Query and calling get. The following shows a simple query to get back a review:

Query query = kinveyClient.query();
query.equals("title","Corner Bistro");
reviews.get(query, new KinveyListCallback() {
    @Override
    public void onSuccess(ReviewEntity result[]) {
        //use restaurant here
    }
    @Override
    public void onFailure(Throwable error) { 
       //handle error here
    }
});

You can also skip the query parameter and get all the records back. To retrieve a specific object, you can call getEntity and specify the objects string id.

User Security

Similar to Parse you can also set up access control lists to allow read or write access to data. If we want to apply read permissions on our review entity, we can modify our class like this:

public class ReviewEntity extends GenericJson {

    private String title;
    private String content;

    @Key("_acl")
    private KinveyMetaData.AccessControlList acl;
 
    public KinveyMetaData.AccessControlList getACL() {
        return acl;
    }
 
    public ReviewEntity() {
        acl = new KinveyMetaData.AccessControlList();
    }
}

Now when creating a review, we can specify that only our current user is to be able to read it. After logging in, user holds Tim:

ReviewEntity review1 = new ReviewEntity();
review1.put("title", "Secured Review");
review1.put("content", "Only viewable by Tim");
ArrayList users = new ArrayList();
users.add(user.getId());
review1.getACL().setGloballyReadable(false);
review1.getACL().setRead(users);

Now we will be able to query this object like we did above. If we log in with someone else and search for that title, we will get an empty list. Kinvey supports a lot more as you can imagine. For more useful things to do you can refer to this site.

Conclusion

So this was a basic look at Kinvey and Parse. Both are great for providing data backends mobile apps that do not have the infrastructure or ability to manage their own database. There are several more out there such as StackMob or StorageRoom and which is best for you may likely come down to pricing and usage terms.

1 comment:

  1. hi..how can I store multiple values in single queries ..i.e if I want to store five questions in question column..regards

    ReplyDelete