You are on page 1of 211

Android

Guide

Browse

Android Guide Browse all platforms Browse

Getting Started
If you haven't installed the SDK yet, please head over to
the QuickStart guide to get our SDK up and running in
Android Studio. Note that we support Android 2.3 and
higher. You can also check out our API Reference for
more detailed information about our SDK.
The Parse platform provides a complete backend
solution for your mobile application. Our goal is to
totally eliminate the need for writing server code or
maintaining servers. If you're familiar with web
frameworks like Ruby on Rails, we've taken many of the
same principles and applied them to our platform. In
particular, our SDK is ready to use out of the box with
minimal conguration on your part.
On Parse, you create an App for each of your mobile
applications. Each App has its own application id and
client key that you apply to your SDK install. Your
account on Parse can accommodate multiple Apps. This
is useful even if you have one application, since you can
deploy dierent versions for test and production.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Objects
The ParseObject
Storing data on Parse is built around the ParseObject .
Each ParseObject contains key-value pairs of JSONcompatible data. This data is schemaless, which means
that you don't need to specify ahead of time what keys
exist on each ParseObject . You simply set whatever
key-value pairs you want, and our backend will store it.
For example, let's say you're tracking high scores for a
game. A single ParseObject could contain:

score: 1337, playerName: "Sean Plott",


cheatMode: false

Keys must be alphanumeric strings. Values can be


strings, numbers, booleans, or even arrays and objects anything that can be JSON-encoded.
Each ParseObject has a class name that you can use to
distinguish dierent sorts of data. For example, we
could call the high score object a GameScore . We
recommend that you NameYourClassesLikeThis and
nameYourKeysLikeThis, just to keep your code looking
pretty.

Saving Objects
Let's say you want to save the GameScore described
above to the Parse Cloud. The interface is similar to a
Map , plus the saveInBackground method:

ParseObject gameScore = new


ParseObject("GameScore");
gameScore.put("score", 1337);
gameScore.put("playerName", "Sean Plott");
gameScore.put("cheatMode", false);
gameScore.saveInBackground();

After this code runs, you will probably be wondering if


anything really happened. To make sure the data was
saved, you can look at the Data Browser in your app on
Parse. You should see something like this:

objectId: "xWMyZ4YEGZ", score: 1337,


playerName: "Sean Plott", cheatMode: false,
createdAt:"2011-06-10T18:33:42Z",
updatedAt:"2011-06-10T18:33:42Z"

There are two things to note here. You didn't have to


congure or set up a new Class called GameScore
before running this code. Your Parse app lazily creates
this Class for you when it rst encounters it.
There are also a few elds you don't need to specify
that are provided as a convenience. objectId is a
unique identier for each saved object. createdAt and
updatedAt represent the time that each object was
created and last modied in the cloud. Each of these
elds is lled in by Parse, so they don't exist on a
ParseObject until a save operation has completed.

Retrieving Objects
Saving data to the cloud is fun, but it's even more fun to
get that data out again. If you have the objectId , you
can retrieve the whole ParseObject using a

ParseQuery :

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
query.getInBackground("xWMyZ4YEGZ", new
GetCallback<ParseObject>() {
public void done(ParseObject object,
ParseException e) {
if (e == null) {
// object will be your game score
} else {
// something went wrong
}
}
});

To get the values out of the ParseObject , there's a


getX method for each data type:

int score = gameScore.getInt("score");


String playerName =
gameScore.getString("playerName");
boolean cheatMode =
gameScore.getBoolean("cheatMode");

If you don't know what type of data you're getting out,


you can call get(key) , but then you probably have to
cast it right away anyways. In most situations you
should use the typed accessors like getString .
The three special values have their own accessors:

String objectId = gameScore.getObjectId();


Date updatedAt = gameScore.getUpdatedAt();
Date createdAt = gameScore.getCreatedAt();

If you need to refresh an object you already have with


the latest data that is in the cloud, you can call the
fetchInBackground method like so:

myObject.fetchInBackground(new
GetCallback<ParseObject>() {
public void done(ParseObject object,
ParseException e) {
if (e == null) {
// Success!
} else {
// Failure!
}
}
});

The code in the GetCallback will be run on the main


thread.

The Local Datastore


Parse also lets you store objects in a local datastore on
the Android device itself. You can use this for data that
doesn't need to be saved to the cloud, but this is
especially useful for temporarily storing data so that it
can be synced later. To enable the datastore, call
Parse.enableLocalDatastore() in your Application
constructor before calling Parse.initialize() . Once
the local datastore is enabled, you can store an object
by pinning it.

ParseObject gameScore = new


ParseObject("GameScore");
gameScore.put("score", 1337);

gameScore.put("playerName", "Sean Plott");


gameScore.put("cheatMode", false);
gameScore.pinInBackground();

As with saving, this recursively stores every object and


le that gameScore points to, if it has been fetched
from the cloud. Whenever you save changes to the
object, or fetch new changes from Parse, the copy in
the datastore will be automatically updated, so you
don't have to worry about it.

Retrieving Objects from the Local


Datastore
Storing an object is only useful if you can get it back
out. To get the data for a specic object, you can use a
ParseQuery just like you would while on the network,
but using the fromLocalDatastore method to tell it
where to get the data.

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
query.fromLocalDatastore();
query.getInBackground("xWMyZ4YEGZ", new
GetCallback<ParseObject>() {
public void done(ParseObject object,
ParseException e) {
if (e == null) {
// object will be your game score
} else {
// something went wrong
}
}
});

If you already have an instance of the object, you can

instead use the


fetchFromLocalDatastoreInBackground method.

ParseObject object =
ParseObject.createWithoutData("GameScore",
"xWMyZ4YEGZ");
object.fetchFromLocalDatastoreInBackground(new
GetCallback<ParseObject>() {
public void done(ParseObject object,
ParseException e) {
if (e == null) {
// object will be your game score
} else {
// something went wrong
}
}
});

Unpinning Objects
When you are done with the object and no longer need
to keep it on the device, you can release it with
unpinInBackground .

gameScore.unpinInBackground();

Saving Objects Offline


Most save functions execute immediately, and inform
your app when the save is complete. If you don't need
to know when the save has nished, you can use
saveEventually instead. The advantage is that if the
user currently doesn't have a network connection,
saveEventually will store the update on the device
until a network connection is re-established. If your app
is closed before the connection is back, Parse will try

again the next time the app is opened. All calls to


saveEventually (and deleteEventually ) are
executed in the order they are called, so it is safe to call
saveEventually on an object multiple times. If you
have the local datastore enabled, then any object you
saveEventually will be pinned as long as that save is in
progress. That makes it easy to retrieve your local
changes while waiting for the network to be available.

ParseObject gameScore = new


ParseObject("GameScore");
gameScore.put("score", 1337);
gameScore.put("playerName", "Sean Plott");
gameScore.put("cheatMode", false);
gameScore.saveEventually();

Updating Objects
Updating an object is simple. Just set some new data
on it and call one of the save methods. Assuming you
have saved the object and have the objectId , you can
retrieve the ParseObject using a ParseQuery and
update its data:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
// Retrieve the object by id
query.getInBackground("xWMyZ4YEGZ", new
GetCallback<ParseObject>() {
public void done(ParseObject gameScore,
ParseException e) {
if (e == null) {
// Now let's update it with some new
data. In this case, only cheatMode and score
// will get sent to the Parse Cloud.
playerName hasn't changed.

gameScore.put("score", 1338);
gameScore.put("cheatMode", true);
gameScore.saveInBackground();
}
}
});

Parse automatically gures out which data has changed


so only "dirty" elds will be transmitted during a save.
You don't need to worry about squashing data in the
cloud that you didn't intend to update.

Counters
The above example contains a common use case. The
"score" eld is a counter that we'll need to continually
update with the player's latest score. Using the above
method works but it's cumbersome and can lead to
problems if you have multiple clients trying to update
the same counter.
To help with storing counter-type data, Parse provides
methods that atomically increment (or decrement) any
number eld. So, the same update can be rewritten as:

gameScore.increment("score");
gameScore.saveInBackground();

You can also increment by any amount using


increment(key, amount) .

Arrays
To help with storing array data, there are three
operations that can be used to atomically change an
array eld:

add and addAll append the given objects to the


end of an array eld.

addUnique and addAllUnique add only the given


objects which aren't already contained in an array
eld to that eld. The position of the insert is not
guaranteed.

removeAll removes all instances of the given


objects from an array eld.

For example, we can add items to the set-like "skills"


eld like so:

gameScore.addAllUnique("skills",
Arrays.asList("flying", "kungfu"));
gameScore.saveInBackground();

Note that it is not currently possible to atomically add


and remove items from an array in the same save. You
will have to call save in between every dierent kind of
array operation.

Deleting Objects
To delete an object from the Parse Cloud:

myObject.deleteInBackground();

If you want to run a callback when the delete is


conrmed, you can provide a DeleteCallback to the
deleteInBackground method. If you want to block the
calling thread, you can use the delete method.
You can delete a single eld from an object with the
remove method:

// After this, the playerName field will be


empty
myObject.remove("playerName");
// Saves the field deletion to the Parse Cloud
myObject.saveInBackground();

Relational Data
Objects can have relationships with other objects. To
model this behavior, any ParseObject can be used as a
value in other ParseObject s. Internally, the Parse
framework will store the referred-to object in just one
place, to maintain consistency.
For example, each Comment in a blogging app might
correspond to one Post . To create a new Post with a
single Comment , you could write:

// Create the post


ParseObject myPost = new ParseObject("Post");
myPost.put("title", "I'm Hungry");
myPost.put("content", "Where should we go for
lunch?");
// Create the comment
ParseObject myComment = new
ParseObject("Comment");
myComment.put("content", "Let's do
Sushirrito.");
// Add a relation between the Post and Comment
myComment.put("parent", myPost);
// This will save both myPost and myComment
myComment.saveInBackground();

You can also link objects using just their objectId s like
so:

// Add a relation between the Post with


objectId "1zEcyElZ80" and the comment
myComment.put("parent",
ParseObject.createWithoutData("Post",
"1zEcyElZ80"));

By default, when fetching an object, related


ParseObject s are not fetched. These objects' values
cannot be retrieved until they have been fetched like so:

fetchedComment.getParseObject("post")
.fetchIfNeededInBackground(new
GetCallback<ParseObject>() {
public void done(ParseObject post,
ParseException e) {
String title =
post.getString("title");
// Do something with your new title
variable
}
});

You can also model a many-to-many relation using the


ParseRelation object. This works similar to
List<ParseObject> , except that you don't need to
download all the ParseObject s in a relation at once.
This allows ParseRelation to scale to many more
objects than the List<ParseObject> approach. For
example, a User may have many Post s that they might
like. In this case, you can store the set of Post s that a
User likes using getRelation . In order to add a post to
the list, the code would look something like:

ParseUser user = ParseUser.getCurrentUser();


ParseRelation<ParseObject> relation =
user.getRelation("likes");
relation.add(post);
user.saveInBackground();

You can remove a post from the ParseRelation with


something like:

relation.remove(post);

By default, the list of objects in this relation are not


downloaded. You can get the list of Post s by calling
findInBackground on the ParseQuery returned by
getQuery . The code would look like:

relation.getQuery().findInBackground(new
FindCallback<ParseObject>() {
void done(List<ParseObject> results,
ParseException e) {
if (e != null) {
// There was an error
} else {
// results have all the Posts the
current user liked.
}
}
});

If you want only a subset of the Post s you can add


extra constraints to the ParseQuery returned by
getQuery . The code would look something like:

ParseQuery<ParseObject> query =
relation.getQuery();
// Add other query constraints.

For more details on ParseQuery , please look at the


query portion of this guide. A ParseRelation behaves
similar to a List<ParseObject> for querying purposes,
so any queries you can do on lists of objects (other than
include ) you can do on ParseRelation .

Data Types
So far we've used values with type String , %{number} ,
bool , and ParseObject . Parse also supports float ,
java.util.Date , and JSONObject.NULL .
You can nest JSONObject and JSONArray objects to
store more structured data within a single
ParseObject . Overall, the following types are allowed
for each eld in your object:

+ String => String


+ Number => primitive numeric values such as
double s, long s, or float s

+ Bool => bool


+ Array => JSONArray
+ Object => JSONObject
+ Date => java.util.Date
+ File => ParseFile
+ Pointer => other ParseObject
+ Relation => ParseRelation
+ Null => JSONObject.NULL
Some examples:

int myNumber = 42;


String myString = "the number is " + myNumber;
Date myDate = new Date();
JSONArray myArray = new JSONArray();
myArray.put(myString);
myArray.put(myNumber);
JSONObject myObject = new JSONObject();
myObject.put("number", myNumber);
myObject.put("string", myString);
ParseObject bigObject = new
ParseObject("BigObject");
bigObject.put("myNumber", myNumber);
bigObject.put("myString", myString);
bigObject.put("myDate", myDate);
bigObject.put("myArray", myArray);
bigObject.put("myObject", myObject);
bigObject.put("myNull", JSONObject.NULL);
bigObject.saveInBackground();

We do not recommend storing large pieces of binary


data like images or documents on ParseObject .
ParseObject s should not exceed 128 kilobytes in size.
We recommend you use ParseFile s to store images,
documents, and other types of les. You can do so by
instantiating a ParseFile object and setting it on a
eld. See Files for more details.
For more information about how Parse handles data,
check out our documentation on Data.

Subclasses
Parse is designed to get you up and running as quickly
as possible. You can access all of your data using the

ParseObject class and access any eld with get() . In


mature codebases, subclasses have many advantages,
including terseness, extensibility, and support for
autocomplete. Subclassing is completely optional, but
can transform this code:

ParseObject shield = new ParseObject("Armor");


shield.put("displayName", "Wooden Shield");
shield.put("fireproof", false);
shield.put("rupees", 50);

Into this:

Armor shield = new Armor();


shield.setDisplayName("Wooden Shield");
shield.setFireproof(false);
shield.setRupees(50);

SUBCLASSING PARSEOBJECT

To create a ParseObject subclass:


1

Declare a subclass which extends ParseObject .

Add a @ParseClassName annotation. Its value


should be the string you would pass into the
ParseObject constructor, and makes all future
class name references unnecessary.

Ensure that your subclass has a public default (i.e.


zero-argument) constructor. You must not
modify any ParseObject elds in this
constructor.

Call
ParseObject.registerSubclass(YourClass.cla
ss) in your Application constructor before
calling Parse.initialize() . The following code
sucessfully implements and registers the Armor
subclass of ParseObject :

// Armor.java
import com.parse.ParseObject;
import com.parse.ParseClassName;
@ParseClassName("Armor")
public class Armor extends ParseObject {
}
// App.java
import com.parse.Parse;
import android.app.Application;
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
ParseObject.registerSubclass(Armor.class);
Parse.initialize(this,
PARSE_APPLICATION_ID, PARSE_CLIENT_KEY);
}
}

ACCESSORS, MUTATORS, AND METHODS

Adding methods to your ParseObject subclass helps


encapsulate logic about the class. You can keep all your
logic about a subject in one place rather than using
separate classes for business logic and
storage/transmission logic.
You can add accessors and mutators for the elds of
your ParseObject easily. Declare the getter and setter
for the eld as you normally would, but implement
them in terms of get() and put() . The following
example creates a displayName eld in the Armor
class:

// Armor.java
@ParseClassName("Armor")
public class Armor extends ParseObject {
public String getDisplayName() {
return getString("displayName");
}
public void setDisplayName(String value) {
put("displayName", value);
}
}

You can now access the displayName eld using


armor.getDisplayName() and assign to it using
armor.setDisplayName("Wooden Sword") . This allows
your IDE to provide autocompletion as you develop
your app and allows typos to be caught at compiletime.
Accessors and mutators of various types can be easily
dened in this manner using the various forms of
get() such as getInt() , getParseFile() , or
getMap() .
If you need more complicated logic than simple eld
access, you can declare your own methods as well:

public void takeDamage(int amount) {


// Decrease the armor's durability and
determine whether it has broken
increment("durability", -amount);
if (getDurability() < 0) {
setBroken(true);
}
}

INITIALIZING SUBCLASSES

You should create new instances of your subclasses

using the constructors you have dened. Your subclass


must dene a public default constructor that does not
modify elds of the ParseObject , which will be used
throughout the Parse SDK to create strongly-typed
instances of your subclass.
To create a reference to an existing object, use
ParseObject.createWithoutData() :

Armor armorReference =
ParseObject.createWithoutData(Armor.class,
armor.getObjectId());

QUERIES

You can get a query for objects of a particular subclass


using the static method ParseQuery.getQuery() . The
following example queries for armors that the user can
aord:

ParseQuery<Armor> query =
ParseQuery.getQuery(Armor.class);
query.whereLessThanOrEqualTo("rupees",
ParseUser.getCurrentUser().get("rupees"));
query.findInBackground(new FindCallback<Armor>
() {
@Override
public void done(List<Armor> results,
ParseException e) {
for (Armor a : results) {
// ...
}
}
});

Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Queries
We've already seen how a ParseQuery with
getInBackground can retrieve a single ParseObject
from Parse. There are many other ways to retrieve data
with ParseQuery - you can retrieve many objects at
once, put conditions on the objects you wish to
retrieve, cache queries automatically to avoid writing
that code yourself, and more.

Basic Queries
In many cases, getInBackground isn't powerful enough
to specify which objects you want to retrieve. The
ParseQuery oers dierent ways to retrieve a list of
objects rather than just a single object.
The general pattern is to create a ParseQuery , put
conditions on it, and then retrieve a List of matching
ParseObject s using the findInBackground method
with a FindCallback . For example, to retrieve scores
with a particular playerName , use the whereEqualTo
method to constrain the value for a key:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
query.whereEqualTo("playerName", "Dan
Stemkoski");
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject>
scoreList, ParseException e) {
if (e == null) {

Log.d("score", "Retrieved " +


scoreList.size() + " scores");
} else {
Log.d("score", "Error: " +
e.getMessage());
}
}
});

findInBackground works similarly to


getInBackground in that it assures the network
request is done on a background thread, and runs its
callback in the main thread.

Query Constraints
There are several ways to put constraints on the
objects found by a ParseQuery . You can lter out
objects with a particular key-value pair with
whereNotEqualTo :

query.whereNotEqualTo("playerName", "Michael
Yabuti");

You can give multiple constraints, and objects will only


be in the results if they match all of the constraints. In
other words, it's like an AND of constraints.

query.whereNotEqualTo("playerName", "Michael
Yabuti");
query.whereGreaterThan("playerAge", 18);

You can limit the number of results with setLimit . By


default, results are limited to 100, but anything from 1

to 1000 is a valid limit:

query.setLimit(10); // limit to at most 10


results

If you want exactly one result, a more convenient


alternative may be to use getFirst or
getFirstBackground instead of using find .

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
query.whereEqualTo("playerEmail",
"dstemkoski@example.com");
query.getFirstInBackground(new
GetCallback<ParseObject>() {
public void done(ParseObject object,
ParseException e) {
if (object == null) {
Log.d("score", "The getFirst request
failed.");
} else {
Log.d("score", "Retrieved the object.");
}
}
});

You can skip the rst results with setSkip . This can be
useful for pagination:

query.setSkip(10); // skip the first 10


results

For sortable types like numbers and strings, you can


control the order in which results are returned:

// Sorts the results in ascending order by the


score field
query.orderByAscending("score");
// Sorts the results in descending order by
the score field
query.orderByDescending("score");

You can add more sort keys to the query as follows:

// Sorts the results in ascending order by the


score field if the previous sort keys are
equal.
query.addAscendingOrder("score");
// Sorts the results in descending order by
the score field if the previous sort keys are
equal.
query.addDescendingOrder("score");

For sortable types, you can also use comparisons in


queries:

// Restricts to wins < 50


query.whereLessThan("wins", 50);
// Restricts to wins <= 50
query.whereLessThanOrEqualTo("wins", 50);
// Restricts to wins > 50
query.whereGreaterThan("wins", 50);
// Restricts to wins >= 50

query.whereGreaterThanOrEqualTo("wins", 50);

If you want to retrieve objects matching several


dierent values, you can use whereContainedIn ,
providing a collection of acceptable values. This is often
useful to replace multiple queries with a single query.
For example, if you want to retrieve scores made by any
player in a particular list:

String[] names = {"Jonathan Walsh", "Dario


Wunsch", "Shawn Simon"};
query.whereContainedIn("playerName",
Arrays.asList(names));

If you want to retrieve objects that do not match any of


several values you can use whereNotContainedIn ,
providing an array of acceptable values. For example, if
you want to retrieve scores from players besides those
in a list:

String[] names = {"Jonathan Walsh", "Dario


Wunsch", "Shawn Simon"};
query.whereNotContainedIn("playerName",
Arrays.asList(names));

If you want to retrieve objects that have a particular key


set, you can use whereExists . Conversely, if you want
to retrieve objects without a particular key set, you can
use whereDoesNotExist .

// Finds objects that have the score set


query.whereExists("score");
// Finds objects that don't have the score set

query.whereDoesNotExist("score");

You can use the whereMatchesKeyInQuery method to


get objects where a key matches the value of a key in a
set of objects resulting from another query. For
example, if you have a class containing sports teams
and you store a user's hometown in the user class, you
can issue one query to nd the list of users whose
hometown teams have winning records. The query
would look like:

ParseQuery<ParseObject> teamQuery =
ParseQuery.getQuery("Team");
teamQuery.whereGreaterThan("winPct", 0.5);
ParseQuery<ParseUser> userQuery =
ParseUser.getQuery();
userQuery.whereMatchesKeyInQuery("hometown",
"city", teamQuery);
userQuery.findInBackground(new
FindCallback<ParseUser>() {
void done(List<ParseUser> results,
ParseException e) {
// results has the list of users with a
hometown team with a winning record
}
});

Conversely, to get objects where a key does not match


the value of a key in a set of objects resulting from
another query, use whereDoesNotMatchKeyInQuery . For
example, to nd users whose hometown teams have
losing records:

ParseQuery<ParseUser> losingUserQuery =
ParseUser.getQuery();

losingUserQuery.whereDoesNotMatchKeyInQuery("hometown"
"city", teamQuery);
losingUserQuery.findInBackground(new
FindCallback<ParseUser>() {
void done(List<ParseUser> results,
ParseException e) {
// results has the list of users with a
hometown team with a losing record
}
});

You can restrict the elds returned by calling


selectKeys with a collection of keys. To retrieve
documents that contain only the score and
playerName elds (and also special built-in elds such
as objectId , createdAt , and updatedAt ):

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
query.selectKeys(Arrays.asList("playerName",
"score"));;
List<ParseObject> results = query.find();

The remaining elds can be fetched later by calling one


of the fetchIfNeeded variants on the returned objects:

ParseObject object = results.get(0);


object.fetchIfNeededInBackground(new
GetCallback<ParseObject>() {
public void done(ParseObject object,
ParseException e) {
// all fields of the object will now be
available here.
}
});

Queries on Array Values


If a key contains an array value, you can search for
objects where the key's array value contains 2 by:

// Find objects where the array in arrayKey


contains the number 2.
query.whereEqualTo("arrayKey", 2);

You can also search for objects where the key's array
value contains each of the values 2, 3, and 4 with the
following:

// Find objects where the array in arrayKey


contains all of the numbers 2, 3, and 4.
ArrayList<Integer> numbers = new
ArrayList<Integer>();
numbers.add(2);
numbers.add(3);
numbers.add(4);
query.whereContainsAll("arrayKey", numbers);

Queries on String Values

If you're trying to implement a generic search feature, we


recommend taking a look at this blog post:
Implementing Scalable Search on a NoSQL Backend.

Use whereStartsWith to restrict to string values that


start with a particular string. Similar to a MySQL LIKE
operator, this is indexed so it is ecient for large
datasets:

// Finds barbecue sauces that start with 'Big


Daddy's'.
ParseQuery<ParseObject> query =
ParseQuery.getQuery("BarbecueSauce");
query.whereStartsWith("name", "Big Daddy's");

The above example will match any BarbecueSauce


objects where the value in the "name" String key starts
with "Big Daddy's". For example, both "Big Daddy's" and
"Big Daddy's BBQ" will match, but "big daddy's" or
"BBQ Sauce: Big Daddy's" will not.
Queries that have regular expression constraints are
very expensive. Refer to the Performance Guide for
more details.

Relational Queries
There are several ways to issue queries for relational
data. If you want to retrieve objects where a eld
matches a particular ParseObject , you can use
whereEqualTo just like for other data types. For
example, if each Comment has a Post object in its post
eld, you can fetch comments for a particular Post :

// Assume ParseObject myPost was previously


created.
ParseQuery<ParseObject> query =
ParseQuery.getQuery("Comment");
query.whereEqualTo("post", myPost);
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject>
commentList, ParseException e) {
// commentList now has the comments for

myPost
}
});

If you want to retrieve objects where a eld contains a


ParseObject that matches a dierent query, you can
use whereMatchesQuery . Note that the default limit of
100 and maximum limit of 1000 apply to the inner
query as well, so with large data sets you may need to
construct queries carefully to get the desired behavior.
In order to nd comments for posts containing images,
you can do:

ParseQuery<ParseObject> innerQuery =
ParseQuery.getQuery("Post");
innerQuery.whereExists("image");
ParseQuery<ParseObject> query =
ParseQuery.getQuery("Comment");
query.whereMatchesQuery("post", innerQuery);
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject>
commentList, ParseException e) {
// comments now contains the comments for
posts with images.
}
});

If you want to retrieve objects where a eld contains a


ParseObject that does not match a dierent query,
you can use whereDoesNotMatchQuery . In order to nd
comments for posts without images, you can do:

ParseQuery<ParseObject> innerQuery =
ParseQuery.getQuery("Post");

innerQuery.whereExists("image");
ParseQuery<ParseObject> query =
ParseQuery.getQuery("Comment");
query.whereDoesNotMatchQuery("post",
innerQuery);
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject>
commentList, ParseException e) {
// comments now contains the comments for
posts without images.
}
});

In some situations, you want to return multiple types of


related objects in one query. You can do this with the
include method. For example, let's say you are
retrieving the last ten comments, and you want to
retrieve their related posts at the same time:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("Comment");
// Retrieve the most recent ones
query.orderByDescending("createdAt");
// Only retrieve the last ten
query.setLimit(10);
// Include the post data with each comment
query.include("post");
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject>
commentList, ParseException e) {
// commentList now contains the last ten

comments, and the "post"


// field has been populated. For example:
for (ParseObject comment : commentList) {
// This does not require a network
access.
ParseObject post =
comment.getParseObject("post");
Log.d("post", "retrieved a related
post");
}
}
});

You can also do multi level includes using dot notation.


If you wanted to include the post for a comment and
the post's author as well you can do:

query.include("post.author");

You can issue a query with multiple elds included by


calling include multiple times. This functionality also
works with ParseQuery helpers like getFirst() and
getInBackground() .

Querying the Local Datastore


If you have enabled the local datastore by calling
Parse.enableLocalDatastore() before your call to
Parse.initialize() , then you can also query against
the objects stored locally on the device. To do this, call
the fromLocalDatastore method on the query.

query.fromLocalDatastore();
query.findInBackground(new
FindCallback<ParseObject>() {

public void done(final List<ParseObject>


scoreList, ParseException e) {
if (e == null) {
// Results were successfully found from
the local datastore.
} else {
// There was an error.
}
}
});

You can query from the local datastore using exactly


the same kinds of queries you use over the network.
The results will include every object that matches the
query that's been pinned to your device. The query
even takes into account any changes you've made to
the object that haven't yet been saved to the cloud. For
example, if you call deleteEventually , on an object, it
will no longer be returned from these queries.

Caching Queries
It's often useful to cache the result of a query on a
device. This lets you show data when the user's device
is oine, or when the app has just started and network
requests have not yet had time to complete. The
easiest way to do this is with the local datastore. When
you pin objects, you can attach a label to the pin, which
lets you manage a group of objects together. For
example, to cache the results of the query above, you
can call pinAllInBackground and give it a label.

final String TOP_SCORES_LABEL = "topScores";


// Query for the latest objects from Parse.
query.findInBackground(new
FindCallback<ParseObject>() {

public void done(final List<ParseObject>


scoreList, ParseException e) {
if (e != null) {
// There was an error or the network
wasn't available.
return;
}
// Release any objects previously pinned
for this query.
ParseObject.unpinAllInBackground(TOP_SCORES_LABEL,
scoreList, new DeleteCallback() {
public void done(ParseException e) {
if (e != null) {
// There was some error.
return;
}
// Add the latest results for this
query to the cache.
ParseObject.pinAllInBackground(TOP_SCORES_LABEL,
scoreList);
}
});
}
});

Now when you do any query with


fromLocalDatastore , these objects will be included in
the results if they still match the query.
If you aren't using the local datastore, you can use the
per-query cache for ParseQuery instead. The default
query behavior doesn't use the cache, but you can
enable caching with setCachePolicy . For example, to
try the network and then fall back to cached data if the
network is not available:

query.setCachePolicy(ParseQuery.CachePolicy.NETWORK_ELSE_CACHE);
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject>
scoreList, ParseException e) {
if (e == null) {
// Results were successfully found,
looking first on the
// network and then on disk.
} else {
// The network was inaccessible and we
have no cached data
// for this query.
}
}
});

Parse provides several dierent cache policies:

IGNORE_CACHE : The query does not load from the


cache or save results to the cache. IGNORE_CACHE
is the default cache policy.

CACHE_ONLY : The query only loads from the


cache, ignoring the network. If there are no
cached results, that causes a ParseException .

NETWORK_ONLY : The query does not load from the


cache, but it will save results to the cache.

CACHE_ELSE_NETWORK : The query rst tries to


load from the cache, but if that fails, it loads
results from the network. If neither cache nor
network succeed, there is a ParseException .

NETWORK_ELSE_CACHE : The query rst tries to


load from the network, but if that fails, it loads
results from the cache. If neither network nor
cache succeed, there is a ParseException .

CACHE_THEN_NETWORK : The query rst loads from


the cache, then loads from the network. In this

case, the FindCallback will actually be called


twice - rst with the cached results, then with the
network results. This cache policy can only be
used asynchronously with findInBackground .
If you need to control the cache's behavior, you can use
methods provided in ParseQuery to interact with the
cache. You can do the following operations on the
cache:

+ Check to see if there is a cached result for the


query with:

boolean isInCache =
query.hasCachedResult();

+ Remove any cached results for a query with:

query.clearCachedResult();

+ Remove cached results for all queries with:

ParseQuery.clearAllCachedResults();

Query caching also works with ParseQuery helpers


including getFirst() and getInBackground() .

Counting Objects
Caveat: Count queries are rate limited to a maximum of
160 requests per minute. They can also return
inaccurate results for classes with more than 1,000
objects. Thus, it is preferable to architect your

application to avoid this sort of count operation (by


using counters, for example.)
If you just need to count how many objects match a
query, but you do not need to retrieve all the objects
that match, you can use count instead of find . For
example, to count how many games have been played
by a particular player:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
query.whereEqualTo("playerName", "Sean
Plott");
query.countInBackground(new CountCallback() {
public void done(int count, ParseException
e) {
if (e == null) {
// The count request succeeded. Log the
count
Log.d("score", "Sean has played " +
count + " games");
} else {
// The request failed
}
}
});

If you want to block the calling thread, you can also use
the synchronous query.count() method.

Compound Queries
If you want to nd objects that match one of several
queries, you can use ParseQuery.or method to
construct a query that is an or of the queries passed in.
For instance if you want to nd players who either have
a lot of wins or a few wins, you can do:

ParseQuery<ParseObject> lotsOfWins =
ParseQuery.getQuery("Player");
lotsOfWins.whereGreaterThan(150);
ParseQuery<ParseObject> fewWins =
ParseQuery.getQuery("Player");
fewWins.whereLessThan(5);
List<ParseQuery<ParseObject>> queries = new
ArrayList<ParseQuery<ParseObject>>();
queries.add(lotsOfWins);
queries.add(fewWins);
ParseQuery<ParseObject> mainQuery =
ParseQuery.or(queries);
mainQuery.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject> results,
ParseException e) {
// results has the list of players that
win a lot or haven't won much.
}
});

You can add additional constraints to the newly created


ParseQuery that act as an 'and' operator.
Note that we do not, however, support GeoPoint or
non-ltering constraints (e.g. whereNear ,
withinGeoBox , setLimit , skip , orderBy... ,
include ) in the subqueries of the compound query.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Users
At the core of many apps, there is a notion of user
accounts that lets users access their information in a
secure manner. We provide a specialized user class
called ParseUser that automatically handles much of
the functionality required for user account
management.
With this class, you'll be able to add user account
functionality in your app.
ParseUser is a subclass of the ParseObject , and has
all the same features, such as exible schema,
automatic persistence, and a key value interface. All the
methods that are on ParseObject also exist in
ParseUser . The dierence is that ParseUser has some
special additions specic to user accounts.

Properties
ParseUser has several properties that set it apart from
ParseObject :

+ username: The username for the user (required).


+ password: The password for the user (required
on signup).

+ email: The email address for the user (optional).


We'll go through each of these in detail as we run
through the various use cases for users. Keep in mind
that if you set username and email using the setters,
you do not need to set it using the put method.

Signing Up
The rst thing your app will do is probably ask the user
to sign up. The following code illustrates a typical sign

up:

ParseUser user = new ParseUser();


user.setUsername("my name");
user.setPassword("my pass");
user.setEmail("email@example.com");
// other fields can be set just like with
ParseObject
user.put("phone", "650-253-0000");
user.signUpInBackground(new SignUpCallback() {
public void done(ParseException e) {
if (e == null) {
// Hooray! Let them use the app now.
} else {
// Sign up didn't succeed. Look at the
ParseException
// to figure out what went wrong
}
}
});

This call will asynchronously create a new user in your


Parse App. Before it does this, it checks to make sure
that both the username and email are unique. Also, it
securely hashes the password in the cloud using bcrypt.
We never store passwords in plaintext, nor will we ever
transmit passwords back to the client in plaintext.
Note that we used the signUpInBackground method,
not the saveInBackground method. New ParseUser s
should always be created using the
signUpInBackground (or signUp ) method. Subsequent
updates to a user can be done by calling save .
The signUpInBackground method comes in various
avors, with the ability to pass back errors, and also

synchronous versions. As usual, we highly recommend


using the asynchronous versions when possible, so as
not to block the UI in your app. You can read more
about these specic methods in our API docs.
If a signup isn't successful, you should read the error
object that is returned. The most likely case is that the
username or email has already been taken by another
user. You should clearly communicate this to your
users, and ask them try a dierent username.
You are free to use an email address as the username.
Simply ask your users to enter their email, but ll it in
the username property ParseUser will work as
normal. We'll go over how this is handled in the reset
password section.

Logging In
Of course, after you allow users to sign up, you need be
able to let them log in to their account in the future. To
do this, you can use the class method
logInInBackground .

ParseUser.logInInBackground("Jerry",
"showmethemoney", new LogInCallback() {
public void done(ParseUser user,
ParseException e) {
if (user != null) {
// Hooray! The user is logged in.
} else {
// Signup failed. Look at the
ParseException to see what happened.
}
}
});

Verifying Emails
Enabling email verication in an application's settings
allows the application to reserve part of its experience
for users with conrmed email addresses. Email
verication adds the emailVerified key to the
ParseUser object. When a ParseUser 's email is set
or modied, emailVerified is set to false . Parse
then emails the user a link which will set
emailVerified to true .
There are three emailVerified states to consider:
1

true - the user conrmed his or her email


address by clicking on the link Parse emailed
them. ParseUsers can never have a true value
when the user account is rst created.

false - at the time the ParseUser object was


last fetched, the user had not conrmed his or
her email address. If emailVerified is false ,
consider calling fetch() on the ParseUser .

missing - the ParseUser was created when email


verication was o or the ParseUser does not
have an email .

Current User
It would be bothersome if the user had to log in every
time they open your app. You can avoid this by using
the cached currentUser object.
Whenever you use any signup or login methods, the
user is cached on disk. You can treat this cache as a
session, and automatically assume the user is logged in:

ParseUser currentUser =
ParseUser.getCurrentUser();
if (currentUser != null) {
// do stuff with the user
} else {

// show the signup or login screen


}

You can clear the current user by logging them out:

ParseUser.logOut();
ParseUser currentUser =
ParseUser.getCurrentUser(); // this will now
be null

Anonymous Users
Being able to associate data and objects with individual
users is highly valuable, but sometimes you want to be
able to do this without forcing a user to specify a
username and password.
An anonymous user is a user that can be created
without a username and password but still has all of the
same capabilities as any other ParseUser . After
logging out, an anonymous user is abandoned, and its
data is no longer accessible.
You can create an anonymous user using
ParseAnonymousUtils :

ParseAnonymousUtils.logIn(new LogInCallback()
{
@Override
public void done(ParseUser user,
ParseException e) {
if (e != null) {
Log.d("MyApp", "Anonymous login
failed.");
} else {
Log.d("MyApp", "Anonymous user logged

in.");
}
}
});

You can convert an anonymous user into a regular user


by setting the username and password, then calling
signUp() , or by logging in or linking with a service like
Facebook or Twitter. The converted user will retain all
of its data. To determine whether the current user is an
anonymous user, you can check
ParseAnonymousUtils.isLinked() :

if
(ParseAnonymousUtils.isLinked(ParseUser.getCurrentUser()))
{
enableSignUpButton();
} else {
enableLogOutButton();
}

Anonymous users can also be automatically created for


you without requiring a network request, so that you
can begin working with your user immediately when
your application starts. When you enable automatic
anonymous user creation at application startup,
ParseUser.getCurrentUser() will never be null . The
user will automatically be created in the cloud the rst
time the user or any object with a relation to the user is
saved. Until that point, the user's object ID will be null .
Enabling automatic user creation makes associating
data with your users painless. For example, in your
Application.onCreate() method, you might write:

ParseUser.enableAutomaticUser();

ParseUser.getCurrentUser().increment("RunCount");
ParseUser.getCurrentUser().saveInBackground();

Setting the Current User


If youve created your own authentication routines, or
otherwise logged in a user on the server side, you can
now pass the session token to the client and use the
become method. This method will ensure the session
token is valid before setting the current user.

ParseUser.becomeInBackground("session-tokenhere", new LogInCallback() {


public void done(ParseUser user,
ParseException e) {
if (user != null) {
// The current user is now set to user.
} else {
// The token could not be validated.
}
}
});

Security For User Objects


The ParseUser class is secured by default. Data stored
in a ParseUser can only be modied by that user. By
default, the data can still be read by any client. Thus,
some ParseUser objects are authenticated and can be
modied, whereas others are read-only.
Specically, you are not able to invoke any of the save
or delete type methods unless the ParseUser was
obtained using an authenticated method, like logIn or
signUp . This ensures that only the user can alter their
own data.

The following illustrates this security policy:

ParseUser user =
ParseUser.logIn("my_username", "my_password");
user.setUsername("my_new_username"); //
attempt to change username
user.saveInBackground(); // This succeeds,
since the user was authenticated on the device
// Get the user from a non-authenticated
manner
ParseQuery<ParseUser> query =
ParseUser.getQuery();
query.getInBackground(user.getObjectId(), new
GetCallback<ParseUser>() {
public void done(ParseUser object,
ParseException e) {
object.setUsername("another_username");
// This will throw an exception, since the
ParseUser is not authenticated
object.saveInBackground();
}
});

The ParseUser obtained from getCurrentUser() will


always be authenticated.
If you need to check if a ParseUser is authenticated,
you can invoke the isAuthenticated() method. You
do not need to check isAuthenticated() with
ParseUser objects that are obtained via an
authenticated method.

Security for Other Objects


The same security model that applies to the ParseUser

can be applied to other objects. For any object, you can


specify which users are allowed to read the object, and
which users are allowed to modify an object. To
support this type of security, each object has an access
control list, implemented by the ParseACL class.
The simplest way to use a ParseACL is to specify that
an object may only be read or written by a single user.
To create such an object, there must rst be a logged in
ParseUser . Then, new ParseACL(user) generates a
ParseACL that limits access to that user. An object's
ACL is updated when the object is saved, like any other
property. Thus, to create a private note that can only be
accessed by the current user:

ParseObject privateNote = new


ParseObject("Note");
privateNote.put("content", "This note is
private!");
privateNote.setACL(new
ParseACL(ParseUser.getCurrentUser()));
privateNote.saveInBackground();

This note will then only be accessible to the current


user, although it will be accessible to any device where
that user is signed in. This functionality is useful for
applications where you want to enable access to user
data across multiple devices, like a personal todo list.
Permissions can also be granted on a per-user basis.
You can add permissions individually to a ParseACL
using setReadAccess and setWriteAccess . For
example, let's say you have a message that will be sent
to a group of several users, where each of them have
the rights to read and delete that message:

ParseObject groupMessage = new

ParseObject("Message");
ParseACL groupACL = new ParseACL();
// userList is an Iterable<ParseUser> with the
users we are sending this message to.
for (ParseUser user : userList) {
groupACL.setReadAccess(user, true);
groupACL.setWriteAccess(user, true);
}
groupMessage.setACL(groupACL);
groupMessage.saveInBackground();

You can also grant permissions to all users at once


using setPublicReadAccess and
setPublicWriteAccess . This allows patterns like
posting comments on a message board. For example, to
create a post that can only be edited by its author, but
can be read by anyone:

ParseObject publicPost = new


ParseObject("Post");
ParseACL postACL = new
ParseACL(ParseUser.getCurrentUser());
postACL.setPublicReadAccess(true);
publicPost.setACL(postACL);
publicPost.saveInBackground();

To help ensure that your users' data is secure by


default, you can set a default ACL to be applied to all
newly-created ParseObjects :

ParseACL.setDefaultACL(defaultACL, true);

In the code above, the second parameter to


setDefaultACL tells Parse to ensure that the default
ACL assigned at the time of object creation allows read
and write access to the current user at that time.
Without this setting, you would need to reset the
defaultACL every time a user logs in or out so that the
current user would be granted access appropriately.
With this setting, you can ignore changes to the current
user until you explicitly need to grant dierent kinds of
access.
Default ACLs make it easy to create apps that follow
common access patterns. An application like Twitter,
for example, where user content is generally visible to
the world, might set a default ACL such as:

ParseACL defaultACL = new ParseACL();


defaultACL.setPublicReadAccess(true);
ParseACL.setDefaultACL(defaultACL, true);

For an application like Dropbox, where a user's data is


only accessible by the user itself unless explicit
permission is given, you would provide a default ACL
where only the current user is given access:

ParseACL.setDefaultACL(new ParseACL(), true);

An application that logs data to Parse but doesn't


provide any user access to that data would instead deny
access to the current user while providing a restrictive
ACL:

ParseACL.setDefaultACL(new ParseACL(), false);

Operations that are forbidden, such as deleting an


object that you do not have write access to, result in a
ParseException.OBJECT_NOT_FOUND error code. For
security purposes, this prevents clients from
distinguishing which object ids exist but are secured,
versus which object ids do not exist at all.

Resetting Passwords
It's a fact that as soon as you introduce passwords into
a system, users will forget them. In such cases, our
library provides a way to let them securely reset their
password.
To kick o the password reset ow, ask the user for
their email address, and call:

ParseUser.requestPasswordResetInBackground("myemail@example.com"
new RequestPasswordResetCallback() {
public void done(ParseException e) {
if (e == null) {
// An email was successfully sent with
reset instructions.
} else {
// Something went wrong. Look at the
ParseException to see what's up.
}
}
});

This will attempt to match the given email with the


user's email or username eld, and will send them a
password reset email. By doing this, you can opt to have
users use their email as their username, or you can
collect it separately and store it in the email eld.
The ow for password reset is as follows:

User requests that their password be reset by


typing in their email.

Parse sends an email to their address, with a


special password reset link.

User clicks on the reset link, and is directed to a


special Parse page that will allow them type in a
new password.

User types in a new password. Their password


has now been reset to a value they specify.

Note that the messaging in this ow will reference your


app by the name that you specied when you created
this app on Parse.

Querying
To query for users, you need to use the special user
query:

ParseQuery<ParseUser> query =
ParseUser.getQuery();
query.whereEqualTo("gender", "female");
query.findInBackground(new
FindCallback<ParseUser>() {
public void done(List<ParseUser> objects,
ParseException e) {
if (e == null) {
// The query was successful.
} else {
// Something went wrong.
}
}
});

In addition, you can use get to get a ParseUser by id.

Associations
Associations involving a ParseUser work right of the
box. For example, let's say you're making a blogging
app. To store a new post for a user and retrieve all their
posts:

ParseUser user = ParseUser.getCurrentUser();


// Make a new post
ParseObject post = new ParseObject("Post");
post.put("title", "My New Post");
post.put("body", "This is some great
content.");
post.put("user", user);
post.saveInBackground();
// Find all posts by the current user
ParseQuery<ParseObject> query =
ParseQuery.getQuery("Post");
query.whereEqualTo("user", user);
query.findInBackground(new
FindCallback<ParseObject>() { ... });

Facebook Users
Parse provides an easy way to integrate Facebook with
your application. The Facebook SDK can be used with
our SDK, and is integrated with the ParseUser class to
make linking your users to their Facebook identities
easy.
Using our Facebook integration, you can associate an
authenticated Facebook user with a ParseUser . With
just a few lines of code, you'll be able to provide a "Log
in with Facebook" option in your app, and be able to
save their data to Parse.

Note: Parse is compatible with both Facebook SDK 3.x


and 4.x for Android. These instructions are for
Facebook SDK 4.x.
SETUP

To start using Facebook with Parse, you need to:


1

Set up a Facebook app, if you haven't already.

Add your application's Facebook Application ID


on your Parse application's settings page.

Follow Facebook's instructions for getting


started with the Facebook SDK to create an app
linked to the Facebook SDK. Once you get to
Step 6, stop after linking the Facebook SDK
project and conguring the Facebook app ID. You
can use our guide to attach your Parse users to
Facebook accounts when logging in.

Add com.parse:parsefacebookutils-v4android:1.10.3@aar to your Gradle


dependencies. This includes the contents of the
Parse-*.jar and the com.parse:parseandroid:1.10.+ repository, so be sure to remove
as needed to prevent duplicate dependencies,
otherwise a com.android.dex.DexException will
be thrown.

Add the following where you initialize the Parse


SDK in your Application.onCreate() :

ParseFacebookUtils.initialize(context);

Facebook's Android SDK provides an enhanced login


experience on devices that have Facebook's ocial
Android app installed. This allows users of apps that
support Facebook login to sign in directly through the
Facebook app, using credentials that are already on the
device. If the Facebook app is not installed, the default
dialog-based authentication will be used. Facebook calls

this feature "Single sign-on" (SSO), and requires you to


override onActivityResult() in your calling
Activity :

@Override
protected void onActivityResult(int
requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode,
resultCode, data);
ParseFacebookUtils.onActivityResult(requestCode,
resultCode, data);
}

If your Activity is already using onActivityResult() ,


you can avoid requestCode collisions by specifying
your own request code oset when initializing
ParseFacebookUtils.initialize(context,
callbackRequestCodeOffset) . Otherwise, a sensible
default activityCode will be used.
If you encounter any issues that are Facebook-related, a
good resource is the ocial Facebook SDK for
Android page.
There are two main ways to use Facebook with your
Parse users: (1) logging in as a Facebook user and
creating a ParseUser , or (2) linking Facebook to an
existing ParseUser .

It is up to you to record any data that you need from the


Facebook user after they authenticate. To accomplish
this, you'll need to do a graph query via Facebook's SDK.

LOGIN & SIGNUP

ParseFacebookUtils provides a way to allow your

ParseUser s to log in or sign up through Facebook. This


is generally accomplished using the
logInWithReadPermissionsInBackground(String,
Collection<String>) method:

ParseFacebookUtils.logInWithReadPermissionsInBackground(
permissions, new LogInCallback() {
@Override
public void done(ParseUser user,
ParseException err) {
if (user == null) {
Log.d("MyApp", "Uh oh. The user
cancelled the Facebook login.");
} else if (user.isNew()) {
Log.d("MyApp", "User signed up and
logged in through Facebook!");
} else {
Log.d("MyApp", "User logged in through
Facebook!");
}
}
});

When this code is run, the following happens:


1

The user is shown the Facebook login dialog or a


prompt generated by the Facebook app.

The user authenticates via Facebook, and your


app receives a callback.

Our SDK receives the user's Facebook access


data and saves it to a ParseUser . If no
ParseUser exists with the same Facebook ID,
then a new ParseUser is created.

Your LogInCallback is called with the user.

The current user reference will be updated to this


user.

In order to display the Facebook login dialogs and


activities, the current Activity must be provided
(often, the current activity is this when calling
logInWithReadPermissionsInBackground() from
within the Activity ) as we have done above.
ParseUser integration doesn't require any permissions
to work out of the box (i.e. null is perfectly
acceptable). When logging in, you can only use read
permissions. See our documentation below about
requesting additional permissions (read or publish).
Read more about permissions on Facebook's
developer guide.
FACEBOOK LINKING

If you want to associate an existing ParseUser to a


Facebook account, you can link it like so:

if (!ParseFacebookUtils.isLinked(user)) {
ParseFacebookUtils.linkWithReadPermissionsInBackground(user,
this, permissions, new SaveCallback() {
@Override
public void done(ParseException ex) {
if (ParseFacebookUtils.isLinked(user)) {
Log.d("MyApp", "Woohoo, user logged in
with Facebook!");
}
}
});
}

The steps that happen when linking are very similar to


log in. The dierence is that on successful login, the
existing ParseUser is updated with the Facebook
information. Future logins via Facebook will now log the
user into their existing account.

If you want to unlink Facebook from a user, simply do


this:

ParseFacebookUtils.unlinkInBackground(user,
new SaveCallback() {
@Override
public void done(ParseException ex) {
if (ex == null) {
Log.d("MyApp", "The user is no longer
associated with their Facebook account.");
}
}
});

REQUESTING PERMISSIONS

As of v3.0 of the Facebook SDK, read and publish


permissions must be requested separately. To request
additional permissions, you may call
ParseFacebookUtils.linkWithReadPermissionsInBack
ground() or
ParseFacebookUtils.linkWithPublishPermissionsInB
ackground() . For more information about requesting
new permissions, please see Facebook's API
documentation for these functions.
After successfully retrieving new permissions, please
call
ParseFacebookUtilities.linkInBackground(ParseUse
r, AccessToken) , which will save any changes to the
session token back to the ParseUser and ensure that
this session data follows the user wherever it logs in.
FACEBOOK SDK AND PARSE

The Facebook Android SDK provides a number of


helper classes for interacting with Facebook's API.
Generally, you will use the GraphRequest class to
interact with Facebook on behalf of your logged-in

user. You can read more about the Facebook SDK


here.
To access the user's AccessToken you can simply call
AccessToken.getCurrentAccessToken() to access the
AccessToken instance, which can then be passed to
GraphRequest s.

Twitter Users
As with Facebook, Parse also provides an easy way to
integrate Twitter authentication into your application.
The Parse SDK provides a straightforward way to
authorize and link a Twitter account to your
ParseUser s. With just a few lines of code, you'll be able
to provide a "log in with Twitter" option in your app, and
be able to save their data to Parse.
SETUP

To start using Twitter with Parse, you need to:


1

Set up a Twitter app, if you haven't already.

Add your application's Twitter consumer key on


your Parse application's settings page.

When asked to specify a "Callback URL" for your


Twitter app, please insert a valid URL. This value
will not be used by your iOS or Android
application, but is necessary in order to enable
authentication through Twitter.

Add compile 'com.parse:parsetwitterutilsandroid:1.10.+' to your Gradle dependencies.


This includes the contents of the Parse-*.jar
and the com.parse:parse-android:1.10.+
repository, so be sure to remove as needed to
prevent duplicate dependencies, otherwise a
com.android.dex.DexException will be thrown.

Add the following where you initialize the Parse


SDK in your Application.onCreate()

ParseTwitterUtils.initialize("YOUR CONSUMER
KEY", "YOUR CONSUMER SECRET");

If you encounter any issues that are Twitter-related, a


good resource is the ocial Twitter documentation.
There are two main ways to use Twitter with your
Parse users: (1) logging in as a Twitter user and creating
a ParseUser , or (2) linking Twitter to an existing
ParseUser .
LOGIN & SIGNUP

ParseTwitterUtils provides a way to allow your


ParseUser s to log in or sign up through Twitter. This is
accomplished using the logIn() method:

ParseTwitterUtils.logIn(this, new
LogInCallback() {
@Override
public void done(ParseUser user,
ParseException err) {
if (user == null) {
Log.d("MyApp", "Uh oh. The user
cancelled the Twitter login.");
} else if (user.isNew()) {
Log.d("MyApp", "User signed up and
logged in through Twitter!");
} else {
Log.d("MyApp", "User logged in through
Twitter!");
}
}
});

When this code is run, the following happens:

The user is shown the Twitter login dialog.

The user authenticates via Twitter, and your app


receives a callback.

Our SDK receives the Twitter data and saves it to


a ParseUser . If it's a new user based on the
Twitter handle, then that user is created.

Your LogInCallback is called with the user.

In order to display the Twitter login dialogs and


activities, the current Context must be provided
(often, the current context is this when calling
logIn() from within the Activity ) as we have done
above.
TWITTER LINKING

If you want to associate an existing ParseUser with a


Twitter account, you can link it like so:

if (!ParseTwitterUtils.isLinked(user)) {
ParseTwitterUtils.link(user, this, new
SaveCallback() {
@Override
public void done(ParseException ex) {
if (ParseTwitterUtils.isLinked(user)) {
Log.d("MyApp", "Woohoo, user logged in
with Twitter!");
}
}
});
}

The steps that happen when linking are very similar to


log in. The dierence is that on successful login, the
existing ParseUser is updated with the Twitter
information. Future logins via Twitter will now log the
user into their existing account.

If you want to unlink Twitter from a user, simply do this:

ParseTwitterUtils.unlinkInBackground(user, new
SaveCallback() {
@Override
public void done(ParseException ex) {
if (ex == null) {
Log.d("MyApp", "The user is no longer
associated with their Twitter account.");
}
}
});

TWITTER API CALLS

Our SDK provides a straightforward way to sign your


API HTTP requests to the Twitter REST API when your
app has a Twitter-linked ParseUser . To make a request
through our API, you can use the Twitter singleton
provided by ParseTwitterUtils :

HttpClient client = new DefaultHttpClient();


HttpGet verifyGet = new HttpGet(
"https://api.twitter.com/1.1/account/verify_credentials.json"
ParseTwitterUtils.getTwitter().signRequest(verifyGet);
HttpResponse response =
client.execute(verifyGet);

Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Sessions
Sessions represent an instance of a user logged into a
device. Sessions are automatically created when users
log in or sign up. They are automatically deleted when
users log out. There is one distinct ParseSession
object for each user-installation pair; if a user issues a
login request from a device they're already logged into,
that user's previous ParseSession object for that
Installation is automatically deleted. ParseSession
objects are stored on Parse in the Session class, and
you can view them on the Parse.com Data Browser. We
provide a set of APIs to manage ParseSession objects
in your app.

Session APIs are only available in apps with revocable


sessions enabled. Parse apps created after March 25,
2015 have this enabled by default ("Require Revocable
Sessions" toggle in your Parse.com app settings page). If
you have an existing app, you can upgrade to revocable
sessions by following the Session Migration Tutorial.

ParseSession is a subclass of ParseObject , so you


can query, update, and delete sessions in the same way
that you manipulate normal objects on Parse. Because
the Parse Cloud automatically creates sessions when
you log in or sign up users, you should not manually
create ParseSession objects unless you are building a
"Parse for IoT" app (e.g. Arduino or Embedded C).
Deleting a ParseSession will log the user out of the
device that is currently using this session's token.
Unlike other Parse objects, the ParseSession class
does not have Cloud Code triggers. So you cannot
register a beforeSave or afterSave handler for the
Session class.

Properties

The ParseSession object has these special elds:

sessionToken (readonly): String token for


authentication on Parse API requests. In the
response of ParseSession queries, only your
current ParseSession object will contain a
session token.

user : (readonly) Pointer to the ParseUser


object that this session is for.

createdWith (readonly): Information about how


this session was created (e.g. { "action":
"login", "authProvider": "password"} ).

action could have values: login , signup ,


create , or upgrade . The create action is
when the developer manually creates the
session by saving a ParseSession object.
The upgrade action is when the user is
upgraded to revocable session from a
legacy session token.

authProvider could have values:


password , anonymous , facebook , or
twitter .

restricted (readonly): Boolean for whether this


session is restricted.

+ Restricted sessions do not have write


permissions on ParseUser , ParseSession ,
and ParseRole classes on Parse.
Restricted sessions also cannot read
unrestricted sessions.

+ All sessions that the Parse Cloud


automatically creates during user
login/signup will be unrestricted. All
sessions that the developer manually
creates by saving a new ParseSession
object from the client (only needed for
"Parse for IoT" apps) will be restricted.

expiresAt (readonly): Approximate UTC date


when this ParseSession object will be
automatically deleted. You can congure session

expiration settings (either 1-year inactivity


expiration or no expiration) in your app's
Parse.com dashboard settings page.

installationId (can be set only once): String


referring to the ParseInstallation where the
session is logged in from. For Parse SDKs, this
eld will be automatically set when users log in or
sign up. All special elds except installationId
can only be set automatically by the Parse Cloud.
You can add custom elds onto ParseSession
objects, but please keep in mind that any loggedin device (with session token) can read other
sessions that belong to the same user (unless you
disable Class-Level Permissions, see below).

Handling Invalid Session Token Error


Apps created before March 25, 2015 use legacy session
tokens until you migrate them to use the new revocable
sessions. On API requests with legacy tokens, if the
token is invalid (e.g. User object was deleted), then the
request is executed as a non-logged in user and no
error was returned. On API requests with revocable
session tokens, an invalid session token will always fail
with the "invalid session token" error. This new
behavior lets you know when you need to ask the user
to log in again.
With revocable sessions, your current session token
could become invalid if its corresponding
ParseSession object is deleted from the Parse Cloud.
This could happen if you implement a Session Manager
UI that lets users log out of other devices, or if you
manually delete the session via Cloud Code, REST API,
or Data Browser. Sessions could also be deleted due to
automatic expiration (if congured in app settings).
When a device's session token no longer corresponds
to a ParseSession object on the Parse Cloud, all API
requests from that device will fail with Error 209:
invalid session token.
To handle this error, we recommend writing a global

utility function that is called by all of your Parse request


error callbacks. You can then handle the "invalid session
token" error in this global function. You should prompt
the user to login again so that they can obtain a new
session token. This code could look like this:

public class ParseErrorHandler {


public static void
handleParseError(ParseException e) {
switch (e.getCode()) {
case INVALID_SESSION_TOKEN:
handleInvalidSessionToken()
break;
... // Other Parse API errors that you
want to explicitly handle
}
}
private static void
handleInvalidSessionToken() {
//-------------------------------------// Option 1: Show a message asking the
user to log out and log back in.
//-------------------------------------// If the user needs to finish what they
were doing, they have the opportunity to do
so.
//
// new AlertDialog.Builder(getActivity())
//

.setMessage("Session is no longer

valid, please log out and log in again.")


//
.setCancelable(false).setPositiveButton("OK",
...).create().show();
//-------------------------------------// Option #2: Show login screen so user

can re-authenticate.
//-------------------------------------// You may want this if the logout button
could be inaccessible in the UI.
//
// startActivityForResult(new
ParseLoginBuilder(getActivity()).build(), 0);
}
}
// In all API requests, call the global error
handler, e.g.
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject> results,
ParseException e) {
if (e == null) {
// Query successful, continue other app
logic
} else {
// Query failed
ParseErrorHandler.handleParseError(e);
}
}
});

Security
ParseSession objects can only be accessed by the
user specied in the user eld. All ParseSession
objects have an ACL that is read and write by that user
only. You cannot change this ACL. This means querying
for sessions will only return objects that match the
current logged-in user.
When you log in a user via
ParseUser.logInInBackground() , Parse will
automatically create a new unrestricted ParseSession
object in the Parse Cloud. Same for signups and

Facebook/Twitter logins.
Session objects manually created from client SDKs (by
creating an instance of ParseSession , and saving it)
are always restricted. You cannot manually create an
unrestricted sessions using the object creation API.
Restricted sessions are prohibited from creating,
modifying, or deleting any data in the ParseUser ,
ParseSession , and ParseRole classes. Restricted
session also cannot read unrestricted sessions.
Restricted Sessions are useful for "Parse for IoT"
devices (e.g Arduino or Embedded C) that may run in a
less-trusted physical environment than mobile apps.
However, please keep in mind that restricted sessions
can still read data on ParseUser , ParseSession , and
ParseRole classes, and can read/write data in any
other class just like a normal session. So it is still
important for IoT devices to be in a safe physical
environment and ideally use encrypted storage to store
the session token.
If you want to prevent restricted Sessions from
modifying classes other than ParseUser ,
ParseSession , or ParseRole , you can write a Cloud
Code beforeSave handler for that class:

Parse.Cloud.beforeSave("MyClass",
function(request, response) {
Parse.Session.current().then(function(session)
{
if (session.get('restricted')) {
response.error('write operation not
allowed');
}
response.success();
});
});

You can congure Class-Level Permissions (CLPs) for


the Session class just like other classes on Parse. CLPs
restrict reading/writing of sessions via the
ParseSession API, but do not restrict Parse Cloud's
automatic session creation/deletion when users log in,
sign up, and log out. We recommend that you disable all
CLPs not needed by your app. Here are some common
use cases for Session CLPs:

+ Find, Delete Useful for building a UI screen


that allows users to see their active session on all
devices, and log out of sessions on other devices.
If your app does not have this feature, you should
disable these permissions.

+ Create Useful for "Parse for IoT" apps (e.g.


Arduino or Embedded C) that provision restricted
user sessions for other devices from the phone
app. You should disable this permission when
building apps for mobile and web. For "Parse for
IoT" apps, you should check whether your IoT
device actually needs to access user-specic
data. If not, then your IoT device does not need a
user session, and you should disable this
permission.

+ Get, Update, Add Field Unless you need these


operations, you should disable these permissions.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Roles
As your app grows in scope and user-base, you may nd
yourself needing more coarse-grained control over
access to pieces of your data than user-linked ACLs can

provide. To address this requirement, Parse supports a


form of Role-based Access Control. Roles provide a
logical way of grouping users with common access
privileges to your Parse data. Roles are named objects
that contain users and other roles. Any permission
granted to a role is implicitly granted to its users as well
as to the users of any roles that it contains.
For example, in your application with curated content,
you may have a number of users that are considered
"Moderators" and can modify and delete content
created by other users. You may also have a set of users
that are "Administrators" and are allowed all of the
same privileges as Moderators, but can also modify the
global settings for the application. By adding users to
these roles, you can ensure that new users can be made
moderators or administrators, without having to
manually grant permission to every resource for each
user.
We provide a specialized class called ParseRole that
represents these role objects in your client code.
ParseRole is a subclass of ParseObject , and has all of
the same features, such as a exible schema, automatic
persistence, and a key value interface. All the methods
that are on ParseObject also exist on ParseRole . The
dierence is that ParseRole has some additions
specic to management of roles.

Properties
ParseRole has several properties that set it apart from
ParseObject :

+ name: The name for the role. This value is


required, and can only be set once as a role is
being created. The name must consist of
alphanumeric characters, spaces, -, or _. This
name will be used to identify the Role without
needing its objectId.

+ users: A relation to the set of users that will


inherit permissions granted to the containing
role.

+ roles: A relation to the set of roles whose users


and roles will inherit permissions granted to the
containing role.

Security for Role Objects


The ParseRole uses the same security scheme (ACLs)
as all other objects on Parse, except that it requires an
ACL to be set explicitly. Generally, only users with
greatly elevated privileges (e.g. a master user or
Administrator) should be able to create or modify a
Role, so you should dene its ACLs accordingly.
Remember, if you give write-access to a ParseRole to
a user, that user can add other users to the role, or even
delete the role altogether.
To create a new ParseRole , you would write:

// By specifying no write privileges for the


ACL, we can ensure the role cannot be altered.
ParseACL roleACL = new ParseACL();
roleACL.setPublicReadAccess(true);
ParseRole role = new
ParseRole("Administrator", roleACL);
role.saveInBackground();

You can add users and roles that should inherit your
new role's permissions through the "users" and "roles"
relations on ParseRole :

ParseRole role = new ParseRole(roleName,


roleACL);
for (ParseUser user : usersToAddToRole) {
role.getUsers().add(user)

}
for (ParseRole childRole : rolesToAddToRole) {
role.getRoles().add(childRole);
}
role.saveInBackground();

Take great care when assigning ACLs to your roles so


that they can only be modied by those who should
have permissions to modify them.

Security for Other Objects


Now that you have created a set of roles for use in your
application, you can use them with ACLs to dene the
privileges that their users will receive. Each
ParseObject can specify a ParseACL , which provides
an access control list that indicates which users and
roles should be granted read or write access to the
object.
Giving a role read or write permission to an object is
straightforward. You can either use the ParseRole :

ParseRole moderators = /* Query for some


ParseRole */;
ParseObject wallPost = new
ParseObject("WallPost");
ParseACL postACL = new ParseACL();
postACL.setRoleWriteAccess(moderators);
wallPost.setACL(postACL);
wallPost.saveInBackground();

You can avoid querying for a role by specifying its name


for the ACL:

ParseObject wallPost = new


ParseObject("WallPost");
ParseACL postACL = new ParseACL();
postACL.setRoleWriteAccess("Moderators",
true);
wallPost.setACL(postACL);
wallPost.save();

Role-based ParseACL s can also be used when


specifying default ACLs for your application, making it
easy to protect your users' data while granting access
to users with additional privileges. For example, a
moderated forum application might specify a default
ACL like this:

ParseACL defaultACL = new ParseACL();


// Everybody can read objects created by this
user
defaultACL.setPublicReadAccess(true);
// Moderators can also modify these objects
defaultACL.setRoleWriteAccess("Moderators");
// And the user can read and modify its own
objects
ParseACL.setDefaultACL(defaultACL, true);

Role Hierarchy
As described above, one role can contain another,
establishing a parent-child relationship between the
two roles. The consequence of this relationship is that
any permission granted to the parent role is implicitly
granted to all of its child roles.
These types of relationships are commonly found in
applications with user-managed content, such as
forums. Some small subset of users are

"Administrators", with the highest level of access to


tweaking the application's settings, creating new
forums, setting global messages, and so on. Another set
of users are "Moderators", who are responsible for
ensuring that the content created by users remains
appropriate. Any user with Administrator privileges
should also be granted the permissions of any
Moderator. To establish this relationship, you would
make your "Administrators" role a child role of
"Moderators", like this:

ParseRole administrators = /* Your


"Administrators" role */;
ParseRole moderators = /* Your "Moderators"
role */;
moderators.getRoles().add(administrators);
moderators.saveInBackground();

Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Files
The ParseFile
ParseFile lets you store application les in the cloud
that would otherwise be too large or cumbersome to t
into a regular ParseObject . The most common use
case is storing images but you can also use it for
documents, videos, music, and any other binary data
(up to 10 megabytes).
Getting started with ParseFile is easy. First, you'll
need to have the data in byte[] form and then create a

ParseFile with it. In this example, we'll just use a


string:

byte[] data = "Working at Parse is


great!".getBytes();
ParseFile file = new ParseFile("resume.txt",
data);

Notice in this example that we give the le a name of


resume.txt . There's two things to note here:

+ You don't need to worry about lename


collisions. Each upload gets a unique identier so
there's no problem with uploading multiple les
named resume.txt .

+ It's important that you give a name to the le that


has a le extension. This lets Parse gure out the
le type and handle it accordingly. So, if you're
storing PNG images, make sure your lename
ends with .png .
Next you'll want to save the le up to the cloud. As with
ParseObject , there are many variants of the save
method you can use depending on what sort of callback
and error handling suits you.

file.saveInBackground();

Finally, after the save completes, you can associate a


ParseFile onto a ParseObject just like any other
piece of data:

ParseObject jobApplication = new


ParseObject("JobApplication");
jobApplication.put("applicantName", "Joe

Smith");
jobApplication.put("applicantResumeFile",
file);
jobApplication.saveInBackground();

Retrieving it back involves calling one of the getData


variants on the ParseObject . Here we retrieve the
resume le o another JobApplication object:

ParseFile applicantResume =
(ParseFile)anotherApplication.get("applicantResumeFile"
applicantResume.getDataInBackground(new
GetDataCallback() {
public void done(byte[] data, ParseException
e) {
if (e == null) {
// data has the bytes for the resume
} else {
// something went wrong
}
}
});

Just like on ParseObject , you will most likely want to


use the background version of getData .

Progress
It's easy to get the progress of both uploads and
downloads using ParseFile by passing a
ProgressCallback to saveInBackground and
getDataInBackground . For example:

byte[] data = "Working at Parse is

great!".getBytes();
ParseFile file = new ParseFile("resume.txt",
data);
file.saveInBackground(new SaveCallback() {
public void done(ParseException e) {
// Handle success or failure here ...
}
}, new ProgressCallback() {
public void done(Integer percentDone) {
// Update your progress spinner here.
percentDone will be between 0 and 100.
}
});

You can delete les that are referenced by objects


using the REST API. You will need to provide the
master key in order to be allowed to delete a le.
If your les are not referenced by any object in your
app, it is not possible to delete them through the REST
API. You may request a cleanup of unused les in your
app's Settings page. Keep in mind that doing so may
break functionality which depended on accessing
unreferenced les through their URL property. Files
that are currently associated with an object will not be
aected.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

GeoPoints
Parse allows you to associate real-world latitude and

longitude coordinates with an object. Adding a


ParseGeoPoint to a ParseObject allows queries to
take into account the proximity of an object to a
reference point. This allows you to easily do things like
nd out what user is closest to another user or which
places are closest to a user.

ParseGeoPoint
To associate a point with an object you rst need to
create a ParseGeoPoint . For example, to create a point
with latitude of 40.0 degrees and -30.0 degrees
longitude:

ParseGeoPoint point = new ParseGeoPoint(40.0,


-30.0);

This point is then stored in the object as a regular eld.

placeObject.put("location", point);

Geo Queries
Now that you have a bunch of objects with spatial
coordinates, it would be nice to nd out which objects
are closest to a point. This can be done by adding
another restriction to ParseQuery using whereNear .
Getting a list of ten places that are closest to a user
may look something like:

ParseGeoPoint userLocation = (ParseGeoPoint)


userObject.get("location");
ParseQuery<ParseObject> query =
ParseQuery.getQuery("PlaceObject");

query.whereNear("location", userLocation);
query.setLimit(10);
query.findInBackground(new
FindCallback<ParseObject>() { ... });

At this point nearPlaces will be an array of objects


ordered by distance (nearest to farthest) from
userLocation . Note that if an additional
orderByAscending() / orderByDescending()
constraint is applied, it will take precedence over the
distance ordering.
To limit the results using distance, check out
whereWithinKilometers , whereWithinMiles , and
whereWithinRadians .
It's also possible to query for the set of objects that are
contained within a particular area. To nd the objects in
a rectangular bounding box, add the
whereWithinGeoBox restriction to your ParseQuery .

ParseGeoPoint southwestOfSF = new


ParseGeoPoint(37.708813, -122.526398);
ParseGeoPoint northeastOfSF = new
ParseGeoPoint(37.822802, -122.373962);
ParseQuery<ParseObject> query =
ParseQuery.getQuery("PizzaPlaceObject");
query.whereWithinGeoBox("location",
southwestOfSF, northeastOfSF);
query.findInBackground(new
FindCallback<ParseObject>() { ... });

Caveats
At the moment there are a couple of things to watch
out for:

Each ParseObject class may only have one key


with a ParseGeoPoint object.

Using the whereNear constraint will also limit


results to within 100 miles.

Points should not equal or exceed the extreme


ends of the ranges. Latitude should not be -90.0
or 90.0. Longitude should not be -180.0 or
180.0. Attempting to set latitude or longitude out
of bounds will cause an error.

Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Local Datastore
The Parse Android SDK provides a local datastore
which can be used to store and retrieve ParseObject s,
even when the network is unavailable. To enable this
functionality, simply call
Parse.enableLocalDatastore() before your call to
initialize .

import com.parse.Parse;
import android.app.Application;
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
Parse.enableLocalDatastore(this);
Parse.initialize(this,
PARSE_APPLICATION_ID, PARSE_CLIENT_KEY);
}

There are a couple of side eects of enabling the local


datastore that you should be aware of. When enabled,
there will only be one instance of any given
ParseObject . For example, imagine you have an
instance of the "GameScore" class with an objectId of
"xWMyZ4YEGZ" , and then you issue a ParseQuery for all
instances of "GameScore" with that objectId . The
result will be the same instance of the object you
already have in memory.
Another side eect is that the current user and current
installation will be stored in the local datastore, so you
can persist unsaved changes to these objects between
runs of your app using the methods below.
Calling the saveEventually method on a ParseObject
will cause the object to be pinned in the local datastore
until the save completes. So now, if you change the
current ParseUser and call
ParseUser.getCurrentUser().saveEventually() ,
your app will always see the changes that you have
made.

Pinning
You can store a ParseObject in the local datastore by
pinning it. Pinning a ParseObject is recursive, just like
saving, so any objects that are pointed to by the one
you are pinning will also be pinned. When an object is
pinned, every time you update it by fetching or saving
new data, the copy in the local datastore will be
updated automatically. You don't need to worry about it
at all.

ParseObject gameScore = new

ParseObject("GameScore");
gameScore.put("score", 1337);
gameScore.put("playerName", "Sean Plott");
gameScore.put("cheatMode", false);
gameScore.pinInBackground();

If you have multiple objects, you can pin them all at


once with the pinAllInBackground convenience
method.

ParseObject.pinAllInBackground(listOfObjects);

Retrieving Objects
Storing objects is great, but it's only useful if you can
then get the objects back out later. Retrieving an object
from the local datastore works just like retrieving one
over the network. The only dierence is calling the
fromLocalDatastore method to tell the ParseQuery
where to look for its results.

ParseQuery<ParseObject> query =
ParseQuery.getQuery(GameScore");
query.fromLocalDatastore();
query.getInBackground("xWMyZ4YE", new
GetCallback<ParseObject>() {
public void done(ParseObject object,
ParseException e) {
if (e == null) {
// object will be your game score
} else {
// something went wrong
}
}
});

Querying
Often, you'll want to nd a whole list of objects that
match certain criteria, instead of getting a single object
by id. To do that, you can use a ParseQuery. Any
ParseQuery can be used with the local datastore just
as with the network. The results will include any object
you have pinned that matches the query. Any unsaved
changes you have made to the object will be considered
when evaluating the query. So you can nd a local
object that matches, even if it was never returned from
the server for this particular query.

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
query.whereEqualTo("playerName", "Joe Bob");
query.fromLocalDatastore();
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject>
scoreList,
ParseException e) {
if (e == null) {
Log.d("score", "Retrieved " +
scoreList.size());
} else {
Log.d("score", "Error: " +
e.getMessage());
}
}
});

Unpinning
When you are done with an object and no longer need
it to be in the local datastore, you can simply unpin it.

This will free up disk space on the device and keep your
queries on the local datastore running quickly.

gameScore.unpinInBackground();

There's also a method to unpin several objects at once.

ParseObject.unpinAllInBackground(listOfObjects);

Pinning with Labels


Manually pinning and unpinning each object individual is
a bit like using malloc and free . It is a very powerful
tool, but it can be dicult to manage what objects get
stored in complex scenarios. For example, imagine you
are making a game with separate high score lists for
global high scores and your friends' high scores. If one
of your friends happens to have a globally high score,
you need to make sure you don't unpin them
completely when you remove them from one of the
cached queries. To make these scenarios easier, you
can also pin with a label. Labels indicate a group of
objects that should be stored together.

// Add several objects with a label.


ParseObject.pinAllInBackground("MyScores",
someGameScores);
// Add another object with the same label.
anotherGameScore.pinInBackground("MyScores");

To unpin all of the objects with the same label at the


same time, you can pass a label to the unpin methods.

This saves you from having to manually track which


objects are in each group you care about.

ParseObject.unpinAllInBackground("MyScores");

Any object will stay in the datastore as long as it is


pinned with any label. In other words, if you pin an
object with two dierent labels, and then unpin it with
one label, the object will stay in the datastore until you
also unpin it with the other label.

Caching Query Results


Pinning with labels makes it easy to cache the results of
queries. You can use one label to pin the results of each
dierent query. To get new results from the network,
just do a query and update the pinned objects.

ParseQuery<ParseObject> query =
ParseQuery.getQuery(GameScore");
query.orderByDescending(score);
// Query for new results from the network.
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(final List<ParseObject>
scores, ParseException e) {
// Remove the previously cached results.
ParseObject.unpinAllInBackground(highScores,
new DeleteCallback() {
public void done(ParseException e) {
// Cache the new results.
ParseObject.pinAllInBackground(highScores,
scores);

}
});
}
});

When you want to get the cached results for the query,
you can then run the same query against the local
datastore.

ParseQuery<ParseObject> query =
ParseQuery.getQuery(GameScore");
query.orderByDescending(score);
query.fromLocalDatastore();
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject> scores,
ParseException e) {
// Yay! Cached scores!
}
});

Syncing Local Changes


Once you've saved some changes locally, there are a
few dierent ways you can save those changes back to
Parse over the network. The easiest way to do this is
with saveEventually . When you call saveEventually
on a ParseObject , it will be pinned until it can be saved.
The SDK will make sure to save the object the next
time the network is available.

gameScore.saveEventually();

If you'd like to have more control over the way objects

are synced, you can keep them in the local datastore


until you are ready to save them yourself using
saveInBackground . To manage the set of objects that
need to be saved, you can again use a label. The
fromPin method on ParseQuery makes it easy to
fetch just the objects you care about.

ParseQuery<ParseObject> query =
ParseQuery.getQuery(GameScore");
query.fromPin(MyChanges);
query.findInBackground(new
FindCallback<ParseObject>() {
public void done(List<ParseObject> scores,
ParseException e) {
for (ParseObject score in scores) {
score.saveInBackground();
score.unpinInBackground(MyChanges);
}
}
});

Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Push Notications
Push notications are a great way to keep your users
engaged and informed about your app. You can reach
your entire user base quickly and eectively. This guide
will help you through the setup process and the general
usage of Parse to send push notications.
If you haven't installed the SDK yet, head over to the

Push QuickStart to get our SDK up and running.

Setting Up Push
If you want to start using push, start by completing the
Android Push tutorial to learn how to congure your
app. Come back to this guide afterwards to learn more
about the push features oered by Parse.
The Parse library provides push notications using
Google Cloud Messaging (GCM) if Google Play Services
are available. Learn more about Google Play Services
here.
When sending pushes to Android devices with GCM,
there are several pieces of information that Parse keeps
track of automatically:

+ Registration ID: The GCM registration ID


uniquely identies an app/device pairing for push
purposes.

+ Sender ID: The GCM sender ID is a public number


that identies the sender of a push notication.

+ API key: The GCM API key is a server secret that


allows a server to send pushes to a registration ID
on behalf of a particular sender ID.
The Parse Android SDK chooses a reasonable default
conguration so that you do not have to worry about
GCM registration ids, sender ids, or API keys. In
particular, the SDK will automatically register your app
for push at startup time using Parse's sender ID
(1076345567071) and will store the resulting
registration ID in the deviceToken eld of the app's
current ParseInstallation .
However, as an advanced feature for developers that
want to send pushes from multiple push providers,
Parse allows you to optionally register your app for
pushes with additional GCM sender IDs. To do this,

specify the additional GCM sender ID with the following


<meta-data> tag as a child of the <application>
element in your app's AndroidManifest.xml :

<meta-data
android:name="com.parse.push.gcm_sender_id"
android:value="id:YOUR_SENDER_ID"
/>;

In the sample snippet above, YOUR_SENDER_ID should


be replaced by a numeric GCM sender ID. Note that the
Parse SDK expects you to prex your sender ID with an
id: prex, as shown in the sample snippet.
If you want to register your app with multiple additional
sender IDs, then the android:value in the <metadata> element above should hold a comma-delimited
list of sender IDs, as in the following snippet:

<meta-data
android:name="com.parse.push.gcm_sender_id"
android:value="id:YOUR_SENDER_ID_1,YOUR_SENDER_ID_2,YOUR_SENDER_ID_3"
/>;

Installations
Every Parse application installed on a device registered
for push notications has an associated Installation
object. The Installation object is where you store all
the data needed to target push notications. For
example, in a baseball app, you could store the teams a
user is interested in to send updates about their
performance. Saving the Installation object is also
required for tracking push-related app open events.

In Android, Installation objects are available through


the ParseInstallation class, a subclass of
ParseObject . It uses the same API for storing and
retrieving data. To access the current Installation
object from your Android app, use the
ParseInstallation.getCurrentInstallation()
method. The rst time you save a ParseInstallation ,
Parse will add it to your Installation class and it will
be available for targeting push notications.

// Save the current Installation to Parse.


ParseInstallation.getCurrentInstallation().saveInBackground();

While it is possible to modify a ParseInstallation just


like you would a ParseObject , there are several special
elds that help manage and target devices.

channels : An array of the channels to which a


device is currently subscribed.

installationId : Unique Id for the device used


by Parse (readonly).

deviceType : The type of device, "ios", "osx",


"android", "winrt", "winphone", "dotnet", or
"embedded". On Android devices, this eld will be
set to "android" (readonly).

pushType : This eld is reserved for directing


Parse to the push delivery network to be used. If
the device is registered to receive pushes via
GCM, this eld will be marked "gcm". If Google
Play Services is not available, it will be blank
(readonly).

GCMSenderId : This eld only has meaning for


Android ParseInstallation s that use the GCM
push type. It is reserved for directing Parse to
send pushes to this installation with an alternate
GCM sender ID. This eld should generally not be
set unless you are uploading installation data
from another push provider. If you set this eld,

then you must set the GCM API key


corresponding to this GCM sender ID in your
Parse application's push settings.

deviceToken : The token used by GCM to keep


track of registration ID. On iOS devices, this is the
Apple generated token (readonly).

appName : The display name of the client


application to which this installation belongs. This
value is synchronized every time an
ParseInstallation object is saved from the
device (readonly).

appVersion : The version string of the client


application to which this installation belongs. This
value is synchronized every time an
ParseInstallation object is saved from the
device (readonly).

parseVersion : The version of the Parse SDK


which this installation uses. This value is
synchronized every time an ParseInstallation
object is saved from the device (readonly).

timeZone : The current time zone where the


target device is located. This value is
synchronized every time an ParseInstallation
object is saved from the device (readonly).

localeIdentifier : The locale identier of the


device in the format [language code]-[COUNTRY
CODE]. The language codes are two-letter
lowercase ISO language codes (such as "en") as
dened by ISO 639-1. The country codes are twoletter uppercase ISO country codes (such as
"US") as dened by ISO 3166-1. This value is
synchronized every time an ParseInstallation
object is saved from the device (readonly).

badge : The current value of the icon badge for


iOS apps. Changes to this value on the server will
be used for future badge-increment push
notications.

channelUris : The Microsoft-generated push


URIs for Windows devices (readonly).

appIdentifier : A unique identier for this


installation's client application. This parameter is
not supported in Android (readonly).

The Parse Android SDK will avoid making unnecessary


requests. If a ParseInstallation is saved on the
device, a request to the Parse servers will only be made
if one of the ParseInstallation 's elds has been
explicitly updated.

Sending Pushes
There are two ways to send push notications using
Parse: channels and advanced targeting. Channels
oer a simple and easy to use model for sending
pushes, while advanced targeting oers a more
powerful and exible model. Both are fully compatible
with each other and will be covered in this section.
Sending notications is often done from the Parse.com
push console, the REST API or from Cloud Code.
However, push notications can also be triggered by
the existing client SDKs. If you decide to send
notications from the client SDKs, you will need to set
Client Push Enabled in the Push Notications settings
of your Parse app.
However, be sure you understand that enabling Client
Push can lead to a security vulnerability in your app, as
outlined on our blog. We recommend that you enable
Client Push for testing purposes only, and move your
push notication logic into Cloud Code when your app
is ready to go into production.

You can view your past push notications on the


Parse.com push console for up to 30 days after
creating your push. For pushes scheduled in the future,

you can delete the push on the push console as long as


no sends have happened yet. After you send the push,
the push console shows push analytics graphs.
USING CHANNELS

The simplest way to start sending notications is using


channels. This allows you to use a publisher-subscriber
model for sending pushes. Devices start by subscribing
to one or more channels, and notications can later be
sent to these subscribers. The channels subscribed to
by a given Installation are stored in the channels
eld of the Installation object.
SUBSCRIBING TO CHANNELS

A channel is identied by a string that starts with a


letter and consists of alphanumeric characters,
underscores, and dashes. It doesn't need to be explicitly
created before it can be used and each Installation
can subscribe to any number of channels at a time.
Subscribing to a channel can be done using a single
method call. For example, in a baseball score app, we
could do:

// When users indicate they are Giants fans,


we subscribe them to that channel.
ParsePush.subscribeInBackground("Giants");

By default, the main activity for your app will be run


when a user responds to notications.
Once subscribed to the "Giants" channel, your
Installation object should have an updated
channels eld.

Unsubscribing from a channel is just as easy:

// When users indicate they are no longer


Giants fans, we unsubscribe them.
ParsePush.unsubscribeInBackground("Giants");

You can also get the set of channels that the current
device is subscribed to using:

List<String> subscribedChannels =
ParseInstallation.getCurrentInstallation().getList(

Neither the subscribe method nor the unsubscribe


method blocks the thread it is called from. The
subscription information is cached on the device's disk
if the network is inaccessible and transmitted to the
Parse Cloud as soon as the network is usable. This
means you don't have to worry about threading or
callbacks while managing subscriptions.
SENDING PUSHES TO CHANNELS

In the Android SDK, the following code can be used to


alert all subscribers of the "Giants" channel that their
favorite team just scored. This will display a notication
center alert to iOS users and a system tray notication
to Android users.

ParsePush push = new ParsePush();


push.setChannel("Giants");
push.setMessage("The Giants just scored! It's
now 2-2 against the Mets.");
push.sendInBackground();

If you want to target multiple channels with a single


push notication, you can use a LinkedList of
channels.

LinkedList<String> channels = new


LinkedList<String>();
channels.add("Giants");
channels.add("Mets");
ParsePush push = new ParsePush();
push.setChannels(channels); // Notice we use
setChannels not setChannel
push.setMessage("The Giants won against the
Mets 2-3.");
push.sendInBackground();

USING ADVANCED TARGETING

While channels are great for many applications,


sometimes you need more precision when targeting the
recipients of your pushes. Parse allows you to write a
query for any subset of your Installation objects
using the querying API and to send them a push.
Since ParseInstallation is a subclass of
ParseObject , you can save any data you want and even
create relationships between Installation objects
and your other objects. This allows you to send pushes
to a very customized and dynamic segment of your
user base.
SAVING INSTALLATION DATA

Storing data on an ParseInstallation object is just as


easy as storing any other data on Parse. In our Baseball
app, we could allow users to get pushes about game
results, scores and injury reports.

// Store app language and version


ParseInstallation installation =
ParseInstallation.getCurrentInstallation();
installation.put("scores",true);
installation.put("gameResults",true);
installation.put("injuryReports",true);
installation.saveInBackground();

You can even create relationships between your


Installation objects and other classes saved on
Parse. To associate a ParseInstallation with a
particular user, for example, you can simply store the
current user on the ParseInstallation .

// Associate the device with a user


ParseInstallation installation =
ParseInstallation.getCurrentInstallation();
installation.put("user",ParseUser.getCurrentUser());
installation.saveInBackground();

SENDING PUSHES TO QUERIES

Once you have your data stored on your


ParseInstallation objects, you can use a
ParseQuery to target a subset of these devices.
ParseInstallation queries work just like any other
Parse query, but we use the special static method
ParseInstallation.getQuery() to create it. We set
this query on our ParsePush object, before sending the
notication.

// Create our Installation query


ParseQuery pushQuery =
ParseInstallation.getQuery();

pushQuery.whereEqualTo("injuryReports", true);
// Send push notification to query
ParsePush push = new ParsePush();
push.setQuery(pushQuery); // Set our
Installation query
push.setMessage("Willie Hayes injured by own
pop fly.");
push.sendInBackground();

We can even use channels with our query. To send a


push to all subscribers of the "Giants" channel but
ltered by those who want score update, we can do the
following:

// Create our Installation query


ParseQuery pushQuery =
ParseInstallation.getQuery();
pushQuery.whereEqualTo("channels", "Giants");
// Set the channel
pushQuery.whereEqualTo("scores", true);
// Send push notification to query
ParsePush push = new ParsePush();
push.setQuery(pushQuery);
push.setMessage("Giants scored against the
A's! It's now 2-2.");
push.sendInBackground();

If we store relationships to other objects in our


ParseInstallation class, we can also use those in our
query. For example, we could send a push notication
to all users near a given location like this.

// Find users near a given location

ParseQuery userQuery = ParseUser.getQuery();


userQuery.whereWithinMiles("location",
stadiumLocation, 1.0)
// Find devices associated with these users
ParseQuery pushQuery =
ParseInstallation.getQuery();
pushQuery.whereMatchesQuery("user",
userQuery);
// Send push notification to query
ParsePush push = new ParsePush();
push.setQuery(pushQuery); // Set our
Installation query
push.setMessage("Free hotdogs at the Parse
concession stand!");
push.sendInBackground();

Sending Options
Push notications can do more than just send a
message. In Android, pushes can also include custom
data you wish to send. You have complete control of
how you handle the data included in your push
notication as we will see in the Receiving
Notications section. An expiration date can also be
set for the notication in case it is time sensitive.
CUSTOMIZING YOUR NOTIFICATIONS

If you want to send more than just a message, you will


need to use a JSONObject to package all of the data.
There are some reserved elds that have a special
meaning in Android.

alert : the notication's message.

uri : (Android only) an optional eld that


contains a URI. When the notication is opened,
an Activity associated with opening the URI is

launched.

title : (Android, Windows 8, & Windows Phone


8 only) the value displayed in the Android system
tray or Windows 8 toast notication.

For example, to send a notication that would increases


the badge number by 1 and plays a custom sound, you
can do the following. Note that you can set these
properties from your Android client, but they would
only take eect in the iOS version of your app. The
badge and sound elds would have no eects for
Android recipients.

JSONObject data = new JSONObject("{\"alert\":


\"The Mets scored!\",
\"badge\":
\"Increment\",
\"sound\":
\"cheering.caf\"}");
ParsePush push = new ParsePush();
push.setChannel("Mets");
push.setData(data);
push.sendPushInBackground();

It is also possible to specify your own data in this


dictionary. As we'll see in the Receiving Notications
section, you're able to use the data sent with your push
to do custom processing when a user receives and
interacts with a notication.

JSONObject data = new JSONObject("{\"name\":


\"Vaughn\",
\"newsItem\": \"Man bites dog\"}"));

ParsePush push = new ParsePush();


push.setQuery(injuryReportsQuery);
push.setChannel("Indians");
push.setData(data);
push.sendPushInBackground();

SETTING AN EXPIRATION DATE

When a user's device is turned o or not connected to


the internet, push notications cannot be delivered. If
you have a time sensitive notication that is not worth
delivering late, you can set an expiration date. This
avoids needlessly alerting users of information that may
no longer be relevant.
There are two methods provided by the ParsePush
class to allow setting an expiration date for your
notication. The rst is setExpirationTime which
simply takes an time (in UNIX epoch time) specifying
when Parse should stop trying to send the notication.

// Send push notification with expiration date


ParsePush push = new ParsePush();
push.setExpirationTime(1424841505);
push.setQuery(everyoneQuery);
push.setMessage("Season tickets on sale until
February 25th");
push.sendPushInBackground();

There is however a caveat with this method. Since


device clocks are not guaranteed to be accurate, you
may end up with inaccurate results. For this reason, the
ParsePush class also provides the
setExpirationTimeInterval method which accepts a
timeInterval (in seconds). The notication will expire
after the specied interval has elapsed.

// Create time interval


long weekInterval = 60*60*24*7; // 1 week
// Send push notification with expiration
interval
ParsePush push = new ParsePush();
push.setExpirationTimeInterval(weekInterval);
push.setQuery(everyoneQuery);
push.setMessage("Season tickets on sale until
next week!");
push.sendPushInBackground();

TARGETING BY PLATFORM

If you build a cross platform app, it is possible you may


only want to target devices of a particular operating
system. Advanced Targeting allow you to lter which of
these devices are targeted.
The following example would send a dierent
notication to Android, iOS, and Windows users.

ParseQuery query =
ParseInstallation.getQuery();
query.whereEqualTo("channels",
"suitcaseOwners");
// Notification for Android users
query.whereEqualTo("deviceType", "android");
ParsePush androidPush = new ParsePush();
androidPush.setMessage("Your suitcase has been
filled with tiny robots!");
androidPush.setQuery(query);
androidPush.sendPushInBackground();
// Notification for iOS users
query.whereEqualTo("deviceType", "ios");
ParsePush iOSPush = new ParsePush();

iOSPush.setMessage("Your suitcase has been


filled with tiny apples!");
iOSPush.setQuery(query);
iOSPush.sendPushInBackground();
// Notification for Windows 8 users
query.whereEqualTo("deviceType", "winrt");
ParsePush winPush = new ParsePush();
winPush.setMessage("Your suitcase has been
filled with tiny glass!");
winPush.setQuery(query);
winPush.sendPushInBackground();
// Notification for Windows Phone 8 users
query.whereEqualTo("deviceType", "winphone");
ParsePush wpPush = new ParsePush();
wpPush.setMessage("Your suitcase is very hip;
very metro.");
wpPush.setQuery(query);
wpPush.sendPushInBackground();

Scheduling Pushes
Sending scheduled push notications is not currently
supported by the Android SDK. Take a look at the REST
API, JavaScript SDK or the Parse.com push console.

Receiving Pushes
Make sure you've gone through the Android Push
QuickStart to set up your app to receive pushes.
When a push notication is received, the title is
displayed in the status bar and the alert is displayed
alongside the title when the user expands the
notication drawer. If you choose to subclass
com.parse.ParsePushBroadcastReceiver , be sure to
replace that name with your class' name in the

registration.
Note that some Android emulators (the ones without
Google API support) don't support GCM, so if you test
your app in an emulator make sure to select an
emulator image that has Google APIs installed.
CUSTOMIZING NOTIFICATIONS
CUSTOMIZING NOTIFICATION ICONS

The Android style guide recommends apps use a push


icon that is monochromatic and at. The default push
icon is your application's launcher icon, which is unlikely
to conform to the style guide. To provide a custom push
icon, add the following metadata tag to your app's
AndroidManifest.xml :

<meta-data
android:name="com.parse.push.notification_icon"
android:resource="@drawable/push_icon"/>

where push_icon is the name of a drawable resource


in your package. If your application needs more than
one small icon, you can override getSmallIconId in
your ParsePushBroadcastReceiver subclass.
If your push has a unique context associated with an
image, such as the avatar of the user who sent a
message, you can use a large push icon to call attention
to the notication. When a notication has a large push
icon, your app's static (small) push icon is moved to the
lower right corner of the notication and the large icon
takes its place. See the Android UI documentation for
examples. To provide a large icon, you can override
getLargeIcon in your ParsePushBroadcastReceiver
subclass.
RESPONDING WITH A CUSTOM ACTIVITY

If your push has no "uri" parameter, onPushOpen will


invoke your application's launcher activity. To customize
this behavior, you can override getActivity in your
ParsePushBroadcastReceiver subclass.
RESPONDING WITH A URI

If you provide a "uri" eld in your push, the


ParsePushBroadcastReceiver will open that URI when
the notication is opened. If there are multiple apps
capable of opening the URI, a dialog will displayed for
the user. The ParsePushBroadcastReceiver will
manage your back stack and ensure that clicking back
from the Activity handling URI will navigate the user
back to the activity returned by getActivity .
MANAGING THE PUSH LIFECYCLE

The push lifecycle has three phases:


1

A notication is received and the


com.parse.push.intent.OPEN Intent is red,
causing the ParsePushBroadcastReceiver to call
onPushReceive . If either "alert" or "title" are
specied in the push, then a Notication is
constructed using getNotification . This
Notication uses a small icon generated using
getSmallIconId , which defaults to the icon
specied by the
com.parse.push.notification_icon metadata
in your AndroidManifest.xml . The Notication's
large icon is generated from getLargeIcon which
defaults to null. The notication's contentIntent
and deleteIntent are
com.parse.push.intent.OPEN and
com.parse.push.intent.DELETE respectively.

If the user taps on a Notication, the


com.parse.push.intent.OPEN Intent is red. The
ParsePushBroadcastReceiver calls onPushOpen .
The default implementation automatically sends
an analytics event back to Parse tracking that this
notication was opened. If the push contains a
"uri" parameter, an activity is launched to
navigate to that URI, otherwise the activity

returned by getActivity is launched.


3

If the user dismisses a Notication, the


com.parse.push.intent.DELETE Intent is red.
The ParsePushBroadcastReceiver calls
onPushDismiss , which does nothing by default

All of the above methods may be subclassed to


customize the way your application handles push
notications. When subclassing the methods
onPushReceive , onPushOpen , onPushDismiss , or
getNotification , consider delegating to super where
appropriate. For example, one might override
onPushReceive to trigger a background operation for
"silent" pushes and then delegate to super for all other
pushes. This provides the most benet from Parse Push
and makes your code forward-compatible.
TRACKING PUSHES AND APP OPENS

The default implementation of onPushOpen will


automatically track user engagment from pushes. If you
choose not to use the ParsePushBroadcastReceiver
or override the onPushOpen implementation, you may
need to track your app open event manually. To do this,
add the following to the onCreate method of the
Activity or the onReceive method of the
BroadcastReceiver which handles the
com.parse.push.intent.OPEN Intent:

ParseAnalytics.trackAppOpened(getIntent());

To track push opens, you should always pass the


Intent to trackAppOpened . Passing null to
trackAppOpened will track only a standard app-opened
event, not the push-opened event. If you don't track the
push-opened event, you will not be able to use
advanced analytics features such as push-open graphs
and A/B testing.

Please be sure to set up your application to save the


Installation object. Push open tracking only works
when your application's devices are associated with
saved Installation objects.
You can view the open rate for a specic push
notication on your Parse.com push console. You can
also view your application's overall app open and push
open graphs on the Parse analytics console. Our push
open analytics graphs are rendered in real time, so you
can easily verify that your application is sending the
correct analytics events before your next release.

Push Experiments
You can A/B test your push notications to gure out
the best way to keep your users engaged. With A/B
testing, you can simultaneously send two versions of
your push notication to dierent devices, and use each
version's push open rates to gure out which one is
better. You can test by either message or send time.
A/B TESTING

Our web push console guides you through every step of


setting up an A/B test.
For each push campaign sent through the Parse web
push console, you can allocate a subset of your devices
to be in the experiment's test audience, which Parse
will automatically split into two equally-sized
experiment groups. For each experiment group, you can
specify a dierent push message. The remaining
devices will be saved so that you can send the winning
message to them later. Parse will randomly assign
devices to each group to minimize the chance for a test
to aect another test's results (although we still don't
recommend running multiple A/B tests over the same
devices on the same day).

After you send the push, you can come back to the
push console to see in real time which version resulted
in more push opens, along with other metrics such as
statistical condence interval. It's normal for the
number of recipients in each group to be slightly
dierent because some devices that we had originally
allocated to that experiment group may have
uninstalled the app. It's also possible for the random
group assignment to be slightly uneven when the test
audience size is small. Since we calculate open rate
separately for each group based on recipient count, this
should not signicantly aect your experiment results.

If you are happy with the way one message performed,


you can send that to the rest of your app's devices (i.e.
the Launch Group). This step only applies to A/B
tests where you vary the message.

Push experiments are supported on all recent Parse


SDKs (iOS v1.2.13+, Android v1.4.0+, .NET v1.2.7+).
Before running experiments, you must instrument your
app with push open tracking.
EXPERIMENT STATISTICS

Parse provides guidance on how to run experiments to


achieve statistically signicant results.
TEST AUDIENCE SIZE

When you setup a push message experiment, we'll


recommend the minimum size of your test audience.
These recommendations are generated through
simulations based on your app's historical push open
rates. For big push campaigns (e.g. 100k+ devices), this
recommendation is usually small subset of your devices.
For smaller campaigns (e.g. < 5k devices), this
recommendation is usually all devices. Using all devices
for your test audience will not leave any remaining
devices for the launch group, but you can still gain
valuable insight into what type of messaging works
better so you can implement similar messaging in your
next push campaign.
CONFIDENCE INTERVAL

After you send your pushes to experiment groups, we'll


also provide a statistical condence interval when your
experiment has collected enough data to have
statistically signicant results. This condence interval
is in absolute percentage points of push open rate (e.g.
if the open rates for groups A and B are 3% and 5%,
then the dierence is reported as 2 percentage points).
This condence interval is a measure of how much
dierence you would expect to see between the two
groups if you repeat the same experiment many times.
Just after a push send, when only a small number of
users have opened their push notications, the open
rate dierence you see between groups A and B could

be due to random chance, so it might not be


reproducible if you run the same experiment again.
After your experiment collects more data over time, we
become increasingly condent that the observed
dierence is a true dierence. As this happens, the
condence interval will become narrower, allowing us
to more accurately estimate the true dierence
between groups A and B. Therefore, we recommend
that you wait until there is enough data to generate a
statistical condence interval before deciding which
group's push is better.

Push Localization
Localizing your app's content is a proven way to drive
greater engagement. We've made it easy to localize
your push messages with Push Localization. The latest
version of the Parse Android SDK will detect and store
the user's language in the installation object, and via the
web push console youll be able to send localized push
messages to your users in a single broadcast.
SETUP

To take advantage of Push Localization you will need to


make sure you've published your app with the Parse
Android SDK version 1.10.1 or greater. Any users of your
application running the Parse Android SDK version
1.10.1 or greater will then be targetable by Push
Localization via the web push console.
It's important to note that for developers who have
users running apps with versions of the Parse Android
SDK earlier than 1.10.1 that targeting information for
Localized Push will not be available and these users will
receive the default message from the push console.
SENDING A LOCALIZED PUSH

Our web push console guides you through every step of


setting up a Localized Push.

Troubleshooting
Setting up Push Notications is often a source of
frustration for developers. The process is complicated
and invites problems to happen along the way. If you
run into issues, try some of these troubleshooting tips.
It's important to break down the system into
components when troubleshooting push notication
issues. You can start by asking yourself the following
questions:

+ Is the push notication listed in your push logs?


+ Are you targeting your push notication to the
correct audience?

+ Are your devices registering for push


notications?

+ Is your app set up to receive these notications?


If the answer is Yes to all of these questions, there may
be an operational issue that is aecting the delivery of
push notications. You can see the current status of the
platform in our status page. If everything looks green,
please create a bug report with more information.
Every bug report opened against the push notication
category will be redirected to this troubleshooting
guide unless it includes evidence of having gone
through these steps, so please be as verbose as
possible.
If you're unsure about the answer to any of the above
questions, read on!
CONFIRM THAT THE PUSH CAMPAIGN WAS CREATED

Having everything set up correctly in your Parse app


won't help if your request to send a push notication
does not reach Parse. The rst step in debugging a
push issue is to conrm that the push campaign is listed
in your push logs. You can nd these logs by visiting

your app's Dashboard and clicking on Push.


If the push notication campaign is not showing up on
that list, the issue is quite simple to resolve. Go back to
your push notication sending code and make sure to
check for any error responses. If you're using any of the
client SDKs, make sure to listen for and catch any errors
that may be returned. For example, you could log errors
like so:

push.sendPushInBackground(new SendCallback() {
public void done(ParseException e) {
if (e == null) {
Log.d("push", "The push campaign has
been created.");
} else {
Log.d("push", "Error sending push:" +
e.getMessage());
}
}
});

Please note that SDKs that use a Client Key, such as the
Android SDK, can only send push notications if Client
Push is enabled in your Parse app's Push Notication
settings. Otherwise, you'll only be able to send pushes
from the web console, the REST API, or by using the
JavaScript SDK from Cloud Code. We strongly
encourage developers to turn o Client Push before
releasing their app publicly unless your use case allows
for any amount of arbitrary pushes to be sent by any of
your users. You can read our security guide for more
information.
VERIFY YOUR TARGETING

You have conrmed that the push notication is making


it to your push logs. Now what? The next step is to

verify if your push notication targeting is correct. Say,


if you're debugging why notications are not reaching a
specic device, it's important to make sure that this
device is actually included in the targeting of this push
notication. You can't deliver something that is not
addressed correctly.
In order to do this, you'll have to conrm that the
device's ParseInstallation object is included in your
push notication targeting criteria. This is quite
straightforward if you're using channels: all you need to
verify is that your device's ParseInstallation is
subscribed to the channel by checking the channels
array. If you're using advanced targeting, e.g. you're
using a push query with constraints, you'll have to work
a bit more to determine if your targeting is on point.
Basically, you will need to run the same push query
you're using for your targeting, and verify that your
installation of interest is included in the result set. As
you can only query the installation class
programmatically using the Master Key, you will need to
use one of the Master Key capable SDKs (JavaScript
SDK, .NET) or the REST API. Ideally, you would use the
same SDK that you're currently using to send the push
notications.
DEBUGGING USING THE REST API

The REST API is quite easy to use for this sort of


purpose as you can easily recreate the push query using
the information provided in your push notication logs.
If you look closely at the Full Target value in your push
campaign log item, you may notice that it matches the
query format for a REST API query. You can grab an
example of what a REST API query over
ParseInstallation s would look like from the REST
API docs. Don't forget to use the X-Parse-Master-Key
header to ensure that the Master Key is used to run this
query.

# Query over installations


curl -X GET \
-H "X-Parse-Application-Id:
{YOUR_APPLICATION_ID}" \
-H "X-Parse-Master-Key: {YOUR_MASTER_KEY}" \
-G \
--data-urlencode 'limit=1000' \
--data-urlencode 'where={ "city": "San
Francisco", "deviceType": { "$in": [ "ios",
"android", "winphone", "embedded" ] } }' \
https://api.parse.com/1/installations

If you type the above into a console, you should be able


to see the rst 1,000 objects that match your query.
Note that constraints are always ANDed, so if you want
to further reduce the search scope, you can add a
constraint that matches the specic installation for
your device:

# Query over installations


curl -X GET \
-H "X-Parse-Application-Id:
{YOUR_APPLICATION_ID}" \
-H "X-Parse-Master-Key: {YOUR_MASTER_KEY}" \
-G \
--data-urlencode 'limit=1' \
--data-urlencode 'where={ objectId:
{YOUR_INSTALLATION_OBJECT_ID}, "city": "San
Francisco", "deviceType": { "$in": [ "ios",
"android", "winphone", "embedded" ] } }' \
https://api.parse.com/1/installations

If the above query returns no results, it is likely that


your installation does not meet the targeting criteria
for your campaign.

CHECK YOUR DEVICE CONFIGURATION

Your push campaign is created and the device is


included in the targeting, but you're still not receiving
push notications. What gives?
You can check the Push Delivery Report for the cause
of failed deliveries: Mismatch Sender ID happens when
the registered sender is not the same as the one
currently sending the notications, Not Registered is
returned when the registration key is deemed invalid,
either by misconguration or if the user have
uninstalled the app.
If everything looks great so far, but push notications
are not showing up on your phone, there are a few
more things you can check.

+ Upgrade to the latest SDK. This documentation


covers the push API introduced in the 1.7.0
version of the Android Parse SDK. Please upgrade
if you are getting compiler errors following these
instructions.

+ Make sure you are using the correct


packageName in your AndroidManifest.xml .

+ Make sure you have the correct permissions


listed in your AndroidManifest.xml le, as
outlined in the Android Push Quickstart. If you
are using a a custom receiver, be sure you have
registered it in the Manifest le with the correct
android:name property and the proper intent
lters.

+ Make sure you've used the correct App ID and


client key, and that Parse.initialize() is being
called. Parse.initialize() lets the service
know which application it is listening for; this
code must be in your Application.onCreate
rather than Activity.onCreate for a particular
Activity , so that any activation technique will
know how to use Parse.

+ Check that the push registration call is being


called successfully. Your device must successfully
register a ParseInstallation object with a valid

GCM Registration id in the "deviceToken" eld, if


using GCM.

+ Check that the device is set to accept push


notications from your app.

+ Note that, by design, force-killed apps will not be


able to receive push notications. Launch the app
again to reenable push notications.

+ Check the number of subscribers in your Parse


Push Console. Does it match the expected
number of subscribers? Your push might be
targeted incorrectly.

+ If testing in the emulator, try cleaning and


rebuilding your project and restarting your AVD.

+ Turn on verbose logging with


Parse.setLogLevel(Parse.LOG_LEVEL_VERBOSE) .
The error messages will be a helpful guide to
what may be going on.

+ If you see the message "Finished (with error)" in


your Dashboard, check your verbose logs. If you
are performing pushes from a device, check that
client-side push is enabled in your dashboard.

+ In your logs, you may see an error message,


"Could not construct writer" or other issues
related to a broken pipe. When this occurs, the
framework will continue to try reconnecting. It
should not crash your app.
If your app has been released for a while, it is expected
that a percentage of your user base will have opted out
of push notications from your app or uninstalled your
app from their device. Parse does not automatically
delete installation objects in either of these cases.
When a push campaign is sent out, Parse will detect
uninstalled installations and exclude them from the
total count of push notications sent. The recipient
estimate on the push composer is based on the
estimated number of installations that match your
campaign's targeting criteria. With that in mind, it is
possible for the recipient estimate to be higher than
the number of push notications that is sent as

reported by the push campaign status page.


Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Cong
Parse Config
ParseConfig is a way to congure your applications
remotely by storing a single conguration object on
Parse. It enables you to add things like feature gating or
a simple "Message of the Day". To start using
ParseConfig you need to add a few key/value pairs
(parameters) to your app on the Parse Cong
Dashboard.

After that you will be able to fetch the ParseConfig on


the client, like in this example:

ParseConfig.getInBackground(new
ConfigCallback() {
@Override
public void done(ParseConfig config,
ParseException e) {
int number =
config.getInt("winningNumber");
Log.d("TAG", String.format("Yay! The
number is %d!", number));
}

});

Retrieving Config
ParseConfig is built to be as robust and reliable as
possible, even in the face of poor internet connections.
Caching is used by default to ensure that the latest
successfully fetched cong is always available. In the
below example we use getInBackground to retrieve
the latest version of cong from the server, and if the
fetch fails we can simply fall back to the version that we
successfully fetched before via getCurrentConfig .

Log.d("TAG", "Getting the latest config...");


ParseConfig.getInBackground(new
ConfigCallback() {
@Override
public void done(ParseConfig config,
ParseException e) {
if (e == null) {
Log.d("TAG", "Yay! Config was fetched
from the server.");
} else {
Log.e("TAG", "Failed to fetch. Using
Cached Config.");
config = ParseConfig.getCurrentConfig();
}
// Get the message from config or fallback
to default value
String welcomeMessage =
config.getString("welcomeMessage",
"Welcome!");
Log.d("TAG", String.format("Welcome
Messsage From Config = %s", welcomeMessage));
}
});

Current Config
Every ParseConfig instance that you get is always
immutable. When you retrieve a new ParseConfig in
the future from the network, it will not modify any
existing ParseConfig instance, but will instead create a
new one and make it available via
ParseConfig.getCurrentConfig() . Therefore, you can
safely pass around any ParseConfig object and safely
assume that it will not automatically change.
It might be troublesome to retrieve the cong from the
server every time you want to use it. You can avoid this
by simply using the cached getCurrentConfig object
and fetching the cong only once in a while.

class Helper {
private static final long
configRefreshInterval = 12 * 60 * 60 * 1000;
private static long lastFetchedTime;
// Fetches the config at most once every 12
hours per app runtime
public static void refreshConfig() {
long currentTime =
System.currentTimeMillis();
if (currentTime - lastFetchedTime >
configRefreshInterval) {
lastFetchedTime = currentTime;
ParseConfig.getInBackground();
}
}
}

Parameters
ParseConfig supports most of the data types
supported by ParseObject :

+ String
+ Numbers (boolean/int/double/long)
+ Date
+ ParseFile
+ ParseGeoPoint
+ List
+ Map
We currently allow up to 100 parameters in your cong
and a total size of 128KB across all parameters.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Analytics
Parse provides a number of hooks for you to get a
glimpse into the ticking heart of your app. We
understand that it's important to understand what your
app is doing, how frequently, and when.
While this section will cover dierent ways to
instrument your app to best take advantage of Parse's
analytics backend, developers using Parse to store and
retrieve data can already take advantage of metrics on
Parse.
Without having to implement any client-side logic, you
can view real-time graphs and breakdowns (by device
type, Parse class name, or REST verb) of your API
Requests in your app's dashboard and save these graph
lters to quickly access just the data you're interested
in.

App-Open & Push Analytics


Our initial analytics hook allows you to track your
application being launched. By adding the following line
to the onCreate method of your main Activity , you'll
begin to collect data on when and how often your
application is opened.

ParseAnalytics.trackAppOpenedInBackground(getIntent

Graphs and breakdowns of your statistics are


accessible from your app's Dashboard.
Further analytics are available around push notication
delivery and open rates. Take a look at the Tracking
Pushes and App Opens subsection of our Push Guide
for more detailed information on handling notication
payloads and push-related callbacks.

Custom Analytics
ParseAnalytics also allows you to track free-form
events, with a handful of String keys and values.
These extra dimensions allow segmentation of your
custom events via your app's Dashboard.
Say your app oers search functionality for apartment
listings, and you want to track how often the feature is
used, with some additional metadata.

Map<String, String> dimensions = new


HashMap<String, String>();
// Define ranges to bucket data points into
meaningful segments
dimensions.put("priceRange", "1000-1500");
// Did the user filter the query?

dimensions.put("source", "craigslist");
// Do searches happen more often on weekdays
or weekends?
dimensions.put("dayType", "weekday");
// Send the dimensions to Parse along with the
'search' event
ParseAnalytics.trackEvent("search",
dimensions);

ParseAnalytics can even be used as a lightweight


error tracker simply invoke the following and you'll
have access to an overview of the rate and frequency of
errors, broken down by error code, in your application:

Map<String, String> dimensions = new


HashMap<String, String>();
dimensions.put('code',
Integer.toString(error.getCode()));
ParseAnalytics.trackEvent('error',
dimensions);

Note that Parse currently only stores the rst eight


dimension pairs per call to
ParseAnalytics.trackEvent() .
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

User Interface
At the end of the day, users of your application will be
interacting with Android UI components. We provide

several UI widgets to make working with Parse data


easier.

ParseLoginUI
If you are using Parse to manage users in your mobile
app, you are already familiar with the ParseUser class.
At some point in your app, you might want to present a
screen to log in your ParseUser . Parse provides an
open-source ParseLoginUI library that does exactly
this. Please note ParseLoginUI is not included with the
Parse Android SDK; you need to import this library from
maven or from our Git repository into your project.
Check the README.md of the ParseLoginUI for detail
steps.
This library contains an Android login activity that is
ultra-customizable and easy to integrate with your app.
You can congure the look and feel of the login screens
by either specifying XML congurations or constructing
an Intent in code. In this guide, we rst provide several
ways to integrate with the login library. Then, we
describe in detail how to customize the login screens.

Login Library API


BASIC INTEGRATION

The login library integration is simple. Our library


exposes an activity-level API that lets you launch the

login library activity ( ParseLoginActivity ) to obtain a


ParseUser . ParseLoginActivity will guide the user
through several screens to log in, sign up, or reset their
password. If the user resets their password by email,
they are taken back to the login screen. The user will
also see helpful toast messages if they provide invalid
input (e.g. logging in with an incorrect password or
signing up with a username that's already taken).
To include ParseLoginActivity in your app, import the
ParseLoginUI library project, and add the following into
your AndroidManifest.xml :

<activity
android:name="com.parse.ui.ParseLoginActivity"
android:label="@string/app_name"
android:launchMode="singleTop">
<meta-data
android:name="com.parse.ui.ParseLoginActivity.PARSE_LOGIN_ENABLED"
android:value="true"/>
</activity>

Then, you can launch ParseLoginActivity from your


own activity by calling:

ParseLoginBuilder builder = new


ParseLoginBuilder(MyActivity.this);
startActivityForResult(builder.build(), 0);

When ParseLoginActivity nishes, your caller activity


will receive either:

RESULT_OK : The user successfully logged in.

ParseUser.getCurrentUser() will be populated.

RESULT_CANCELLED : The user pressed the back


button. If the user fails to log in or sign up, the
only way to return to the previous screen is by
pressing the back button.

We've provided a sample app, ParseLoginSampleBasic


for this use case. This sample app is a simple prole
viewer. If you are not logged in,
SampleProfileActivity prompts you to log in.
Clicking on the login button in SampleProfileActivity
launches the ParseLoginActivity , which prompts the
user for login credentials. If the user successfully logs
in, ParseLoginActivity will automatically nish itself
and return RESULT_OK . Then, SampleProfileActivity
will display the user's name and a logout button.

This basic integration case works well if your caller


activity is designed to function regardless of whether
there is a valid current user. For example, a restaurant
reviews app may allow the user to browse restaurants
even when the user is not logged in. If the user does log
in, the app could provide a more personalized
experience on that same screen.
ADVANCED INTEGRATION

If some parts or all of your app cannot function without


a valid ParseUser , you can protect these parts of your
app with a ParseLoginDispatchActivity (supplied in
this library project). This dispatch activity acts like a
gatekeeper; it automatically launches
ParseLoginActivity if no user is logged in, or

launches the protected activity if a user is already


logged in. To use this, you subclass
ParseLoginDispatchActivity and specify what
protected activity to launch.

public class SampleDispatchActivity extends


ParseLoginDispatchActivity {
@Override
protected Class<?> getTargetClass() {
return SampleProfileActivity.class;
}
}

We've provided another sample app,


ParseLoginSampleWithDispatchActivity for this use
case. The SampleProfileActivity in this app cannot
function without a logged-in user, so it is protected by
SampleDispatchActivity .

The dispatch activity does not necessarily need to be


the rst activity at app launch. You can launch the
dispatch activity from any activity. When your
protected activity nishes, the dispatch activity will
automatically forward the result code to your caller
activity.

Let's revisit the restaurant reviews app again. You might


have a comment activity that requires a user. You can
protect this activity behind a dispatch activity. The main
restaurant listing activity (supports either user or no
user), can launch the dispatch activity when the user
presses the comment button. If no user is logged in, the
dispatch activity will start ParseLoginActivity to
obtain a ParseUser. If the user refuses to log in, they
will be gracefully taken back to the restaurant listings
activity. In the restaurant listings activity, you can
always call ParseUser.getCurrentUser() to determine
whether the user logged in.

Customizing the Login Library


There are three ways to customize the login library:

+ Activity metadata in AndroidManifest.xml - This


is most recommended because it allows you to
customize the login experience without writing
any code, and it makes the login experience
consistent regardless which activity in your app
launches ParseLoginActivity .

+ In code with ParseLoginBuilder - If you love


writing code to do everything.

+ Overriding layout resource XMLs - This option is


useful if you want to make signicant changes to
the look and feel of the login experience.
CONFIGURE BY ACTIVITY METADATA

We provide the following options for easily customizing


the ParseLoginActivity in your app's
AndroidManifest.xml :

APP_LOGO : Drawable` resource for app logo.

PARSE_LOGIN_ENABLED : Boolean for whether to


enable the Parse username/password login
(default = false)

PARSE_LOGIN_BUTTON_TEXT : String to display on


the login button (default = Log in)

PARSE_SIGNUP_BUTTON_TEXT : String to display


on the signup button on the login screen (default
= "Sign up")

PARSE_LOGIN_HELP_TEXT : String to display on


the password-reset link (default = "Forgotten
password")

PARSE_LOGIN_INVALID_CREDENTIALS_TEXT :
String to show on the toast when the user login
fails (default = "The username and password you
entered don't match")

PARSE_LOGIN_EMAIL_AS_USERNAME : Boolean for


whether to prompt for the user's email as the
username on the login and signup form (default =
false)

PARSE_SIGNUP_MIN_PASSWORD_LENGTH : Integer
for the minimum required password length on
the signup form (default = 6)

PARSE_SIGNUP_SUBMIT_BUTTON_TEXT : String to
display on the submit button on the signup
screen (default = "Submit")

FACEBOOK_LOGIN_ENABLED : Boolean for whether


to show the Facebook login button (default =
false)

FACEBOOK_LOGIN_BUTTON_TEXT : String to
display on the Facebook login button (default =
"Log in with Facebook")

FACEBOOK_LOGIN_PERMISSIONS : String array


resource containing requested Facebook
permissions (default = empty)

TWITTER_LOGIN_ENABLED : Boolean for whether


to show the Twitter login button (default = false)

TWITTER_LOGIN_BUTTON_TEXT : String to display


on the Twitter login button (default = "Log in
with Twitter")

Please note that PARSE_LOGIN_ENABLED ,


FACEBOOK_LOGIN_ENABLED , and
TWITTER_LOGIN_ENABLED are all false by default. You
need to explicitly set them to true for those
components to show up on the screen. For APP_LOGO
and FACEBOOK_LOGIN_PERMISSIONS , make sure you use
android:resource instead of android:value .
Example conguration:

<activity
android:name="com.parse.ui.ParseLoginActivity"
android:label="@string/my_app_name"
android:launchMode="singleTop">
<!-- We reference a drawable resource
here, so we must use android:resource -->
<meta-data
android:name="com.parse.ui.ParseLoginActivity.APP_LOGO"

android:resource="@drawable/my_app_logo"/>
<!-- For these non-resource options, use
android:value -->
<meta-data
android:name="com.parse.ui.ParseLoginActivity.PARSE_LOGIN_ENABLED"
android:value="true"/>
<meta-data

android:name="com.parse.ui.ParseLoginActivity.PARSE_LOGIN_EMAIL_AS_USE

android:value="true"/>
<meta-data
android:name="com.parse.ui.ParseLoginActivity.PARSE_LOGIN_HELP_TEXT"

android:value="@string/password_reset_text"/>
<meta-data
android:name="com.parse.ui.ParseLoginActivity.MIN_PASSWORD_LENGTH"
android:value="8"/>
<meta-data
android:name="com.parse.ui.ParseLoginActivity.FACEBOOK_LOGIN_ENABLED"
android:value="true"/>
<!-- We reference a string-array resource
here, so we must use android:resource -->
<meta-data

android:name="com.parse.ui.ParseLoginActivity.FACEBOOK_LOGIN_PERMISSIO

android:resource="@array/my_facebook_permissions"
</activity>

For the Facebook permission array, you also need to


have this in your res/values/strings.xml :

<resources>
<string-array
name="my_facebook_permissions">
<item>public_profile</item>
<item>user_friends</item>

</string-array>
</resources>

CONFIGURE BY CODE

You can congure the ParseLoginActivity by code


using the ParseLoginBuilder. You specify the options on
the builder, and then call build() to generate an
Intent that can be used to start the
ParseLoginActivity . We've provided a sample app,
ParseLoginSampleCodeCustomization demonstrating
this use case. The options in ParseLoginBuilder are
the same as those in activity metadata customization. If
you specify options in both code and activity metadata,
the options in code take precedence.

ParseLoginBuilder builder = new


ParseLoginBuilder(ProfileActivity.this);
Intent parseLoginIntent =
builder.setAppLogo(R.drawable.my_app_logo)
.setParseLoginEnabled(true)
.setParseLoginButtonText("Go")
.setParseSignupButtonText("Register")
.setParseLoginHelpText("Forgot password?")
.setParseLoginInvalidCredentialsToastText("You
email and/or password is not correct")
.setParseLoginEmailAsUsername(true)
.setParseSignupSubmitButtonText("Submit
registration")
.setFacebookLoginEnabled(true)
.setFacebookLoginButtonText("Facebook")
.setFacebookLoginPermissions(Arrays.asList("public_profile"
"user_friends"))
.setTwitterLoginEnabled(true)
.setTwitterLoginButtontext("Twitter")
.build();

startActivityForResult(parseLoginIntent, 0);

CONFIGURE BY OVERRIDING LAYOUT RESOURCE


FILES

You can override any layout resources by having les


with the same name as those in the ParseLoginUI
library project. This is useful if you want to add a
background image, or reposition the login components
on the screen. The Android build process will
automatically merge resource les with the same name,
giving your app project's les precedence. The top-level
layout les are:

com_parse_ui_parse_login_fragment.xml : If
you do not use certain login methods
(username/password, Facebook, or Twitter), you
can remove the corresponding UI elements from
this layout.

com_parse_ui_parse_signup_fragment.xml : You
can add additional input elds in the signup form
here. If you do, you also need add code to
ParseSignupFragment to copy that data into the
ParseUser object.

com_parse_ui_parse_login_help_fragment.xml :
You can change the message for password reset.

We've provided another sample app,


ParseLoginSampleLayoutOverride showing how to do
this. This sample app only has a Facebook login button
in com_parse_ui_parse_login_fragment.xml , and
adds a background image to the login screens.

ParseQueryAdapter
To display collections of data, we provide an
implementation of Adapter in the Parse Android SDK.
Instead of using a basic ListAdapter backed by a static
array of objects, our ParseQueryAdapter provides a

layer of abstraction and allows you to easily display data


from one of your Parse classes in your AdapterView of
choice (e.g. ListView or GridView ).
To use a ParseQueryAdapter to display data in an
Activity , follow the steps outlined below in your
Activity 's onCreate :
1

Instantiate a ParseQueryAdapter .

Customize it as necessary (see the below


subsections for detailed instructions to display
data from specic queries, change the UI of the
View s to be displayed, and more).

Set your new Adapter on your AdapterView


with setAdapter() .

When the AdapterView is attached to the window, your


ParseQueryAdapter will automatically fetch the rst
set of data. This subclass simplies the code that would
otherwise be involved with:
1

Pagination, with a row that can be tapped to load


the next page.

Congurable downloading and displaying of


remote images within rows.

Automatic loading and management of the Parse


objects array.

Callbacks from major events in the data cycle.

Consider the following code, which sets up a very


simple ParseQueryAdapter to display data in a
ListView . You can be up and running with a functional
ListView full of data with very little conguration.

// Inside an Activity
public void onCreate(Bundle
savedInstanceState) {

super.onCreate(savedInstanceState);
// Uses a layout with a ListView (id:
"listview"), which uses our Adapter.
setContentView(R.layout.main);
ParseQueryAdapter<ParseObject> adapter = new
ParseQueryAdapter<ParseObject>(this,
"Instrument");
adapter.setTextKey("name");
adapter.setImageKey("photo");
ListView listView = (ListView)
findViewById(R.id.listview);
listView.setAdapter(adapter);
}

This view will display a list of Instruments by name.


Notice all the code that we're not writing: we can skip
the logic to fetch each consecutive page of results, to
manually update and maintain the backing data array, to
download images in the background and set the image
data on UI widgets, and to handle touch events to
trigger loading the next page of results.
The ParseQueryAdapter can be congured to
customize what data to use, how to display it, and what
to do before and after it's been fetched. Read on to see
what you can do, and how to tweak a
ParseQueryAdapter to t all of your needs.

Customizing the Query


By default, the simplest ParseQueryAdapter
constructor takes a Context and a Parse class name.
All ParseObject s in that class are then fetched and
displayed in order of their createdAt timestamps.
To change this behavior, we drew from the functionality

of an ArrayAdapter : but instead of taking in a vanilla


array of objects to be displayed by the adapter,
ParseQueryAdapter can also take a QueryFactory
class which returns a ParseQuery you dene. Pass that
into the constructor, and the adapter will then use that
query to determine which objects to fetch and display.
See below for an example setting up a
ParseQueryAdapter to display only punk and metal
bands with four or more members, ordered by number
of records sold:

ParseQueryAdapter<ParseObject> adapter =
new ParseQueryAdapter<ParseObject>(this, new
ParseQueryAdapter.QueryFactory<ParseObject>()
{
public ParseQuery<ParseObject> create() {
// Here we can configure a ParseQuery to
our heart's desire.
ParseQuery query = new
ParseQuery("Band");
query.whereContainedIn("genre",
Arrays.asList({ "Punk", "Metal" }));
query.whereGreaterThanOrEqualTo("memberCount",
4);
query.orderByDescending("albumsSoldCount");
return query;
}
});

Customizing the Rows


The default layout for the individual View s in your
AdapterView is a simple LinearLayout with a
ParseImageView and a TextView . If
setTextKey(String) is used with the

ParseQueryAdapter , its parameter will be used to


select which key on your ParseObject is displayed in
the TextView . Similarly, if setImageKey(String) is
used, its parameter will be used to determine the image
displayed in the ImageView.
One way to customize the rows is to override
getItemView(ParseObject, View, ViewGroup) or
getNextPageView(View, ViewGroup) and call the
superclass's implementation of the appropriate method
to do the heavy lifting. If you provide your own layout to
the superclass's implementation, note that
getItemView(ParseObject, View, ViewGroup) and
getNextPageView(View, ViewGroup) expect a
TextView (id: android.R.id.text1 ) if the textKey is
set and a ParseImageView (id: android.R.id.icon ) if
the imageKey is set.
Here, we inate and congure a layout of our own, with
a TextView , a ParseImageView , and an extra
"description" TextView (id: R.id.description ):

@Override
public View getItemView(ParseObject object,
View v, ViewGroup parent) {
if (v == null) {
v = View.inflate(getContext(),
R.layout.adapter_item, null);
}
// Take advantage of ParseQueryAdapter's
getItemView logic for
// populating the main TextView/ImageView.
// The IDs in your custom layout must match
what ParseQueryAdapter expects
// if it will be populating a TextView or
ImageView for you.
super.getItemView(object, v, parent);

// Do additional configuration before


returning the View.
TextView descriptionView = (TextView)
v.findViewById(R.id.description);
descriptionView.setText(object.getString("description"
return v;
}

Another way to customize the rows is to have complete


control over the look of the rows by overriding
ParseQueryAdapter 's methods and ignoring the
superclass's implementation entirely. In this example,
our item views are simply rows where the color is
dened by the ParseObject :

@Override
public View getItemView(ParseObject object,
View v, ViewGroup parent) {
if (v == null) {
v = View.inflate(getContext(),
R.layout.adapter_item, null);
}
v.setBackgroundColor(object.getInt("color"));
return v;
}
@Override
public View getNextPageView(View v, ViewGroup
parent) {
if (v == null) {
// R.layout.adapter_next_page contains an
ImageView with a custom graphic
// and a TextView.
v = View.inflate(getContext(),

R.layout.adapter_next_page, null);
}
TextView textView = (TextView)
v.findViewById(R.id.nextPageTextViewId);
textView.setText("Loaded " + getCount() + "
rows. Get more!");
return v;
}

Loading Remote Images in Rows


ParseQueryAdapter makes it simple to display remote
images. By calling setImageKey(String) , you can pass
in a key name on your ParseObject which should
contain a ParseFile containing an image to be fetched
from Parse and loaded into the ParseImageView of the
corresponding row.
The image will download asynchronously, and the
appropriate ParseImageView will be updated in the
background. As the user scrolls and rows are recycled
by the adapter, images will be fetched as rows become
visible and assigned ParseObject s.
You can dene a placeholder image to be used when
the image fetch has not yet completed. Call
setPlaceholder(Drawable) on your
ParseQueryAdapter to use the specied Drawable as a
fallback image.

Lifecycle Methods
We expose two hooks in the data lifecycle of the
Adapter for you to execute custom logic right before
we query Parse for your data and right after the
fetched objects have been loaded from the query.
These methods are particularly useful for toggling
some loading UI.

An OnQueryLoadListener can be set via


setOnQueryLoadListener(OnQueryLoadListener) ,
which provides onLoading() and
onLoaded(List<ParseObject>, Exception) methods
for implementation.

Pagination
Pagination ensures that the table only gets one page of
objects at a time. You can set the number of objects are
in a page by calling setObjectsPerPage(int) .
The query is automatically altered to apply pagination,
and a pagination row appears at the bottom of the
AdapterView to allow users to load the next page.
Pagination is turned on by default. To turn it o, call
setPaginationEnabled(false) . With pagination
turned o, the ParseQueryAdapter will use the default
ParseQuery limit of 100 items.

Auto-loading of Data
When the AdapterView that your ParseQueryAdapter
is set on is attached to a window, the
ParseQueryAdapter 's loadObjects() method is
automatically called, triggering the fetching of the rst
page of results. To disable this behavior (perhaps to
delay the fetching of data, or run some custom logic
ahead of time), just call setAutoload(false) and call
loadObjects() manually if autoload is disabled.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Data
We've designed the Parse SDKs so that you typically
don't need to worry about how data is saved while
using the client SDKs. Simply add data to the
ParseObject , and it'll be saved correctly.
Nevertheless, there are some cases where it's useful to
be aware of how data is stored on the Parse platform.

Data Storage
Internally, Parse stores data as JSON, so any datatype
that can be converted to JSON can be stored on Parse.
Refer to the Data Types in Objects section of this
guide to see platform-specic examples.
Keys including the characters $ or . , along with the
key __type key, are reserved for the framework to
handle additional types, so don't use those yourself. Key
names must contain only numbers, letters, and
underscore, and must start with a letter. Values can be
anything that can be JSON-encoded.

Data Type Lock-in


When a class is initially created, it doesn't have an
inherent schema dened. This means that for the rst
object, it could have any types of elds you want.
However, after a eld has been set at least once, that
eld is locked into the particular type that was saved.
For example, if a ParseUser object is saved with eld
name of type String , that eld will be restricted to the
String type only (the server will return an error if you
try to save anything else).
One special case is that any eld can be set to null , no
matter what type it is.

The Data Browser


The Data Browser is the web UI where you can update
and create objects in each of your apps. Here, you can
see the raw JSON values that are saved that represents
each object in your class.
When using the interface, keep in mind the following:

+ The objectId , createdAt , updatedAt elds


cannot be edited (these are set automatically).

+ The value "(empty)" denotes that the eld has


not been set for that particular object (this is
dierent than null ).

+ You can remove a eld's value by hitting your


Delete key while the value is selected.
The Data Browser is also a great place to test the Cloud
Code validations contained in your Cloud Code
functions (such as beforeSave ). These are run
whenever a value is changed or object is deleted from
the Data Browser, just as they would be if the value was
changed or deleted from your client code.

Importing Data
You may import data into your Parse app by using CSV
or JSON les. To create a new class with data from a
CSV or JSON le, go to the Data Browser and click the
"Import" button on the left hand column.
The JSON format is an array of objects in our REST
format or a JSON object with a results that contains
an array of objects. It must adhere to the JSON
standard. A le containing regular objects could look
like:

{ "results": [

{
"score": 1337,
"playerName": "Sean Plott",
"cheatMode": false,
"createdAt": "2012-07-11T20:56:12.347Z",
"updatedAt": "2012-07-11T20:56:12.347Z",
"objectId": "fchpZwSuGG"
}]
}

Objects in either format should contain keys and values


that also satisfy the following:

+ Key names must contain only numbers, letters,


and underscore, and must start with a letter.

+ No value may contain a hard newline ' \n '.


Normally, when objects are saved to Parse, they are
automatically assigned a unique identier through the
objectId eld, as well as a createdAt eld and
updatedAt eld which represent the time that the
object was created and last modied in the Parse
Cloud. These elds can be manually set when data is
imported from a JSON le. Please keep in mind the
following:

+ Use a unique 10 character alphanumeric string as


the value of your objectId elds.

+ Use a UTC timestamp in the ISO 8601 format


when setting a value for the createdAt eld or
the updatedAt eld.
In addition to the exposed elds, objects in the Parse
User class can also have the bcryptPassword eld set.
The value of this eld is a String that is the bcrypt
hashed password + salt in the modular crypt format
described in this StackOverow answer. Most
OpenSSL based bcrypt implementations should have

built-in methods to produce these strings.


A le containing a ParseUser object could look like:

{ "results":
[{
"username": "cooldude",
"createdAt": "1983-09-13T22:42:30.548Z",
"updatedAt": "2015-09-04T10:12:42.137Z",
"objectId": "ttttSEpfXm",
"sessionToken":
"dfwfq3dh0zwe5y2sqv514p4ib",
"bcryptPassword":
"$2a$10$ICV5UeEf3lICfnE9W9pN9.O9Ved/ozNo7G83Qbdk5rmyvY8l16MIK"
}]
}

Note that in CSV the import eld types are limited to


String , Boolean , and Number .

Exporting your Data


You can request an export of your data at any time
from your app's Settings page. The data export runs at
a lower priority than production queries, so if your app
is still serving queries, production trac will always be
given a higher priority, which may slow down the
delivery of your data export.
EXPORT FORMATS

Each collection will be exported in the same JSON


format used by our REST API and delivered in a single
zipped le. Since data is stored internally as JSON, this
allows us to ensure that the export closely matches
how the data is saved to Parse. Other formats such as
CSV cannot represent all of the data types supported

by Parse without losing information. If you'd like to


work with your data in CSV format, you can use any of
the JSON-to-CSV converters available widely on the
web.
OFFLINE ANALYSIS

For oine analysis of your data, we highly recommend


using alternate ways to access your data that do not
require extracting the entire collection at once. For
example, you can try exporting only the data that has
changed since your last export. Here are some ways of
achieving this:

+ Use the JavaScript SDK in a node app.


Parse.Query.each() will allow you to extract
every single object that matches a query. You can
use date constraints to make sure the query only
matches data that has been updated since you
last ran this app. Your node app can write this
data to disk for oine analysis.

+ Use the REST API in a script. You can run queries


against your class and use skip/limit to page
through results, which can then be written to disk
for oine analysis. You can again use date
constraints to make sure only newly updated data
is extracted.

+ If the above two options do not t your needs,


you can try using the Data Browser to export
data selectively. Use the Funnel icon to create a
lter for the specic data that you need to
export, such as newly updated objects. Once the
lter has been applied, click on the Export data
icon on the upper right of your Data Browser.
This type of export will only include the objects
that match your criteria.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Relations
There are three kinds of relationships. One-to-one
relationships enable one object to be associated with
another object. One-to-many relationships enable one
object to have many related objects. Finally, many-tomany relationships enable complex relationships among
many objects.
There are four ways to build relationships in Parse:

One-to-Many
When youre thinking about one-to-many relationships
and whether to implement Pointers or Arrays, there are
several factors to consider. First, how many objects are
involved in this relationship? If the "many" side of the
relationship could contain a very large number (greater
than 100 or so) of objects, then you have to use
Pointers. If the number of objects is small (fewer than
100 or so), then Arrays may be more convenient,
especially if you typically need to get all of the related
objects (the "many" in the "one-to-many relationship")
at the same time as the parent object.
USING POINTERS

Let's say we have a game app. The game keeps track of


the player's score and achievements every time she
chooses to play. In Parse, we can store this data in a
single Game object. If the game becomes incredibly
successful, each player will store thousands of Game
objects in the system. For circumstances like this,
where the number of relationships can be arbitrarily
large, Pointers are the best option.

Suppose in this game app, we want to make sure that


every Game object is associated with a Parse User. We
can implement this like so:

ParseObject game = new ParseObject("Game");


game.put("createdBy",
ParseUser.getCurrentUser());

We can obtain all of the Game objects created by a


Parse User with a query:

ParseQuery<ParseObject> gameQuery =
ParseQuery.getQuery("Game");
gameQuery.whereEqualTo("createdBy",
ParseUser.getCurrentUser());

And, if we want to nd the Parse User who created a


specic Game , that is a lookup on the createdBy key:

// say we have a Game object


ParseObject game = ...
// getting the user who created the Game
ParseUser createdBy =
game.getUser("createdBy");

For most scenarios, Pointers will be your best bet for


implementing one-to-many relationships.
USING ARRAYS

Arrays are ideal when we know that the number of


objects involved in our one-to-many relationship are
going to be small. Arrays will also provide some

productivity benet via the includeKey parameter.


Supplying the parameter will enable you to obtain all of
the "many" objects in the "one-to-many" relationship at
the same time that you obtain the "one" object.
However, the response time will be slower if the
number of objects involved in the relationship turns out
to be large.
Suppose in our game, we enabled players to keep track
of all the weapons their character has accumulated as
they play, and there can only be a dozen or so weapons.
In this example, we know that the number of weapons
is not going to be very large. We also want to enable
the player to specify the order in which the weapons
will appear on screen. Arrays are ideal here because the
size of the array is going to be small and because we
also want to preserve the order the user has set each
time they play the game:
Let's start by creating a column on our Parse User
object called weaponsList .
Now let's store some Weapon objects in the
weaponsList :

// let's say we have four weapons


ParseObject scimitar = ...
ParseObject plasmaRifle = ...
ParseObject grenade = ...
ParseObject bunnyRabbit = ...
// stick the objects in an array
ArrayList<ParseObject> weapons = new
ArrayList<ParseObject>();
weapons.add(scimitar);
weapons.add(plasmaRifle);
weapons.add(grenade);
weapons.add(bunnyRabbit);

// store the weapons for the user


ParseUser.getCurrentUser().put("weaponsList",
weapons);

Later, if we want to retrieve the Weapon objects, it's


just one line of code:

ArrayList<ParseObject> weapons =
ParseUser.getCurrentUser().get("weaponsList");

Sometimes, we will want to fetch the "many" objects in


our one-to-many relationship at the same time as we
fetch the "one" object. One trick we could employ is to
use the includeKey (or include in Android)
parameter whenever we use a Parse Query to also
fetch the array of Weapon objects (stored in the
weaponsList column) along with the Parse User
object:

// set up our query for a User object


ParseQuery<ParseUser> userQuery =
ParseUser.getQuery();
// configure any constraints on your query...
// for example, you may want users who are
also playing with or against you
// tell the query to fetch all of the Weapon
objects along with the user
// get the "many" at the same time that you're
getting the "one"
userQuery.include("weaponsList");
// execute the query
userQuery.findInBackground(new
FindCallback<ParseUser>() {

public void done(List<ParseUser> userList,


ParseException e) {
// userList contains all of the User
objects, and their associated Weapon objects,
too
}
});

You can also get the "one" side of the one-to-many


relationship from the "many" side. For example, if we
want to nd all Parse User objects who also have a
given Weapon , we can write a constraint for our query
like this:

// add a constraint to query for whenever a


specific Weapon is in an array
userQuery.whereEqualTo("weaponsList",
scimitar);
// or query using an array of Weapon
objects...
userQuery.whereEqualTo("weaponsList",
arrayOfWeapons);

Many-to-Many
Now lets tackle many-to-many relationships. Suppose
we had a book reading app and we wanted to model
Book objects and Author objects. As we know, a given
author can write many books, and a given book can
have multiple authors. This is a many-to-many
relationship scenario where you have to choose
between Arrays, Parse Relations, or creating your own
Join Table.
The decision point here is whether you want to attach
any metadata to the relationship between two entities.

If you dont, Parse Relation or using Arrays are going to


be the easiest alternatives. In general, using arrays will
lead to higher performance and require fewer queries.
If either side of the many-to-many relationship could
lead to an array with more than 100 or so objects, then,
for the same reason Pointers were better for one-tomany relationships, Parse Relation or Join Tables will be
better alternatives.
On the other hand, if you want to attach metadata to
the relationship, then create a separate table (the "Join
Table") to house both ends of the relationship.
Remember, this is information about the relationship,
not about the objects on either side of the relationship.
Some examples of metadata you may be interested in,
which would necessitate a Join Table approach, include:
USING PARSE RELATIONS

Using Parse Relations, we can create a relationship


between a Book and a few Author objects. In the Data
Browser, you can create a column on the Book object
of type relation and name it authors .
After that, we can associate a few authors with this
book:

// lets say we have a few objects


representing Author objects
ParseObject authorOne =
ParseObject authorTwo =
ParseObject authorThree =
// now we create a book object
ParseObject book = new ParseObject("Book");
// now lets associate the authors with the
book
// remember, we created a "authors" relation
on Book

ParseRelation<ParseObject> relation =
book.getRelation("authors");
relation.add(authorOne);
relation.add(authorTwo);
relation.add(authorThree);
// now save the book object
book.saveInBackground();

To get the list of authors who wrote a book, create a


query:

// suppose we have a book object


ParseObject book = ...
// create a relation based on the authors key
ParseRelation relation =
book.getRelation("authors");
// generate a query based on that relation
ParseQuery query = relation.getQuery();
// now execute the query

Perhaps you even want to get a list of all the books to


which an author contributed. You can create a slightly
dierent kind of query to get the inverse of the
relationship:

// suppose we have a author object, for which


we want to get all books
ParseObject author = ...
// first we will create a query on the Book
object

ParseQuery<ParseObject> query =
ParseQuery.getQuery("Book");
// now we will query the authors relation to
see if the author object we have
// is contained therein
query.whereEqualTo("authors", author);

USING JOIN TABLES

There may be certain cases where we want to know


more about a relationship. For example, suppose we
were modeling a following/follower relationship
between users: a given user can follow another user,
much as they would in popular social networks. In our
app, we not only want to know if User A is following
User B, but we also want to know when User A started
following User B. This information could not be
contained in a Parse Relation. In order to keep track of
this data, you must create a separate table in which the
relationship is tracked. This table, which we will call
Follow , would have a from column and a to column,
each with a pointer to a Parse User. Alongside the
relationship, you can also add a column with a Date
object named date .
Now, when you want to save the following relationship
between two users, create a row in the Follow table,
lling in the from , to , and date keys appropriately:

// suppose we have a user we want to follow


ParseUser otherUser = ...
// create an entry in the Follow table
ParseObject follow = new
ParseObject("Follow");
follow.put("from",
ParseUser.getCurrentUser());

follow.put("to", otherUser);
follow.put("date", Date());
follow.saveInBackground();

If we want to nd all of the people we are following, we


can execute a query on the Follow table:

// set up the query on the Follow table


ParseQuery<ParseObject> query =
ParseQuery.getQuery("Follow");
query.whereEqualTo("from",
ParseUser.getCurrentUser());
// execute the query
query.findInBackground(newFindCallback<ParseObject>
() {
public void done(List<ParseObject>
followList, ParseException e) {
}
});

Its also pretty easy to nd all the users that are


following the current user by querying on the to key:

// set up the query on the Follow table


ParseQuery<ParseObject> query =
ParseQuery.getQuery("Follow");
query.whereEqualTo("to",
ParseUser.getCurrentUser());
// execute the query
query.findInBackground(newFindCallback<ParseObject>
() {
public void done(List<ParseObject>

followList, ParseException e) {
}
});

USING AN ARRAY

Arrays are used in Many-to-Many relationships in much


the same way that they are for One-to-Many
relationships. All objects on one side of the relationship
will have an Array column containing several objects on
the other side of the relationship.
Suppose we have a book reading app with Book and
Author objects. The Book object will contain an Array
of Author objects (with a key named authors ). Arrays
are a great t for this scenario because it's highly
unlikely that a book will have more than 100 or so
authors. We will put the Array in the Book object for
this reason. After all, an author could write more than
100 books.
Here is how we save a relationship between a Book and
an Author .

// let's say we have an author


ParseObject author = ...
// and let's also say we have an book
ParseObject book = ...
// add the author to the authors list for the
book
book.put("authors", author);

Because the author list is an Array, you should use the


includeKey (or include on Android) parameter when
fetching a Book so that Parse returns all the authors

when it also returns the book:

// set up our query for the Book object


ParseQuery bookQuery =
ParseQuery.getQuery("Book");
// configure any constraints on your query...
// tell the query to fetch all of the Author
objects along with the Book
bookQuery.include("authors");
// execute the query
bookQuery.findInBackground(newFindCallback<ParseObject>
() {
public void done(List<ParseObject>
bookList, ParseException e) {
}
});

At that point, getting all the Author objects in a given


Book is a pretty straightforward call:

ArrayList<ParseObject> authorList =
book.getList("authors");

Finally, suppose you have an Author and you want to


nd all the Book objects in which she appears. This is
also a pretty straightforward query with an associated
constraint:

// set up our query for the Book object


ParseQuery bookQuery =
ParseQuery.getQuery("Book");

// configure any constraints on your query...


booKQuery.whereEqualTo("authors", author);
// tell the query to fetch all of the Author
objects along with the Book
bookQuery.include("authors");
// execute the query
bookQuery.findInBackground(newFindCallback<ParseObject>
() {
public void done(List<ParseObject>
bookList, ParseException e) {
}
});

One-to-One
In Parse, a one-to-one relationship is great for
situations where you need to split one object into two
objects. These situations should be rare, but two
examples include:

+ Limiting visibility of some user data. In this


scenario, you would split the object in two, where
one portion of the object contains data that is
visible to other users, while the related object
contains data that is private to the original user
(and protected via ACLs).

+ Splitting up an object for size. In this scenario,


your original object is greater than the 128K
maximum size permitted for an object, so you
decide to create a secondary object to house
extra data. It is usually better to design your data
model to avoid objects this large, rather than
splitting them up. If you can't avoid doing so, you
can also consider storing large data in a Parse
File.
Thank you for reading this far. We apologize for the

complexity. Modeling relationships in data is a hard


subject, in general. But look on the bright side: it's still
easier than relationships with people.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Handling Errors
Many of the methods on ParseObject , including
save() , delete() , and get() will throw a
ParseException on an invalid request, such as deleting
or editing an object that no longer exists in the cloud, or
when there is a network failure preventing
communication with the Parse Cloud. You will need to
catch and deal with these exceptions.
For more details, look at the Android API.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Security
As your app development progresses, you will want to
use Parse's security features in order to safeguard data.
This document explains the ways in which you can
secure your apps.
If your app is compromised, it's not only you as the
developer who suers, but potentially the users of your

app as well. Continue reading for our suggestions for


sensible defaults and precautions to take before
releasing your app into the wild.

Client vs. Server


When an app rst connects to Parse, it identies itself
with an Application ID and a Client key (or REST Key, or
.NET Key, or JavaScript Key, depending on which
platform you're using). These are not secret and by
themselves they do not secure an app. These keys are
shipped as a part of your app, and anyone can
decompile your app or proxy network trac from their
device to nd your client key. This exploit is even easier
with JavaScript one can simply "view source" in the
browser and immediately nd your client key.
This is why Parse has many other security features to
help you secure your data. The client key is given out to
your users, so anything that can be done with just the
client key is doable by the general public, even
malicious hackers.
The master key, on the other hand, is denitely a
security mechanism. Using the master key allows you to
bypass all of your app's security mechanisms, such as
class-level permissions and ACLs. Having the master
key is like having root access to your app's servers, and
you should guard your master key with the same zeal
with which you would guard your production machines'
root password.
The overall philosophy is to limit the power of your
clients (using client keys), and to perform any sensitive
actions requiring the master key in Cloud Code. You'll
learn how to best wield this power in the section titled
Implementing Business Logic in Cloud Code.
A nal note: All connections are made with HTTPS and
SSL, and Parse will reject all non-HTTPS connections.

As a result, you don't need to worry about man-in-themiddle attacks.

Class-Level Permissions
The second level of security is at the schema and data
level. Enforcing security measures at this level will
restrict how and when client applications can access
and create data on Parse. When you rst begin
developing your Parse application, all of the defaults are
set so that you can be a more productive developer. For
example:

+ A client application can create new classes on


Parse

+ A client application can add elds to classes


+ A client application can modify or query for
objects on Parse
You can congure any of these permissions to apply to
everyone, no one, or to specic users or roles in your
app. Roles are groups that contain users or other roles,
which you can assign to an object to restrict its use. Any
permission granted to a role is also granted to any of its
children, whether they are users or other roles, enabling
you to create an access hierarchy for your apps. Each of
the Parse guides includes a detailed description of
employing Roles in your apps.
Once you are condent that you have the right classes
and relationships between classes in your app, you
should begin to lock it down by doing the following:
Almost every class that you create should have these
permissions tweaked to some degree. For classes
where every object has the same permissions, classlevel settings will be most eective. For example, one
common use case entails having a class of static data
that can be read by anyone but written by no one.

RESTRICTING CLASS CREATION

As a start, you can congure your application so that


clients cannot create new classes on Parse. This is done
from the Settings tab on the Data Browser. Scroll down
to the App Permissions section and turn o Allow
client class creation. Once enabled, classes may only
be created from the Data Browser. This will prevent
attackers from lling your database with unlimited,
arbitrary new classes.

CONFIGURING CLASS-LEVEL PERMISSIONS

Parse lets you specify what operations are allowed per


class. This lets you restrict the ways in which clients can
access or modify your classes. To change these
settings, go to the Data Browser, select a class, and
click the "Security" button.
You can congure the client's ability to perform each of
the following operations for the selected class:

+ Read:
+ Get: With Get permission, users can fetch
objects in this table if they know their
objectIds.

+ Find: Anyone with Find permission can


query all of the objects in the table, even if
they dont know their objectIds. Any table
with public Find permission will be
completely readable by the public, unless
you put an ACL on each object.

+ Write:

+ Update: Anyone with Update permission


can modify the elds of any object in the
table that doesn't have an ACL. For publicly
readable data, such as game levels or
assets, you should disable this permission.

+ Create: Like Update, anyone with Create


permission can create new objects of a
class. As with the Update permission, you'll
probably want to turn this o for publicly
readable data.

+ Delete: With this permission, people can


delete any object in the table that doesn't
have an ACL. All they need is its objectId.

+ Add elds: Parse classes have schemas that are


inferred when objects are created. While you're
developing your app, this is great, because you
can add a new eld to your object without having
to make any changes on the backend. But once
you ship your app, it's very rare to need to add
new elds to your classes automatically. You
should pretty much always turn o this
permission for all of your classes when you
submit your app to the public.

For each of the above actions, you can grant permission


to all users (which is the default), or lock permissions
down to a list of roles and users. For example, a class
that should be available to all users would be set to
read-only by only enabling get and nd. A logging class
could be set to write-only by only allowing creates. You
could enable moderation of user-generated content by
providing update and delete access to a particular set
of users or roles.

Object-Level Access Control

Once you've locked down your schema and class-level


permissions, it's time to think about how data is
accessed by your users. Object-level access control
enables one user's data to be kept separate from
another's, because sometimes dierent objects in a
class need to be accessible by dierent people. For
example, a users private personal data should be
accessible only to them.
Parse also supports the notion of anonymous users for
those apps that want to store and protect user-specic
data without requiring explicit login.
When a user logs into an app, they initiate a session
with Parse. Through this session they can add and
modify their own data but are prevented from
modifying other users' data.
ACCESS CONTROL LISTS

The easiest way to control who can access which data


is through access control lists, commonly known as
ACLs. The idea behind an ACL is that each object has a
list of users and roles along with what permissions that
user or role has. A user needs read permissions (or
must belong to a role that has read permissions) in
order to retrieve an object's data, and a user needs
write permissions (or must belong to a role that has
write permissions) in order to update or delete that
object.
Once you have a User, you can start using ACLs.
Remember: Users can be created through traditional
username/password signup, through a third-party login
system like Facebook or Twitter, or even by using
Parse's automatic anonymous users functionality. To
set an ACL on the current user's data to not be publicly
readable, all you have to do is:

ParseUser user = ParseUser.getCurrentUser();

user.setACL(new ParseACL(user));

Most apps should do this. If you store any sensitive user


data, such as email addresses or phone numbers, you
need to set an ACL like this so that the user's private
information isn't visible to other users. If an object
doesn't have an ACL, it's readable and writeable by
everyone. The only exception is the _User class. We
never allow users to write each other's data, but they
can read it by default. (If you as the developer need to
update other _User objects, remember that your
master key can provide the power to do this.)
To make it super easy to create user-private ACLs for
every object, we have a way to set a default ACL that
will be used for every new object you create:

ParseACL.setDefaultACL(new ParseACL(), true);

If you want the user to have some data that is public


and some that is private, it's best to have two separate
objects. You can add a pointer to the private data from
the public one.

ParseObject privateData = new


ParseObject("PrivateUserData");
privateData.setACL(new
ParseACL(ParseUser.getCurrentUser()));
privateData.put("phoneNumber", "555-5309");
ParseUser.getCurrentUser().put("privateData",
privateData);

Of course, you can set dierent read and write


permissions on an object. For example, this is how you

would create an ACL for a public post by a user, where


anyone can read it:

ParseACL acl = new ParseACL();


acl.setPublicReadAccess(true);
acl.setWriteAccess(ParseUser.getCurrentUser(),
true);

Sometimes it's inconvenient to manage permissions on


a per-user basis, and you want to have groups of users
who get treated the same (like a set of admins with
special powers). Roles are are a special kind of object
that let you create a group of users that can all be
assigned to the ACL. The best thing about roles is that
you can add and remove users from a role without
having to update every single object that is restricted to
that role. To create an object that is writeable only by
admins:

// Assuming you've already created a role


called "admins"...
ParseACL acl = new ParseACL();
acl.setPublicReadAccess(true);
acl.setRoleWriteAccess("admins", true);

Of course, this snippet assumes you've already created


a role named "admins". This is often reasonable when
you have a small set of special roles set up while
developing your app. Roles can also be created and
updated on the y for example, adding new friends
to a "friendOf_" role after each connection is made.
All this is just the beginning. Applications can enforce all
sorts of complex access patterns through ACLs and
class-level permissions. For example:

+ For private data, read and write access can be


restricted to the owner.

+ For a post on a message board, the author and


members of the "Moderators" role can have
"write" access, and the general public can have
"read" access.

+ For logging data that will only be accessed by the


developer through the REST API using the
master key, the ACL can deny all permissions.

+ Data created by a privileged group of users or the


developer, like a global message of the day, can
have public read access but restrict write access
to an "Administrators" role.

+ A message sent from one user to another can


give "read" and "write" access just to those users.
For the curious, here's the format for an ACL that
restricts read and write permissions to the owner
(whose objectId is identied by "aSaMpLeUsErId" )
and enables other users to read the object:

{
"*": { "read":true },
"aSaMpLeUsErId": { "read" :true, "write":
true }
}

And here's another example of the format of an ACL


that uses a Role:

{
"role:RoleName": { "read": true },
"aSaMpLeUsErId": { "read": true, "write":
true }
}

POINTER PERMISSIONS

Pointer permissions are a special type of class-level


permission that create a virtual ACL on every object in
a class, based on users stored in pointer elds on those
objects. For example, given a class with an owner eld,
setting a read pointer permission on owner will make
each object in the class only readable by the user in that
object's owner eld. For a class with a sender and a
reciever eld, a read pointer permission on the
receiver eld and a read and write pointer permission
on the sender eld will make each object in the class
readable by the user in the sender and receiver eld,
and writable only by the user in the sender eld.
Given that objects often already have pointers to the
user(s) that should have permissions on the object,
pointer permissions provide a simple and fast solution
for securing your app using data which is already there,
that doesn't require writing any client code or cloud
code.
Pointer permissions are like virtual ACLs. They don't
appear in the ACL column, buf if you are familiar with
how ACLs work, you can think of them like ACLs. In the
above example with the sender and receiver , each
object will act as if it has an ACL of:

{
"<SENDER_USER_ID>": {
"read": true,
"write": true
},
"<RECEIVER_USER_ID>": {
"read": true
}
}

Note that this ACL is not actually created on each

object. Any existing ACLs will not be modied when


you add or remove pointer permissions, and any user
attempting to interact with an object can only interact
with the object if both the virtual ACL created by the
pointer permissions, and the real ACL already on the
object allow the interaction. For this reason, it can
sometimes be confusing to combine pointer
permissions and ACLs, so we recommend using pointer
permissions for classes that don't have many ACLs set.
Fortunately, it's easy to remove pointer permissions if
you later decide to use Cloud Code or ACLs to secure
your app.
CLP AND ACL INTERACTION

Class-Level Permissions (CLPs) and Access Control


Lists (ACLs) are both powerful tools for securing your
app, but they don't always interact exactly how you
might expect. They actually represent two separate
layers of security that each request has to pass through
to return the correct information or make the intended
change. These layers, one at the class level, and one at
the object level, are shown below. A request must pass
through BOTH layers of checks in order to be
authorized. Note that despite acting similarly to ACLs,
Pointer Permissions are a type of class level
permission, so a request must pass the pointer
permission check in order to pass the CLP check.

As you can see, whether a user is authorized to make a


request can become complicated when you use both
CLPs and ACLs. Let's look at an example to get a better
sense of how CLPs and ACLs can interact. Say we have
a Photo class, with an object, photoObject . There are
2 users in our app, user1 and user2 . Now lets say we
set a Get CLP on the Photo class, disabling public Get,
but allowing user1 to perform Get. Now let's also set
an ACL on photoObject to allow Read - which includes
GET - for only user2 .
You may expect this will allow both user1 and user2
to Get photoObject , but because the CLP layer of
authentication and the ACL layer are both in eect at
all times, it actually makes it so neither user1 nor
user2 can Get photoObject . If user1 tries to Get
photoObject , it will get through the CLP layer of
authentication, but then will be rejected because it
does not pass the ACL layer. In the same way, if user2
tries to Get photoObject , it will also be rejected at the
CLP layer of authentication.
Now lets look at example that uses Pointer
Permissions. Say we have a Post class, with an object,
myPost . There are 2 users in our app, poster , and

viewer . Lets say we add a pointer permission that


gives anyone in the Creator eld of the Post class
read and write access to the object, and for the myPost
object, poster is the user in that eld. There is also an
ACL on the object that gives read access to viewer .
You may expect that this will allow poster to read and
edit myPost , and viewer to read it, but viewer will be
rejected by the Pointer Permission, and poster will be
rejected by the ACL, so again, neither user will be able
to access the object.
Because of the complex interaction between CLPs,
Pointer Permissions, and ACLs, we recommend being
careful when using them together. Often it can be
useful to use CLPs only to disable all permissions for a
certain request type, and then using Pointer
Permissions or ACLs for other request types. For
example, you may want to disable Delete for a Photo
class, but then put a Pointer Permission on Photo so
the user who created it can edit it, just not delete it.
Because of the especially complex way that Pointer
Permissions and ACLs interact, we usually recommend
only using one of those two types of security
mechanisms.
SECURITY EDGE CASES

There are some special classes in Parse that don't


follow all of the same security rules as every other
class. Not all classes follow Class-Level Permissions
(CLPs) or Access Control Lists (ACLs) exactly how
they are dened, and here those exceptions are
documented. Here "normal behavior" refers to CLPs
and ACLs working normally, while any other special
behaviors are described in the footnotes.

Get

_User

_Installation

normal behaviour
[1, 2, 3]

ignores CLP, but


not ACL

Find

normal behavior
[3]

master key only


[6]

Create

normal behavior
[4]

ignores CLP

Update

normal behavior
[5]

ignores CLP, but


not ACL [7]

Delete

normal behavior
[5]

master key only


[7]

Add
Field

normal behavior

normal behavior

Logging in, or /1/login in the REST API, does


not respect the Get CLP on the user class. Login
works just based on username and password, and
cannot be disabled using CLPs.

Retrieving the current user, or becoming a User


based on a session token, which are both
/1/users/me in the REST API, do not respect the
Get CLP on the user class.

Read ACLs do not apply to the logged in user. For


example, if all users have ACLs with Read
disabled, then doing a nd query over users will
still return the logged in user. However, if the Find
CLP is disabled, then trying to perform a nd on
users will still return an error.

Create CLPs also apply to signing up. So disabling


Create CLPs on the user class also disables
people from signing up without the master key.

Users can only Update and Delete themselves.


Public CLPs for Update and Delete may still apply.
For example, if you disable public Update for the
user class, then users cannot edit themselves. But
no matter what the write ACL on a user is, that
user can still Update or Delete itself, and no other
user can Update or Delete that user. As always,

however, using the master key allows users to


update other users, independent of CLPs or
ACLs.
6

Get requests on installations follow ACLs


normally. Find requests without master key is not
allowed unless you supply the installationId
as a constraint.

Update requests on installations do adhere to the


ACL dened on the installation, but Delete
requests are master-key-only. For more
information about how installations work, check
out the installations section of the REST guide.

Data Integrity in Cloud Code


For most apps, care around keys, class-level
permissions, and object-level ACLs are all you need to
keep your app and your users' data safe. Sometimes,
though, you'll run into an edge case where they aren't
quite enough. For everything else, there's Cloud Code.
Cloud Code allows you to upload JavaScript to Parse's
servers, where we will run it for you. Unlike client code
running on users' devices that may have been tampered
with, Cloud Code is guaranteed to be the code that
you've written, so it can be trusted with more
responsibility.
One particularly common use case for Cloud Code is
preventing invalid data from being stored. For this sort
of situation, it's particularly important that a malicious
client not be able to bypass the validation logic.
To create validation functions, Cloud Code allows you
to implement a beforeSave trigger for your class.
These triggers are run whenever an object is saved, and
allow you to modify the object or completely reject a
save. For example, this is how you create a Cloud Code
beforeSave trigger to make sure every user has an

email address set:

Parse.Cloud.beforeSave(Parse.User,
function(request, response) {
var user = request.object;
if (!user.get("email")) {
response.error("Every user must have an
email address.");
} else {
response.success();
}
});

Our Cloud Code guide provides instructions on how to


upload this trigger to our servers.
Validations can lock down your app so that only certain
values are acceptable. You can also use afterSave
validations to normalize your data (e.g. formatting all
phone numbers or currency identically). You get to
retain most of the productivity benets of accessing
Parse data directly from your client applications, but
you can also enforce certain invariants for your data on
the y.
Common scenarios that warrant validation include:

+ Making sure phone numbers have the right


format

+ Sanitizing data so that its format is normalized


+ Making sure that an email address looks like a real
email address

+ Requiring that every user species an age within


a particular range

+ Not letting users directly change a calculated eld


+ Not letting users delete specic objects unless
certain conditions are met

Implementing Business Logic in Cloud


Code
While validation often makes sense in Cloud Code,
there are likely certain actions that are particularly
sensitive, and should be as carefully guarded as
possible. In these cases, you can remove permissions or
the logic from clients entirely and instead funnel all
such operations to Cloud Code functions.
When a Cloud Code function is called, it can invoke the
useMasterKey function to gain the ability to modify
user data. With the master key, your Cloud Code
function can override any ACLs and write data. This
means that it'll bypass all the security mechanisms
you've put in place in the previous sections.
Say you want to allow a user to "like" a Post object
without giving them full write permissions on the
object. You can do this by having the client call a Cloud
Code function instead of modifying the Post itself:
The master key should be used carefully. When invoked,
the master key is in eect for the duration of the Cloud
Code function in which it is called:

Parse.Cloud.define("like", function(request,
response) {
Parse.Cloud.useMasterKey();
// Everything after this point will bypass
ACLs and other security
// even if I do things besides just updating
a Post object.
});

A more prudent way to use the master key would be to


pass it as a parameter on a per-function basis. For
example, instead of the above, set useMasterKey to

true in each individual API function:

Parse.Cloud.define("like", function(request,
response) {
var post = new Parse.Object("Post");
post.id = request.params.postId;
post.increment("likes");
post.save(null, { useMasterKey: true
}).then(function() {
// If I choose to do something else here,
it won't be using
// the master key and I'll be subject to
ordinary security measures.
response.success();
}, function(error) {
response.error(error);
});
});

One very common use case for Cloud Code is sending


push notications to particular users. In general, clients
can't be trusted to send push notications directly,
because they could modify the alert text, or push to
people they shouldn't be able to. Your app's settings
will allow you to set whether "client push" is enabled or
not; we recommend that you make sure it's disabled.
Instead, you should write Cloud Code functions that
validate the data to be pushed and sent before sending
a push.

Parse Security Summary


Parse provides a number of ways for you to secure data
in your app. As you build your app and evaluate the
kinds of data you will be storing, you can make the
decision about which implementation to choose.

It is worth repeating that that the Parse User object is


readable by all other users by default. You will want to
set the ACL on your User object accordingly if you wish
to prevent data contained in the User object (for
example, the user's email address) from being visible by
other users.
Most classes in your app will fall into one of a couple of
easy-to-secure categories. For fully public data, you can
use class-level permissions to lock down the table to
put publicly readable and writeable by no one. For fully
private data, you can use ACLs to make sure that only
the user who owns the data can read it. But
occasionally, you'll run into situations where you don't
want data thats fully public or fully private. For
example, you may have a social app, where you have
data for a user that should be readable only to friends
whom theyve approved. For this you'll need to a
combination of the techniques discussed in this guide
to enable exactly the sharing rules you desire.
We hope that you'll use these tools to do everything
you can to keep your app's data and your users' data
secure. Together, we can make the web a safer place.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Performance
As your app scales, you will want to ensure that it
performs well under increased load and usage. There
are parts of optimizing performance that Parse takes
care of but there are some things you can do. This
document provides guidelines on how you can optimize
your app's performance. While you can use Parse for

quick prototyping and not worry about performance,


you will want to keep our performance guidelines in
mind when you're initially designing your app. We
strongly advise that you make sure you've followed all
suggestions before releasing your app.
Parse provides services out of the box to help your app
scale auto-magically. On top of our MongoDB
datastore, we have built an API layer that seamlessly
integrates with our client-side SDKs. Our cloud
infrastructure uses online learning algorithms to
automatically rewrite inecient queries and generate
database indexes based on your apps realtime query
stream.
In addition to what Parse provides, you can improve
your app's performance by looking at the following:

+ Writing ecient queries.


+ Writing restrictive queries.
+ Using client-side caching.
+ Using Cloud Code.
+ Avoiding count queries.
+ Using ecient search techniques.
Keep in mind that not all suggestions may apply to your
app. Let's look into each one of these in more detail.

Write Efficient Queries


Parse objects are stored in a database. A Parse query
retrieves objects that you are interested in based on
conditions you apply to the query. To avoid looking
through all the data present in a particular Parse class
for every query, the database can use an index. An index
is a sorted list of items matching a given criteria.
Indexes help because they allow the database to do an

ecient search and return matching results without


looking at all of the data. Indexes are typically smaller in
size and available in memory, resulting in faster lookups.

Smart Indexing
The key to writing ecient queries is understanding
our indexing strategy. If your data is not indexed, every
query will have to go through the the entire data for a
class to return a query result. On the other hand, if your
data is indexed appropriately, the number of
documents scanned to return a correct query result
should be low.
One of the advantages to using Parse if that you don't
have to worry about managing your own database and
maintaining indexes. We've built an abstraction to
manage all that complexity. However, you do have to
organize your data model and use performant queries
to take advantage of this. To better understand how to
go about doing this, you need to understand how our
systems are operating behind the abstraction. The key
strategy you will want to understand here is our use of
smart indexing.
Smart indexing means that we algorithmically generate
indexes for the apps that we host. The sheer number of
apps hosted on Parse means that we cannot manually
generate indexes for each app. This would not scale
well as developers can change their schemas or query
patterns at any time. This is why we rely on smart
indexes.
We perform two types of index creation logic. The rst
generates simple (single eld) indexes for each API
request, and the second does oine processing to pick
good compound indexes based on real API trac
patterns. In each case the goal is to pick indexes that
result in the smallest search space for the query, that is,
there will be less data to scan to nd results.

The simple indexing strategy looks at every API request


and attempts to pick good indexes based on the
following:

+ The query constraint's likelihood of providing the


smallest search space.

+ The number of possible values a eld may have


based on its data type. We consider data types
with a larger number of possible values to have a
higher entropy.
The order of a query constraint's usefulness is:

+ Equal to
+ Contained In
+ Less than, Less than or Equal to, Greater than,
Greater than or Equal to

+ Prex string matches


+ Not equal to
+ Not contained in
+ Everything else
Take a look at the following query to retrieve
GameScore objects:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
query.whereEqualTo("score", 50);
query.whereContainedIn("playerName",
Arrays.asList("Jonathan Walsh", "Dario
Wunsch", "Shawn Simon"));

Creating an index query based on the score eld would


yield a smaller search space in general than creating
one on the playerName eld.

When examining data types, booleans have a very low


entropy and and do not make good indexes. Take the
following query constraint:

query.whereEqualTo("cheatMode", false);

The two possible values for "cheatMode" are true and


false . If an index was added on this eld it would be of
little use because it's likely that 50% of the records will
have to be looked at to return query results.
We also throw out relations and join tables, since no
values are stored for these keys. We heavily promote
GeoPoints since MongoDB wont run a geo query
without a geo index. Other data types are ranked by
their expected entropy of the value space for the key:

+ Array
+ Pointer
+ Date
+ String
+ Number
+ Other
We score each query according to the above metrics,
and make sure we create a unique index on the three
top-scoring elds for each query. For a compound
query that consists of an OR of subqueries, we
compute the top three indexes for each subquery.
Even the best indexing strategy can be defeated by
suboptimal queries. You will need to design queries that
work hand in hand with smart indexing to deliver
performant apps.

Efficient Query Design


Writing ecient queries means taking full advantage of
indexes. Let's take a look at some query constraints
that negate the use of indexes:

+ Not Equal To
+ Not Contained In
Additionally, the following queries under certain
scenarios may result in slow query responses if they
can't take advantage of indexes:

+ Regular Expressions
+ Ordered By
NOT EQUAL TO

For example, let's say you're tracking high scores for a


game in a GameScore class. Now say you want to
retrieve the scores for all players except a certain one.
You could create this query:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
query.whereNotEqualTo("playerName", "Michael
Yabuti");
query.findInBackground(new
FindCallback<ParseObject>() {
@Override
public void done(List<ParseObject> list,
ParseException e) {
if ( e == null) {
// Retrieved scores successfully
}
}
});

This query can't take advantage of indexes. The


database has to look at all the objects in the
"GameScore" class to satisfy the constraint and
retrieve the results. As the number of entries in the
class grows, the query takes longer to run.
Luckily, most of the time a Not Equal To query
condition can be rewritten as a Contained In
condition. Instead of querying for the absence of values,
you ask for values which match the rest of the column
values. Doing this allows the database to use an index
and your queries will be faster.
For example if the User class has a column called state
which has values SignedUp, Veried, and Invited,
the slow way to nd all users who have used the app at
least once would be to run the query:

ParseQuery<ParseUser> query =
ParseQuery.getQuery(ParseUser.class);
query.whereNotEqualTo("state", "Invited");

It would be faster to use the Contained In condition


when setting up the query:

query.whereContainedIn("state",
Arrays.asList("SignedUp", "Verified"));

Sometimes, you may have to completely rewrite your


query. Going back to the "GameScore" example, let's
say we were running that query to display players who
had scored higher than the given player. We could do
this dierently, by rst getting the given player's high
score and then using the following query:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("GameScore");
// Previously retrieved highScore for Michael
Yabuti
query.whereGreaterThan("score", highScore);
query.findInBackground(new
FindCallback<ParseObject>() {
@Override
public void done(List<ParseObject> list,
ParseException e) {
if (e == null) {
// Retrieved scores successfully
}
}
});

The new query you use depends on your use case. This
may sometimes mean a redesign of your data model.
NOT CONTAINED IN

Similar to Not Equal To, the Not Contained In query


constraint can't use an index. You should try and use
the complementary Contained In constraint. Building
on the User example, if the state column had one more
value, Blocked, to represent blocked users, a slow
query to nd active users would be:

ParseQuery<ParseUser> query =
ParseQuery.getQuery(ParseUser.class);
query.whereNotContainedIn("state",
Arrays.asList("Invited", "Blocked"));

Using a complimentary Contained In query constraint


will always be faster:

query.whereContainedIn("state",
Arrays.asList("SignedUp", "Verified"));

This means rewriting your queries accordingly. Your


query rewrites will depend on your schema set up. It
may mean redoing that schema.
REGULAR EXPRESSIONS

Most regular expression queries in Parse are heavily


throttled due to performance considerations. MongoDB
is not ecient for doing partial string matching except
for the special case where you only want a prex
match. Queries that have regular expression constraints
are therefore very expensive, especially for classes with
over 100,000 records. Parse restricts how many such
operations can be run on a particular app at any given
time.
You should avoid using regular expression constraints
that don't use indexes. For example, the following query
looks for data with a given string in the "playerName"
eld. The string search is case insensitive and therefore
cannot be indexed:

query.whereMatches("playerName", "Michael",
"i");

The following query, while case sensitive, looks for any


occurrence of the string in the eld and cannot be
indexed:

query.whereContains("playerName", "Michael");

These queries are both slow. In fact, the matches and

contains query constraints are not covered in our


querying guides on purpose and we do not recommend
using them. Depending on your use case, you should
switch to using the following constraint that uses an
index, such as:

query.whereStartsWith("playerName",
"Michael");

This looks for data that starts with the given string. This
query will use the backend index, so it will be faster
even for large datasets.
As a best practice, when you use regular expression
constraints, you'll want to ensure that other constraints
in the query reduce the result set to the order of
hundreds of objects to make the query ecient. If you
must use the matches or contains constraints for
legacy reasons, then use case sensitive, anchored
queries where possible, for example:

query.whereMatches("playerName", "^Michael");

Most of the use cases around using regular expressions


involve implementing search. A more performant way
of implementing search is detailed later.

Write Restrictive Queries


Writing restrictive queries allows you to return only the
data that the client needs. This is critical in a mobile
environment were data usage can be limited and
network connectivity unreliable. You also want your
mobile app to appear responsive and this is directly
aected by the objects you send back to the client. The

Querying Guide shows the types of constraints you can


add to your existing queries to limit the data returned.
When adding constraints, you want to pay attention
and design ecient queries.
You can limit the number of query results returned. The
limit is 100 by default but anything from 1 to 1000 is a
valid limit:

query.setLimit(10); // limit to at most 10


results

If you're issuing queries on GeoPoints, make sure you


specify a reasonable radius:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("Place");
query.whereWithinMiles("location",
userGeoPoint, 10.0);
query.findInBackground(new
FindCallback<ParseObject>() {
@Override
public void done(List<ParseObject> list,
ParseException e) {
if (e == null) {
// List of places within 10 miles of a
user's location
}
}
});

You can further limit the elds returned by calling


select:

ParseQuery<ParseObject> query =

ParseQuery.getQuery("GameScore");
query.selectKeys(Arrays.asList("score",
"playerName"));
query.findInBackground(new
FindCallback<ParseObject>() {
@Override
public void done(List<ParseObject> list,
ParseException e) {
if (e == null) {
// each of results will only have the
selected fields available.
}
}
});

Client-side Caching
For queries run from iOS and Android, you can turn on
query caching. See the iOS and Android guides for
more details. Caching queries will increase your mobile
app's performance especially in cases where you want
to display cached data while fetching the latest data
from Parse.

Use Cloud Code


Cloud Code allows you to run custom JavaScript logic
on Parse instead of on the client.
You can use this to o load processing to the Parse
servers thus increasing your app's perceived
performance. You can create hooks that run whenever
an object is saved or deleted. This is useful if you want
to validate or sanitize your data. You can also use Cloud
Code to modify related objects or kick o other
processes such as sending o a push notication. There
are time limits to how long Cloud Code can run, for
example an afterSave hook only has 3 seconds to run.

You can use Background Jobs if you need to run more


time consuming processes such as data migrations.
We saw examples of limiting the data returned by
writing restrictive queries. You can also use Cloud
Functions to help limit the amount of data returned to
your app. In the following example, we use a Cloud
Function to get a movie's average rating:

Parse.Cloud.define("averageStars",
function(request, response) {
var Review = Parse.Object.extend("Review");
var query = new Parse.Query(Review);
query.equalTo("movie",
request.params.movie);
query.find().then(function(results) {
var sum = 0;
for (var i = 0; i < results.length; ++i) {
sum += results[i].get("stars");
}
response.success(sum / results.length);
}, function(error) {
response.error("movie lookup failed");
});
});

You could have ran a query on the Review class on the


client, returned only the stars eld data and computed
the result on the client. As the number of reviews for a
movie increases you can see that the data being
returned to the device using this methodology also
increases. Implementing the functionality through a
Cloud Function returns the one result if successful.
As you look at optimizing your queries, you'll nd that
you may have to change the queries - sometimes even
after you've shipped your app to the App Store or
Google Play. The ability to change your queries without

a client update is possible if you use Cloud Functions.


Even if you have to redesign your schema, you could
make all the changes in your Cloud Functions while
keeping the client interface the same to avoid an app
update. Take the average stars Cloud Function example
from before, calling it from a client SDK would look like
this:

Parse.Cloud.run("averageStars", { "movie":
"The Matrix" }).then(function(ratings) {
// ratings is 4.5
});

HashMap<String, String> params = new


HashMap();
params.put("movie", "The Matrix");
ParseCloud.callFunctionInBackground("averageStars"
params, new FunctionCallback<Float>() {
@Override
public void done(Float aFloat,
ParseException e) {
if (e == null) {
// ratings is 4.5
}
}
});

If later on, you need to modify the underlying data


model, your client call can remain the same, as long as
you return back a number that represents the ratings
result.

Avoid Count Operations


For classes with over 1,000 objects, count operations

are limited by timeouts. They may routinely yield


timeout errors or return results that are only
approximately correct. Thus, it is preferable to architect
your application to avoid this count operation.
Suppose you are displaying movie information in your
app and your data model consists of a Movie class and a
Review class that contains a pointer to the
corresponding movie. You might want to display the
review count for each movie on the top-level navigation
screen using a query like this:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("Review");
// movieId corresponds to a given movie's id
query.whereEqualTo("movie", movieId);
query.countInBackground(new CountCallback() {
@Override
public void done(int i, ParseException e) {
if ( e == null) {
// Request succeeded
}
}
});

If you run the count query for each of the UI elements,


they will not run eciently on large data sets. One
approach to avoid using the count() operator could be
to add a eld to the Movie class that represents the
review count for that movie. When saving an entry to
the Review class you could increment the
corresponding movie's review count eld. This can be
done in an afterSave handler:

Parse.Cloud.afterSave("Review",
function(request) {
// Get the movie id for the Review

var movieId =
request.object.get("movie").id;
// Query the Movie represented by this
review
var Movie = Parse.Object.extend("Movie");
var query = new Parse.Query(Movie);
query.get(movieId).then(function(movie) {
// Increment the reviews field on the
Movie object
movie.increment("reviews");
movie.save();
}, function(error) {
throw "Got an error " + error.code + " : "
+ error.message;
});
});

Your new optimized query would not need to look at


the Review class to get the review count:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("Movie");
query.findInBackground(new
FindCallback<ParseObject>() {
@Override
public void done(List<ParseObject> list,
ParseException e) {
if (e == null) {
// Results include the reviews count
field
}
}
});

You could also use a separate Parse Object to keep


track of counts for each review. Whenever a review

gets added or deleted, you can increment or decrement


the counts in an afterSave or afterDelete Cloud
Code handler. The approach you choose depends on
your use case.

Implement Efficient Searches


As mentioned previously, MongoDB is not ecient for
doing partial string matching. However, this is an
important use case when implementing search
functionality that scales well in production.
Simplistic search algorithms simply scan through all the
class data and executes the query on each entry. The
key to making searches run eciently is to minimize the
number of data that has to be examined when
executing each query by using an index as we've
outlined earlier. Youll need to build your data model in
a way that its easy for us to build an index for the data
you want to be searchable. For example, string
matching queries that dont match an exact prex of
the string wont be able to use an index leading to
timeout errors as the data set grows.
Let's walk through an example of how you could build
an ecient search. You can apply the concepts you
learn in this example to your use case. Say your app has
users making posts, and you want to be able to search
those posts for hashtags or particular keywords. Youll
want to pre-process your posts and save the list of
hashtags and words into array elds. You can do this
processing either in your app before saving the posts,
or you can use a Cloud Code beforeSave hook to do
this on the y:

var _ = require("underscore");
Parse.Cloud.beforeSave("Post",
function(request, response) {

var post = request.object;


var toLowerCase = function(w) { return
w.toLowerCase(); };
var words = post.get("text").split(/\b/);
words = _.map(words, toLowerCase);
var stopWords = ["the", "in", "and"]
words = _.filter(words, function(w) {
return w.match(/^\w+$/) && !
_.contains(stopWords, w);
});
var hashtags = post.get("text").match(/#.+?
\b/g);
hashtags = _.map(hashtags, toLowerCase);
post.set("words", words);
post.set("hashtags", hashtags);
response.success();
});

This saves your words and hashtags in array elds,


which MongoDB will store with a multi-key index. There
are some important things to notice about this. First of
all its converting all words to lower case so that we can
look them up with lower case queries, and get case
insensitive matching. Secondly, its ltering out
common words like the, in, and and which will occur
in a lot of posts, to additionally reduce useless scanning
of the index when executing the queries.
Once you've got the keywords set up, you can
eciently look them up using All constraint on your
query:

ParseQuery<ParseObject> query =
ParseQuery.getQuery("Post");
query.whereContainsAll("hashtags",
Arrays.asList("#parse", "#ftw"));
query.findInBackground(new
FindCallback<ParseObject>() {

@Override
public void done(List<ParseObject> list,
ParseException e) {
if (e == null) {
// Request succeeded
}
}
});

Limits and Other Considerations


There are some limits in place to ensure the API can
provide the data you need in a performant manner. We
may adjust these in the future. Please take a moment to
read through the following list:
Objects

+ Parse Objects are limited in size to 128 KB.


+ We recommend against creating more than 64
elds on a single Parse Object to ensure that we
can build eective indexes for your queries.

+ We recommend against using eld names that


are longer than 1,024 characters, otherwise an
index for the eld will not be created.

+ An app may only create up to 100 Parse Cong


keys. Use Parse Objects if you need more.

+ An app may only create up to 200 classes.


+ Only the rst 100 classes will be displayed in the
Data Browser.
Queries

+ Queries return 100 objects by default. Use the


limit parameter to change this, up to a value of
1,000.

+ Queries can only return up to 1,000 objects in a


single result set. This includes any resolved

pointers. You can use skip and limit to page


through results.

+ The maximum value accepted by skip is 10,000.


If you need to get more objects, we recommend
sorting the results and then using a constraint on
the sort column to lter out the rst 10,000
results. You will then be able to continue paging
through results starting from a skip value of 0.
For example, you can sort your results by
createdAt ASC and then lter out any objects
older than the createdAt value of the 10,000th
object when starting again from 0.

+ Alternatively, you may use the each() method in


the JavaScript SDK to page through all objects
that match the query.

+ Skips and limits can only be used on the outer


query.

+ You may increase the limit of a inner query to


1,000, but skip cannot be used to get more
results past the rst 1,000.

+ Constraints that collide with each other will result


in only one of the constraint being applied. An
example of this would be two equalTo
constraints over the same key with two dierent
values, which contradicts itself (perhaps you're
looking for 'contains').

+ Regular expression queries are deprecated and


an app can make no more than 80 regex queries
/ minute.

+ Count operations are limited to 160 count


queries / minute period for each application. The
limit applies to all requests made by all clients of
the application. If you need to query counts more
frequently, you may want to design your system
so the results can be cached in your client
application.

+ No geo-queries inside compound OR queries.


+ Using $exists: false is not advised.
+ The each query method in the JavaScript SDK
cannot be used in conjunction with queries using
geo-point constraints.

+ A maximum of 500,000 objects will be scanned


per query. If your constraints do not successfully
limit the scope of the search, this can result in
queries with incomplete results.

+ A containsAll query constraint can only take up


to 9 items in the comparison array.

Files

+ Parse Files are limited to 10 MB each and the


limit cannot be increased. There is no limit on
how many Parse Files your app can create.

+ Parse Hosting, on the other hand, supports les


up to 500 MB, ideal for Unity projects.

+ Parse Files cannot be accessed via HTTPS.


Push Notications

+ An app can upload up to 6 APNs certicates.


+ Delivery of notications is a best eort, not
guaranteed. It is not intended to deliver data to
your app, only to notify the user that there is new
data available.
Cloud Code

+ JavaScript scripts in Cloud Code are limited to


128 KB.

+ Cloud functions must return within 15 seconds.


Use webhooks if you need more time.

+ Cloud save/delete hooks must return within 3


seconds. Use webhooks if you need more time.

+ Background jobs will be terminated after 15


minutes.

+ Cloud Code invocations (such as functions,


save/delete hooks, background jobs, and custom
endpoint handlers) must use a nite amount of
memory (currently 384 MB, but subject to
change in the future). This memory is garbage

collected by the JavaScript engine, so it is


generally not an issue. To allow the garbage
collector to do its work, make sure that you aren't
saving references to large objects, HTTP
response buers, and other objects to global
variables in your script.

+ Webhooks must return within 30 seconds.


+ Apps can run one job concurrently by default, but
this can be increased by increasing your
requests/second in your Account Overview
page.

+ Additional background jobs over quota will fail to


run. They will not be queued.

+ The params payload that is passed to a Cloud


Function is limited to 50 MB.

+ When using console to log information and


errors, the message string will be limited to 1 KB.

+ Only the rst 100 messages logged by a Cloud


function will be persisted in the Cloud Code logs.

Parse.Cloud.httpRequest has a 2 second


connection timeout. If the connection cannot be
established within 2 seconds, you may see a 124
request timed out error.

Parse.Cloud.httpRequest has a maximum total


timeout of 1 minute. This timeout can be further
restricted by the maximum timeout of the
particular Cloud Code invocation. For instance,
before/after save triggers have a maximum
timeout of 3 seconds, so the maximum timeout
for an HTTP request made within a before/after
save trigger is 3 seconds.

+ There is a limit of 8 concurrent outbound


Parse.Cloud.httpRequest requests per Cloud
Code request.
Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

NO

Error Codes
The following is a list of all the error codes that can be
returned by the Parse API. You may also refer to
RFC2616 for a list of http error codes. Make sure to
check the error message for more details.

API Issues
Name

UserInvalidLoginParams

ObjectNotFound

InvalidQuery

InvalidClassName

Code

Description

101

Invalid login
parameters. Check
error message for
more details.

101

The specied
object or session
doesn't exist or
could not be
found. Can also
indicate that you
do not have the
necessary
permissions to
read or write this
object. Check
error message for
more details.

102

There is a problem
with the
parameters used
to construct this
query. This could
be an invalid eld
name or an invalid
eld type for a
specic constraint.
Check error
message for more
details.

103

Missing or invalid
classname.
Classnames are
case-sensitive.
They must start
with a letter, and

a-zA-Z0-9_ are
the only valid
characters.

MissingObjectId

InvalidFieldName

InvalidPointer

InvalidJSON

CommandUnavailable

104

An unspecied
object id.

105

An invalid eld
name. Keys are
case-sensitive.
They must start
with a letter, and
a-zA-Z0-9_ are
the only valid
characters. Some
eld names may
be reserved. Check
error message for
more details.

106

A malformed
pointer was used.
You would
typically only see
this if you have
modied a client
SDK.

107

Badly formed
JSON was
received upstream.
This either
indicates you have
done something
unusual with
modifying how
things encode to
JSON, or the
network is failing
badly. Can also
indicate an invalid
utf-8 string or use
of multiple form
encoded values.
Check error
message for more
details.

108

The feature you


tried to access is
only available
internally for
testing purposes.

109

You must call


Parse.initialize
before using the
Parse library.
Check the Quick
Start guide for
your platform.

116

The object is too


large.
ParseObjects have
a max size of 128
kilobytes.

116

You have reached


the limit of 100
cong parameters.

117

An invalid value
was set for the
limit. Check error
message for more
details.

118

An invalid value
was set for skip.
Check error
message for more
details.

OperationForbidden

119

The operation isn't


allowed for clients
due to class-level
permissions.
Check error
message for more
details.

CacheMiss

120

The result was not


found in the cache.

InvalidNestedKey

121

An invalid key was


used in a nested
JSONObject.
Check error
message for more
details.

InvalidACL

123

An invalid ACL was


provided.

NotInitialized

ObjectTooLarge

ExceededConfigParamsError

InvalidLimitError

InvalidSkipError

125

The email address


was invalid.

DuplicateValue

137

Unique eld was


given a value that
is already taken.

InvalidRoleName

139

Role's name is
invalid.

ReservedValue

139

Field value is
reserved.

140

You have reached


the quota on the
number of classes
in your app. Please
delete some
classes if you need
to add a new class.

141

Cloud Code script


failed. Usually
points to a
JavaScript error.
Check error
message for more
details.

141

Cloud function not


found. Check that
the specied
Cloud function is
present in your
Cloud Code script
and has been
deployed.

141

Background job
not found. Check
that the specied
job is present in
your Cloud Code
script and has
been deployed.

InvalidEmailAddress

ExceededCollectionQuota

ScriptFailed

FunctionNotFound

JobNotFound

success/error was
not called. A cloud
function will return
once
response.success()
or response.error()

SuccessErrorNotCalled

141

is called. A
background job
will similarly nish
execution once
status.success() or
status.error() is
called. If a function
or job never
reaches either of
the success/error
methods, this
error will be
returned. This may
happen when a
function does not
handle an error
response correctly,
preventing code
execution from
reaching the
success() method
call.

MultupleSuccessErrorCalls

141

Can't call
success/error
multiple times. A
cloud function will
return once
response.success()
or response.error()
is called. A
background job
will similarly nish
execution once
status.success() or
status.error() is
called. If a function
or job calls
success() and/or
error() more than
once in a single
execution path,
this error will be
returned.

ValidationFailed

142

Cloud Code
validation failed.

WebhookError

143

Webhook error.

InvalidImageData

150

Invalid image data.

UnsavedFileError

151

An unsaved le.

InvalidPushTimeError

152

An invalid push
time was specied.

HostingError

158

Hosting error.

160

The provided
analytics event
name is invalid.

ClassNotEmpty

255

Class is not empty


and cannot be
dropped.

AppNameInvalid

256

App name is
invalid.

MissingAPIKeyError

902

The request is
missing an API key.

903

The request is
using an invalid API
key.

InvalidEventName

InvalidAPIKeyError

Push related errors


Name

IncorrectType

InvalidChannelName

Code

Description

111

A eld was
set to an
inconsistent
type. Check
error
message for
more details.

112

Invalid
channel
name. A
channel name
is either an
empty string
(the
broadcast
channel) or
contains only
a-zA-Z0-9_
characters
and starts
with a letter.

InvalidSubscriptionType

InvalidDeviceToken

PushMisconfigured

PushWhereAndChannels

PushWhereAndType

PushMissingData

PushMissingChannels

113

Bad
subscription
type. Check
error
message for
more details.

114

The provided
device token
is invalid.

115

Push is
miscongured
in your app.
Check error
message for
more details.

115

Can't set
channels for a
querytargeted
push. You can
x this by
moving the
channels into
your push
query
constraints.

115

Can't set
device type
for a querytargeted
push. You can
x this by
incorporating
the device
type
constraints
into your
push query.

115

Push is
missing a
'data' eld.

115

Non-query
push is
missing a
'channels'
eld. Fix by

passing a
'channels' or
'query' eld.

ClientPushDisabled

RestPushDisabled

ClientPushWithURI

PushQueryOrPayloadTooLarge

InvalidExpirationError

MissingPushIdError

MissingDeviceTypeError

115

Clientinitiated push
is not
enabled.
Check your
Parse app's
push
notication
settings.

115

RESTinitiated push
is not
enabled.
Check your
Parse app's
push
notication
settings.

115

Clientinitiated push
cannot use
the "uri"
option.

115

Your push
query or data
payload is too
large. Check
error
message for
more details.

138

Invalid
expiration
value.

156

A push id is
missing.
Deprecated.

157

The device
type eld is
missing.
Deprecated.

File related errors


Name

Code

Description

InvalidFileName

122

An invalid
lename was
used for
ParseFile. A
valid le name
contains only azA-Z0-9_.
characters and
is between 1
and 128
characters.

MissingContentType

126

Missing content
type.

MissingContentLength

127

Missing content
length.

InvalidContentLength

128

Invalid content
length.

FileTooLarge

129

File size
exceeds
maximum
allowed.

FileSaveError

130

Error saving a
le.

FileDeleteError

131

File could not


be deleted.

Installation related errors


Name

InvalidInstallationIdError

InvalidDeviceTypeError

Code

Description

132

Invalid
installation
id.

133

Invalid
device
type.

InvalidChannelsArrayError

MissingRequiredFieldError

ChangedImmutableFieldError

134

Invalid
channels
array value.

135

Required
eld is
missing.

136

An
immutable
eld was
changed.

Purchase related errors


Name

ReceiptMissing

InvalidPurchaseReceipt

PaymentDisabled

InvalidProductIdentifier

ProductNotFoundInAppStore

InvalidServerResponse

Code

Description

143

Product
purchase
receipt is
missing.

144

Product
purchase
receipt is
invalid.

145

Payment is
disabled on
this device.

146

The
product
identier is
invalid.

147

The
product is
not found
in the App
Store.

148

The Apple
server
response is
not valid.
The

149

ProductDownloadFilesystemError

product
fails to
download
due to le
system
error.

User related errors


Name

UsernameMissing

PasswordMissing

UsernameTaken

UserEmailTaken

UserEmailMissing

UserWithEmailNotFound

SessionMissing

Code

Description

200

The
username
is missing
or empty.

201

The
password
is missing
or empty.

202

The
username
has already
been
taken.

203

Email has
already
been used.

204

The email
is missing,
and must
be
specied.

205

A user with
the
specied
email was
not found.

206

A user
object
without a
valid
session
could not
be altered.

MustCreateUserThroughSignup

AccountAlreadyLinked

InvalidSessionToken

207

A user can
only be
created
through
signup.

208

An account
being
linked is
already
linked to
another
user.

209

The
device's
session
token is no
longer
valid. The
application
should ask
the user to
log in
again.

Linked services errors


Name

LinkedIdMissing

InvalidLinkedSession

Code

Description

250

A user cannot
be linked to an
account
because that
account's id
could not be
found.

251

A user with a
linked (e.g.
Facebook or
Twitter)
account has an
invalid session.
Check error
message for
more details.
Invalid auth data

InvalidGeneralAuthData

BadAnonymousID

FacebookBadToken

FacebookBadID

FacebookWrongAppID

TwitterVerificationFailed

TwitterWrongID

TwitterWrongScreenName

TwitterConnectFailure

251

value used.

251

Anonymous id is
not a valid
lowercase UUID.

251

The supplied
Facebook
session token is
expired or
invalid.

251

A user with a
linked Facebook
account has an
invalid session.

251

Unacceptable
Facebook
application id.

251

Twitter
credential
verication
failed.

251

Submitted
Twitter id does
not match the id
associated with
the submitted
access token.

251

Submitted
Twitter handle
does not match
the handle
associated with
the submitted
access token.

251

Twitter
credentials
could not be
veried due to
problems
accessing the
Twitter API.
A service being
linked (e.g.

UnsupportedService

UsernameSigninDisabled

AnonymousSigninDisabled

FacebookSigninDisabled

TwitterSigninDisabled

InvalidAuthDataError

252

Facebook or
Twitter) is
unsupported.
Check error
message for
more details.

252

Authentication
by username
and password is
not supported
for this
application.
Check your
Parse app's
authentication
settings.

252

Anonymous
users are not
supported for
this application.
Check your
Parse app's
authentication
settings.

252

Authentication
by Facebook is
not supported
for this
application.
Check your
Parse app's
authentication
settings.

252

Authentication
by Twitter is not
supported for
this application.
Check your
Parse app's
authentication
settings.

253

An invalid
authData value
was passed.
Check error
message for
more details.

LinkingNotSupportedError

Linking to an
external
account not
supported yet
with
signup_or_login.
Use update
instead.

999

Client-only errors
Name

ConnectionFailed

AggregateError

FileReadError

XDomainRequest

Code

Description

100

The connection to
the Parse servers
failed.

600

There were multiple


errors. Aggregate
errors have an
"errors" property,
which is an array of
error objects with
more detail about
each error that
occurred.

601

Unable to read input


for a ParseFile on the
client.

602

A real error code is


unavailable because
we had to use an
XDomainRequest
object to allow CORS
requests in Internet
Explorer, which strips
the body from HTTP
responses that have
a non-2XX status
code.

Operational issues
Name

Code

Description

RequestTimeout

InefficientQueryError

RequestLimitExceeded

TemporaryRejectionError

124

The request was slow and


timed out. Typically this
indicates that the request is
too expensive to run. You
may see this when a Cloud
function did not nish before
timing out, or when a
Parse.Cloud.httpRequest
connection times out.

154

An inecient query was


rejected by the server. Refer
to the Performance Guide
and slow query log.

155

This application has exceeded


its request limit. Please retry
in one minute or raise your
request limit at
https://parse.com/account
Check error message for
more details.

159

An application's requests are


temporary rejected by the
server.

If your requests are rejected with a 155 error code, your


application may be exceeding its request limit. You can
learn more about how the request limit is calculated
in the Pricing FAQ. If waiting one minute or increasing
the request limit does not resolve the issue, please le
a bug report. If your requests are being rejected with a
159 error code, please le a bug report for further
instructions on how to resolve the issue.

Other issues
Name

OtherCause

Code

Description

-1

An unknown
error or an error
unrelated to
Parse occurred.
Internal server

InternalServerError

error. No
information
available.

ServiceUnavailable

The service is
currently
unavailable.

ClientDisconnected

Connection
failure.

Want to contribute to this doc? Edit this section.

Was this section


helpful?

YES

QUICKLINKS

Products

Community

Platforms

Docs

Pricing

Download

About

Status

Blog

Policies

NEED HELP?

Get Support
Developer Forum
Open Source
STAY CONNECTED

NO

GET THE PARSE NEWSLETTER

Be the rst to know about new features,


platform support, events, and more!
nancyshmancy@parse.com

Sign up
We will never, ever spam you or sell your information. Promise.

Let's build together.

You might also like