. . .

SDK Reference

This documentation will guide you through the usage of our SDKs. Please read the Quickstart Guides to see how ApiOmat works. For a quick overview our Cheat Sheet might also be helpful.

Quickstart Guides:

Cheat Sheets:

Some of the SDKs that we offer are very similar to each other, like the JavaScript and the Titanium SDK, or the C# and Xamarin SDK. In those cases the following documentation will only mention the main used language ("JavaScript" or "C#" and the code snippets will also only contain the code once.

The SDKs are generated for each app individually and the whole content of the SDK is located in its own package / namespace ("com.apiomat.frontend" / "Apiomat", depending on the chosen SDK language). But the operations you do within these objects and classes / prototypes (create, read, update, delete, referencing, etc.) are always the same.

For this guide we’ll create an app with the following classes and attributes:

  • Class: Conference

    • name (Text)

    • ranking (Floating point)

    • image (Image)

    • confSessions (Collection of ConfSession)

    • tags (Collection of Strings)

  • Class: Room

    • number (String)

    • floor (String)

    • capacity (Long)

  • Class: Person:

    • firstname (String)

    • lastname (String)

  • Class: ConfSession

    • name (String)

    • fromTime (Date)

    • toTime (Date)

    • i18n_name (Map)

    • conference(Conference)

    • handout(File)

    • room (Room as Embedded Object)

    • attendees (Persion as Embedded Object Collection)

Working with Users

A User is a client that sends requests to the backend that you created within ApiOmat. So an object of the class User is a unique user of your website, app, etc. A User usually logs in to the backend.

To manage users, we are providing several methods. First of all, you have to configure the Datastore (or in Backbone.js: "authentication service") to be able to send requests with the user's credentials.

Android
User user = new User();
user.setUserName("user12");
user.setPassword("123asdf");
Datastore.configureWithCredentials(user);
Backbone.js
var apiomat = new Apiomat();
var userName = "myuserName";
var password = "myPassword";
var user = apiomat.User({userName, password});
user.setUserName("user");
user.setPassword("pasword");
apiomat.authenticationService().configureWithCredentials(userName, password);
C#
User user = new User() { UserName = "user12", Password = "123asdf" };
Datastore.ConfigureWithCredentials(user);
Objective-C
AOMUser* user = [[AOMUser alloc] init];
[user setUserName:@"user12"];
[user setPassword:@"1a2s3d4f"];
[AOMDatastore configureWithUser:user];
Swift
let user = User()
user.userName = "yourName"
user.password = "yourPassword"
DataStore.configureWithCredentials(user: user)
JavaScript
var user = new Apiomat.User();
user.setUserName("user12");
user.setPassword("123asdf");
Apiomat.Datastore.configureWithCredentials(user);
TypeScript
const user = new User();
user.userName = "user12";
user.password = "123asdf";
Datastore.configureWithCredentials(user);

When using the Objective-C SDK, please don’t forget the import statements for the Datastore (AOMDatastore.h) and User (AOMUser.h) class.

Additional to the user's credentials you can configure the Datastore with custom parameters like baseurl, apikey and the used system (LIVE, STAGING, TEST).

Objective-C
AOMUser* user = [[AOMUser alloc] init];
[user setUserName:@"user12"];
[user setPassword:@"1a2s3d4f"];
 
NSString *apiKey = @"1234567890";
NSString *baseURL = @"http://localhost:8080/yambas/rest/apps/Conference";
NSString *system = @"LIVE";
NSDictionary *params = @{"baseURL":baseURL,"apiKey":apiKey,"usedSystem":system};
[AMODatastore configureWithCredentials: user andParams: params];
Swift
let user = User();
user.userName = "yourName"
user.password = "yourPassword"
let configuration = DataStoreConfiguration()
configuration.apiKey = "1234567890"
configuration.baseUrl = "http://localhost:8080/yambas/rest/apps/Conference"
configuration.usedSystem = "LIVE"
 
DataStore.configureWithCredentials(user: user, configuration: configuration)

Now your connection to our servers is initialized and you can start by logging in or signing up.

To sign up with a new user, call the save function on the previously created user object. Some SDKs offer a synchronous and an asynchronous way to do this. In many frontends a synchronous function call blocks the UI thread, which in case of a function call which leads to requests to the server over the network might lead to a UI that appears to be frozen for a short time, which again leads to a bad user experience, so it's not recommended to do this. See further below for the asynchronous function calls.

Android
try {
user.save();
} catch (ApiomatRequestException e) {
// Handle exception
}
C#
user.SaveAsync().Wait();
Objective-C
@try {
[user save];
}
@catch (NSException *exception) {
/* An error occured */
}

Here are the asynchronous method calls for the same task:

Android
user.saveAsync( new AOMEmptyCallback(){
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception){}
});
Backbone.js
user.save({
success: function(model, response, options) {
user.fetch({
success : function(model, response, options) {
//successfully signed up, now we can continue
},
error : function(model, response, options) {
//sth. unexpected happened here
}
});
},
error: function(model, response, options) {
//error while saving the user
}
});

C#
await user.SaveAsync();

Objective-C
[user saveAsyncWithBlock:^(NSError *error) {
if(error) {
/* request was not successful */
}
else {
/* sign up was successful */
}
}];
Swift
user.save(loadAfterwards: true, completion: { error in
if error != nil {
//handle error
}
})
JavaScript
user.save({
onOk: function() {
//Successfully sign up
},
onError: function(error) {
//do error handling
}
});
TypeScript
try {
await user.save();
} catch (e) {
//do error handling
}

If the user already exists you can log in the user instead:

Android
try {
user.loadMe();
} catch (ApiomatRequestException e) {
// Handle exception
 
}
Backbone.js
apiomat.Users().fetch({
search: "userName==\"" + userName + "\"", // there has to be a variable called userName, containing your user's name, as shown in the code snippet above
success: function(model, response, options) {
// user found, we can go on
},
error: function(model, response, options) {
// do error handling
}
});
C#
try
{
await user.LoadMeAsync();
}
catch (ApiomatRequestException ex)
{
// Handle exception
 
}
Swift
user.loadMe { error in
if error != nil {
//handle error
}
}
JavaScript
user.loadMe({
onOk: function() {
//Successfully logged in.
},
onError: function(error) {
//do error handling
}
});
TypeScript
try {
await user.loadMe();
} catch (e) {
// do error handling
}

In many cases you might want to combine the two methods above - you want to try logging the user in, and if this leads to an error because the user doesn't exist yet, sign him up (create the account). There is a common pattern for doing that:

Android
final User user = new User();
user.setUserName("_username_");
user.setPassword("_password_");
Datastore.configureWithCredentials(user);
user.loadMeAsync(new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
if (exception != null) {
// if the member is not found on the server just create it
user.saveAsync(new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});
}
}
});
Backbone.js
var apiomat = new Apiomat();
var userName = "myuserNam4235e";
var password = "myPassword";
var user = apiomat.User({userName, password});
user.set("userName", userName);
user.set("password", password);
apiomat.authenticationService().configureWithCredentials(userName, password)
apiomat.Users().fetch({
search: "userName==\"" + userName + "\"", // there has to be a variable called userName, containing your user's name, as shown in the code snippet above
success: function(model, response, options) {
if(model.length == 0) {
user.save({
success: function(model, response, options) {
//user was successfully saved
},
error: function(model, response, options) {
//error while saving user
}
});
}
},
error: function(model, response, options) {
user.save({
success: function(model, response, options) {
//user was successfully saved
},
error: function(model, response, options) {
//error while saving user
}
});
}
});
C#
private async Task<User> LogInOrSignUpUserAsync(string userName, string password)
{
User user = new User() { UserName = userName, Password = password };
Datastore.ConfigureWithCredentials(user);
bool userExists = false;
try
{
await user.LoadMeAsync();
userExists = true;
}
catch (ApiomatRequestException e)
{
if (e.Status == Status.UNAUTHORIZED)
{
userExists = false;
}
else
{
throw e;
}
}
if (!userExists)
{
// Regarding this saving of the user outside of the previous catch block, please read the section "Error handling" in the documentation below.
await user.SaveAsync();
}
// only needed if you want to use the user to configure the Datastore again later
// user.Password = password;
return user;
}
Objective-C
AOMUser* user = [[AOMUser alloc] init];
[user setUserName:@"yourName"];
[user setPassword:@"yourPassword"];
[AOMDatastore configureWithUser:user];
[user loadMeAsyncWithFinishingBlock:^(NSError *error) {
if([error code] == AOMUNAUTHORIZED)
{
[user saveAsyncWithBlock:^(NSError *error) {
}];
}
}];
Swift
let user = User()
user.userName = "yourName"
user.password = "yourPassword"
DataStore.configureWithCredentials(user: user)
user.loadMe { error in
if error != nil {
user.save(loadAfterwards: true, completion: { error in
if error != nil {
//handle error
}
})
//handle error
}
}
JavaScript
var user = new Apiomat.User();
user.setUserName("user12");
user.setPassword("123asdf");
Apiomat.Datastore.configureWithCredentials(user);
 
user.loadMe({
onOk: function() {
//Successfully logged in.
},
onError: function(error) {
user.save({
onOk: function() {
//Successfully sign up
},
onError: function(error) {
//do error handling
}
});
}
});
TypeScript
async function logInOrSignUpUserAsync(username: string, password: string) {
const user = new User();
user.userName = username;
user.password = password;
Datastore.configureWithCredentials(user);
 
let userExists = false;
try {
await user.loadMe();
userExists = true;
} catch (error) {
if (error.statusCode == AOMStatus.UNAUTHORIZED) {
userExists = false;
} else {
// do error handling
}
}
if(userExists === false){
await user.save();
}
}

Be aware that the password field of the user object is empty after loading and saving, because the password doesn’t get transferred from the server. This is not an issue because at the beginning of the method the Datastore already got configured and all following requests have the correct credentials. But in case you want to configure the Datastore to something else and then back to the user, you might want to store the password somewhere safe, or set it as value of the password attribute afterwards.

Using OAuth2

For authenticating users, you can use our OAuth2 implementation. Read the dedicated documentation page to learn more about it: OAuth2.

Handling Objects

Let’s have a look at how we work with plain objects and do CRUD operations on them. Every object in our system has an ID, which is also part of the object's HREF. This ID and URL identify the object.

Depending on the SDK, there are synchronous and asynchronous functions to send requests to the server. Remember, synchronous function calls usually block the UI thread and are not recommended. The asynchronous function need a way to return a value - this is solved either with callbacks / blocks / completion handlers or with the async/await pattern.

For some SDKs the callback handling requires more explanation, so here's some code that shows you how a callback generally looks like:

Backbone.js
{
success: function(model, response, options) {
/**
/* Request successful
/* If server returns anything than you will find this in the model variable
/* further informations you can find in the response
*/
},
error: function(model, response, options) {
/**
/* Request failed
/* see also section about error handling below
*/
}
}
JavaScript
{
onOk: function([result]) {
//Request successful
//If server returns anything than you will find this in the result variable
},
onError: function(error) {
/*
Request failed
the parameter error contains more information
see also section about Error handling below
*/
}
}

The following section will show you how to create, read, update and delete objects synchronously and asynchronously in the different SDKs.

Save an object

Saving an object in the backend always follows the same pattern:

Android
Conference conference = new Conference();
conference.setName("Our first conference");
// We give the save command an additional parameter which is an new object of class AOMEmptyCallback. The isDone method will be executed after the request is finished. You can check the error parameter to see if something went wrong.
conference.saveAsync(new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception){}
});
Backbone.js
var conference = apiomat.Conference();
conference.save({
success : function(model, response, options) {
//object successfully saved
Apiomat.log.info("Saved succesfully Conference");
},
error: function(model, response, options) {
//handle error
}
});
C#
Conference conference = new Conference();
conference.Name = "Xamarin Evolve 2014";
// In C# there are no checked exceptions like there are in Java, so you don't have to try/catch here, but it's advised to do so. This will be covered in more detail in the documentation below under the topic "Error handling".
await conference.SaveAsync();
Objective-C
Conference *conf = [[Conference alloc] init];
[conf setName:@"Our first conference"];
/* set some other attributes here */
/* We give the save command a parameter of type "AOMEmptyBlock" (AOMEmptyBlock is only a type definition). This block will be executed after the request is finished. You can check the error parameter to see if something went wrong */
[conf saveAsyncWithBlock:^(NSError *error) {
if(!error) {
/* everything was ok */
} else {
/* sth went wrong */
}
}];
Swift
let conf = Conference()
conf.name = "our first conference"
/* set some other attributes here */
conf.save { error in
if error == nil {
//everything okay
}
else {
//something went wrong, handle error
}
}
JavaScript
var conference = new Apiomat.Conference();
//you may set some attributes here
conference.save({
onOk: function() {
//Successfully saved
},
onError: function(error) {
//do error handling
}
});
TypeScript
const conference = new Conference();
try {
await conference.save();
} catch (error) {
// do error handling
}

Getting an object

To get a single object you have to know its unique identifier (the HREF). By using following command you are able to get a specific object.

Android
Conference conference = new Conference();
conference.loadAsync(href, new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
//Now you're holding your object.
}
});
Backbone.js
var id = "0123456"
var conference = new Apiomat.Conference();
conference.loadByHref(id, {
success: function(model, response, options) {
//Successfully saved
},
error: function(model, response, options) {
//do error handling
}
});
C#
Conference conference = new Conference();
await conference.LoadAsync(href);
Objective-C
NSString *href = @"https://apiomat.org/yambas/rest/apps/Conference/models/ConferenceMain/Conference/0123456";
Conference *conf = [[Conference alloc] init];
[conf loadAsyncWithHref:href andBlock:^(NSError *error) {
/* Now you're holding your object. */
}];
Swift
let conf = Conference()
conf.load(withHref: href) { error in
if error == nil {
//successfully reloaded your conference
}
else {
//something went wrong, handle error
}
}
JavaScript
var href = "https://apiomat.org/yambas/rest/apps/Conference/models/ConferenceMain/Conference/0123456"
var conference = new Apiomat.Conference();
conference.loadWithHref(href, {
onOk: function() {
//Successfully saved
},
onError: function(error) {
//do error handling
}
});
TypeScript
const conference = new Conference();
await conference.loadWithHref(href);

If you need to reload the data from server, then type in the following:

Android
loadedConf.loadASync(new AOMEmptyCallback(){
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
//you reloaded your object
}
});
Backbone.js
loadedConference.fetch({
success: function(model, response, options) {
//successfully saved
},
error: function(model, response, options) {
//do error handling
}
});
C#
await loadedConf.LoadAsync();
Objective-C
[loadedConf loadAsyncWithBlock:^(NSError *error) {
/* you reloaded your object */
}];
Swift
conf.load { error in
if error == nil {
//successfully reloaded your conference
}
else {
//something went wrong, handle error
}
}
JavaScript
loadedObj.load({
onOk: function() {
//Successfully saved
},
onError: function(error) {
//do error handling
}
});
TypeScript
await loadedConf.load();

Getting a list of objects

You can also retrieve a list of objects from the server. To do so you have to work with the class / prototype the objects are instances of.

Android
conference.getConferencesAsync(null, new AOMCallback<List<Conference>>() {
@Override
public void isDone(List<Conference> resultObject, boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});
Backbone.js
//Load list of conference objects
apiomat.Conferences().fetch({
success: function(model, response, options) {
//Getting list was successful
//You can now work with parameter objects which contains loaded elements
},
error: function(model, response, options) {
//do error handling
}
});
C#
IList<Conference> loadedConferences = await Conference.GetConferencesAsync();
Swift
/* Load list of conference objects */
Conference.loadList { conferences, error in
if error == nil {
let loadedConferences = conferences
}
else {
//something went wrong, handle errror
}
}
JavaScript
Apiomat.Conference.getConferences(undefined,{
onOk : function(conferences) {
// loading Conferences was successful
},
onError : function(error) {
// do error handling here
}
});
TypeScript
const conferences = await Conference.getConferences();

Instead of no parameter or ‘null’ (depending on the SDK) you can also add a query to filter the objects that get returned by the server.

Backbone.js
//Load list of conference objects
apiomat.Conferences().fetch({
search: 'name=="Node Summit"'
success: function(model, response, options) {
//Getting list was successful
//You can now work with parameter objects which contains loaded elements
},
error: function(model, response, options) {
//do error handling
}
});
C#
IList<Conference> loadedConferencesFiltered = await Conference.GetConferencesAsync("name==\"Microsoft Build 2014\"");
Objective-C
/* Load list of conference objects filtered by name */
[Conference getAsyncWithQuery:@"name like \"Backend\"" withBlock:^(NSMutableArray *loadedConfs, NSError *error) {
/* Getting list was successful if exception == null */
/* You can now work with parameter loadedConfs which contains filtered elements */
}];
Swift
/* Load list of conference objects filtered by name */
Conference.loadList("name like \"backend\"") { conferences, error in
if error == nil {
let loadedConferences = conferences
}
else {
//something went wrong, handle errror
}
}
JavaScript
Apiomat.Conference.getConferences("name==\"Microsoft Build 2014\"",{
onOk : function(conferences) {
// loading Conferences was successful
},
onError : function(error) {
// do error handling here
}
});
TypeScript
const conferences = await Conference.getConferences("name==\"Microsoft Build 2014\"");

You can find some more information about queries in the respective section on this page, or check out the dedicated documentation article about our query language.

Get the count of a list of objects

In case you just want to know the count of elements in a list (either filtered by a query or not), you don't have to fetch the full list to then call some count() method in your code. Instead, you can call the count() method in our SDK, so that the server responds with the count of elements directly:

Android
//Load count of conference objects filtered
conference.getConferencesCountAsync("name like \"JavaOne\"", new AOMCallback<Long>() {
 
@Override
public void isDone(Long resultObject, boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});
Backbone.js
//Load count of conference objects
apiomat.Conference().count({
success: function(count, response, options) {
},
error: function(model, response, options) {
//do error handling
}
});
 
//Load count of conference objects filtered
apiomat.Conference().count({
search: 'name=="Node Summit"'
success: function(model, response, options) {
},
error: function(model, response, options) {
//do error handling
}
});
C#
long conferenceCount = await GetConferencesCountAsync(); // Counts all conferences
long conferenceCountFiltered = await GetConferencesCountAsync("name like \"JavaOne\""); // Counts filtered conferences
Objective-C
long cnt = [Conference getCountWithQuery:@"name like \"JavaOne\""];
 
[Conference getCountAsyncWithQuery:@"name like \"JavaOne\"" withBlock:^(long count, NSError *error) {
cnt = count;
err = error;
}];
Swift
// Count all conferences
Conference.loadCount { conferencesCount, error in
if error == nil {
if let conferencesCount = conferencesCount {
// work with conferencesCount
}
else {
// handle error
}
}
 
// Count filtered conferences
Conference.loadCount(query: "name like \"JavaOne\"") { conferencesCount, error in
if error == nil {
if let conferencesCount = conferencesCount {
// work with conferencesCount
}
else {
// handle error
}
}
JavaScript
//Load count of conference objects
Apiomat.Conference.getConferencesCount(undefined, {
onOk : function(cnt) {
// count is in variable cnt
},
onError : function(error) {
// handle error here
}
});
 
//Load count of conference objects filtered
Apiomat.Conference.getConferencesCount("name like \"JavaOne\"", {
onOk : function(cnt) {
// count is in variable cnt
},
onError : function(error) {
// handle error here
}
});
TypeScript
const conferenceCount = await Conference.getConferencesCount(); // Counts all conferences
const conferenceCountFiltered = await Conference.getConferencesCount("name like 'TypeScript'"); // Counts filtered conferences

Update an object

It’s identical to saving. The client code checks, if the object already contains an ID / HREF and afterwards performs an update. For example:

Android
// Get or save updatedObj
// Change sth on updatedObj
conference.saveAsync( new AOMEmptyCallback(){
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
//Successfully updated
}
});
Backbone.js
var conference = apiomat.Conference();
conference.save({
success : function(model, response, options) {
//object successfully saved
Apiomat.log.info("Saved succesfully Conference");
},
error: function(model, response, options) {
//handle error
}
});
C#
// Get or save a conference
// ...
// Change something on the conference
// ...
// Update the conference
await conference.SaveAsync();
Objective-C
/* Get or save updatedObj */
/* Change sth on updatedObj */
/* udpate it on server */
[updatedObj saveAsyncWithBlock:^(NSError *error) {
}];
Swift
/* Get or save updatedObj */
/* Change something on updatedObj */
/* udpate it on server */
updatedConf.save { error in
if error == nil {
//everything okay
}
else {
//something went wrong, handle error
}
}
JavaScript
// Get or save updatedObj
// Change sth on updatedObj
updatedObj.save({
onOk: function() {
//Successfully updated
},
onError: function(error) {
//do error handling
}
});
TypeScript
// Get or save updateObj
// Change sth on updateObj
await updateObj.save();

Delete an object

According to our pattern, you can delete an object simply by entering the following:

Android
// Load object you want to delete
conference.deleteAsync(new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});
Backbone.js
conference.destroy({
success : function(model, response, options) {
//object successfully deleted
},
error: function(model, response, options) {
//handle error
}
});
C#
await conference.DeleteAsync();
Objective-C
//Load object you want to delete
[conf deleteAsyncWithBlock:^(NSError *error) {
/* check error if sth went wrong */
}];
Swift
//First load object you want to delete (e.g. a conference), then call
conf.delete { error in
if error == nil {
//successfully deleted
}
else {
//something went wrong, handle error
}
}
JavaScript
obj.deleteModel({
onOk: function() {
//Successfully deleted
},
onError: function(error) {
//do error handling
}
});
TypeScript
await conference.deleteModel();

Offline Handling

We extended the offline handling capabilities and have a dedicated documentation page now. See offline handling.

DeltaSync

For reducing the amount of data being sent between the device and backend, you can enable DeltaSync. Read the dedicated documentation page to learn more about it: DeltaSync.

Working with attributes of objects

Of course you can access attributes that you’ve created in your dashboard the standard Java / C# / JavaScript / Objective-C / Swift / etc way. Please be careful to use the appropriate data type. It’s not recommended to add new attributes to generated classes, because it will break serialisation if you call the “save()” method to send an instance of that class to the server.

Setting an attribute

Assuming our conference class has an attribute called ‘name’ , you can set this property easily with:

Android
Conference conference = new Conference();
conference.setName("Android Dev Con");
//save the object
Backbone.js
var conference = apiomat.Conference();
conference.set("name", "JavaScript Dev Con");
C#
// Either with an object initializer
Conference conf1 = new Conference() { Name = "Microsoft Build 2014" };
// Or with the property of the object
Conference conf2 = new Conference();
conf2.Name = "Apps World Europe 2014";
// don't forget to save the objects
Objective-C
Conference *conf = [[Conference alloc] init];
[conf setName:@"The iOS dev conference"];
Swift
Conference conf = Conference()
conf.name = "The iOS dev conference"
JavaScript
var conference = new Apiomat.Conference();
conference.setName("JavaScript Dev Con");
TypeScript
const conference = new Conference();
conference.name = "TypeScript Dev Conf";

or set an attribute “someMap” of ApiOmat type map, which looks like this:

Android
Map<String, String> map = new HashMap<String,String>();
map.put("de", "Wie benutze ich properties?");
map.put("en", "How to use properties");
ConfSession confSession = new ConfSession();
confSession.setI18n_name(map);
Backbone.js
var i18ns = {
"de" : "Wie benutze ich Properties",
"en" : "How to use properties"
};
confSession.set("i18n_name", i18ns);
//save object on server
conference.save({
success : function(model, response, options) {
//object successfully saved
Apiomat.log.info("Conference successfully saved");
},
error: function(model, response, options) {
//handle error
}
});
C#
// Either with a dictionary initializer
IDictionary dict = new Dictionary<string,string< ()
{
{"de", "Wie benutzt man Properties?"},
{"en", "How to use properties?"}
};
// or after the creation
dict.Add("fr", "Comment utiliser les propriétés");
// then set the conference's property accordingly
ConfSession confSession = new ConfSession();
confSession.I18n_name = dict;
Objective-C
NSMutableDictionary *i18n = [[NSMutableDictionary alloc] init];
[i18n setObject:@"wie benutze ich properties" forKey:@"de"];
[i18n setObject:@"how to use properties" forKey:@"en"];
Session *session = [[Session alloc] init];
[session setI18n_name:i18n];
Swift
var i18n = [String: String]()
i18n["de"] = "wie benutze ich Properties"
i18n["en"] = "how to use properties"
let session = Session()
session.i18n_name = i18n as [String : AnyObject]
JavaScript
var i18ns = {
"de" : "Wie benutze ich Properties",
"en" : "How to use properties"
}
confSession.setI18n_name(i18ns);
//save object on server
confSession.save();
TypeScript
confSession.i18n_name = {
de: "Wie benutze ich Properties",
en: "How to use properties"
};
await confSession.save();

Getting an attribute

Like accessing “normal” attributes, you can also access properties of ApiOmat classes.

Android
//Make sure you loaded object conference before
String confName = conference.getName();
Backbone.js
//Make sure you loaded object conference before (see above)
var confName = conference.get("name");
C#
// Make sure you loaded the conference object before
string confName = conference.Name;
Objective-C
/* Make sure you loaded object conference before (see above) */
NSString *confName = [conference name];
Swift
/* Make sure you have loaded object conference before (see above) */
let confName = conf.name
JavaScript
//Make sure you loaded object conference before (see above)
var confName = conference.getName();
TypeScript
const confName = conference.name;

Working with geo points

You can also set ‘Location’ as type of an attribute. To save an attribute of the type “location” with name ‘place’, do:

Android
conference.setPlaceLatitude(10.2);
conference.setPlaceLongitude(20.2);
//To retrieve coordinates, you can call the methods 'getLatitude' and 'getLongitude'.
double latitude = conference.getPlaceLatitude();
double longitude = conference.getPlaceLongitude();
Backbone.js
conference.setPlaceLatitude(10.2);
conference.setPlaceLongitude(20.2);
// To retrieve coordinates, you can call the methods get<PropertyName>Latitude() and get<PropertyName>Longitude()
var latitude = conference.getPlaceLatitude();
var longitude = conference.getPlaceLongitude();
C#
conference.PlaceLatitude = 10.2;
conference.PlaceLongitude = 20.3;
// Access the properties in the same way
double latitude = conference.PlaceLatitude;
double longitude = conference.PlaceLongitude;
Objective-C
[conf setPlaceLatitude:10.2];
[conf setPlaceLongitude:20.2];
//To retrieve coordinates,
//you can call the methods 'latitude' and 'longitude'.
double latitude = [conf placeLatitude];
double longitude = [conference placeLongitude];
Swift
conf.place?.longitude = 10.2
conf.place?.latitude = 20.2
//To retrieve coordinates,
//you can access the corresponding attributes of your your location-typed property
let confLng = conf.place?.longitude
let confLat = conf.place?.latitude
JavaScript
conference.setPlaceLatitude(10.2);
conference.setPlaceLongitude(20.2);
// To retrieve coordinates, you can call the methods 'getLatitude' and 'getLongitude'.
var latitude = conference.getPlaceLatitude();
var longitude = conference.getPlaceLongitude();
TypeScript
const location: AOMLocation = {
latitude: 20.5,
longitude: 30.9,
};
conference.place = location;

Working with files

Objective-C SDK

Instead of a byte array you have to use the class NSData.

Backbone.js SDK

Starting with version 2.5.6 the Backbone.js SDK provides a load and post method named load<PropertyName>AsByteArray and post<PropertyName>AsByteArray. To use these methods, the JQuery plugin BinaryTransport is necessary. For further information have a look at the Backbone.js Quickstart Guide.

If you have an attribute of the type “File” you can upload a file either as a byte array or as stream (or in Android directly as "File"). The method which uploads your file is called post<Attributename>. For our “handout” in the ConfSession class it should look like this:

Android
// As byte array:
byte[] filearr = {0,3,6,2};
ConfSession confsession = new ConfSession();
confsession.postHandoutAsync(filearr, new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});
 
// As stream (for streams where mark and reset is supported)
InputStream inputStream = new ByteArrayInputStream(filearr);
ConfSession confsession = new ConfSession();
confsession.postHandoutAsync(inputStream , new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
}); 
 
// As file
File file = new File(path);
ConfSession confsession = new ConfSession();
confsession.postHandoutAsync(file , new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});  
Backbone.js
// As byte array
var filearr = [0,3,6,2];
var confSession = apiomat.ConfSession();
confSession.postHandoutAsByteArray(filearr, {
success : function(model, response, options) {
// file content successfully posted
Apiomat.log.info("file posted successfully");
},
error: function(model, response, options) {
// handle error
}
});
 
// As stream (this could be any kind of stream, especially useful when working with files from the device like photos or videos)
// Therefore you have to pass a file object to the function, resulting from a form's file input
 
var file = new File(filearr, "filename");
confSession.postHandoutAsStream(file, {
success: function(result, status, jqXHR) {
//file content successfully posted
Apiomat.log.info("file posted successfully");
},
error: function(error, status, jqXHR) {
//handle error
}
}
C#
// As byte array:
byte[] fileArr = new byte[] {0x00, 0x30, 0x60, 0x20};
ConfSession confSession = new ConfSession();
await confSession.PostHandoutAsync(fileArr);
 
// As stream (this could be any kind of stream, especially useful when working with files from the device like photos or videos):
MemoryStream stream = new MemoryStream(fileArr);
await confSession.PostHandoutAsync(stream);
Objective-C
// As NSMutableData
NSMutableData *fileData = [[NSMutableData alloc] init];
/* set value for property fileData */
ConfSession *confSession = [[ConfSession alloc] init];
[confSession postHandoutAsync:fileData andWithBlock:^(NSError *error) {
}];
 
// As stream (this could be any kind of stream, especially useful when working with files from the device like photos or videos):
NSInputStream* stream = [NSInputStream inputStreamWithData: fileData];
[confSession postHandoutAsStreamAsync: stream andWithBlock:^(NSError *error) {
if(error == nil) {
// data successfully posted
}
else {
// something went wrong, handle error
}
}]
Swift
// As byte array
let data = NSMutableData()
let confSession = ConfSession()
confSession.postHandout(data) { error in
if error == nil {
//data successfully posted
}
else {
//something went wrong, handle error
}
}
 
// As stream (this could be any kind of stream, especially useful when working with files from the device like photos or videos):
let stream = file as Data?
confSession.postHandout(stream!) { error in
if error == nil {
// data successfully posted
}
else {
// something went wrong, handle error
}
}
JavaScript
// _data is an array containing the example data
_data = [0,3,6,2];
// session is an object of a class that has a file attribute "slides"
session.postSlides(_data, {
onOk : function() {
// data successfully posted
},
onError : function(error) {
// handle error here
}
});
TypeScript
// _data is a byte-array containing the example data
const _data = [0,3,6,2];
// session is an object of a class that has a file attribute "slides"
await session.postSlides(_data);

Please note two things:

1) There's an upload size limit on the server. It can be configured in the apiomat.yaml and defaults to 200 MB.

2) There's a limit for offline storage in the device. The limit gets ignored when uploading while the device is online (nothing gets stored this way anyway), but an exception gets thrown if the device is offline. When downloading a file, it only gets cached (either in memory or persistently, depending on the setting) if the limit isn't exceeded, otherwise an error will be logged (but no exception thrown). This limit can be configured via the Datastore and defaults to 15 MB.

After the file object was uploaded, the property <property>URL contains an URL to this file.

Android
string slidesUrlPlain = conference.getSlidesResourceURL(); // Without the addition of API key and system
string slidesUrl = conference.getSlidesUrl(); // With the addition of API key and system
Backbone.js
//Get plain URL without concerting parameters
var slidesUrl = conference.get("slidesURL");
C#
string slidesUrlPlain = conference.SlidesURLResource; // Without the addition of API key and system
string slidesUrl = conference.SlidesUrl; // With the addition of API key and system
Objective-C
NSString *handoutURL = [confSession handoutURL];
Swift
let handoutURL = confSession.handoutURL
JavaScript
var slidesURL = session.getSlidesURL();
TypeScript
const slidesURL = session.slidesURL;

To load the file, you can either use a method on the object that contains the file directly, or if you only have a URL of the file and not the object that contains the file you can use a method in the Datastore, called loadResourceAsyncWithHref().

Android
// Via the object that contains the file:
byte[] result = session.LoadSlidesAsync(new AOMEmptyCallback() { // usePersistentStorage is optional
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});
 
 
/* Indicating whether the loaded image should be cached using the persistent storage or not,
for more information have a closer look at the caching topic */
boolean usePersistentStorage = false;
 
// Via Datastore when you only have a file's URL
byte[] result2 = await Datastore.getInstance().LoadResourceAsync(fileUrl, false, usePersistentStorage, new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});
Backbone.js
apiomat.loadResource(slidesURL, {
asByteArray: true,
success: function(data, xhr, options) {
// imageData was loaded successfully
},
error: function(xhr, options) {
// handle error
}
})
 
/* Alternatively you can use the following method as well */
confSession.loadHandoutAsByteArray({
success : function(model, response, options) {
// file content successfully posted
Apiomat.log.info("file posted successfully");
},
error: function(model, response, options) {
// handle error
}
});
C#
// Via the object that contains the file:
byte[] result = await session.LoadSlidesAsync(); // usePersistentStorage is optional
 
// Via Datastore when you only have a file's URL
byte[] result2 = await Datastore.Instance.LoadResourceAsync(fileUrl, false, false, usePersistentStorage);
Objective-C
NSString *handoutURL = [confSession handoutURL];
[[AOMDatastore sharedInstance] loadResourceAsyncWithHref:handoutURL andWithFinishingBlock:^(NSData *data, NSError *error) {
/* data object contains your file data */
}];
 
 
/* Alternatively you can use the following method as well */
[confSession loadHandoutAsync:^(NSData *data, NSError *error) {
/* data object contains your file data */
}];
Swift
let handoutURL = confSession.handoutURL
DataStore.sharedInstance.loadResource(withHref: handoutURL, usePersistentStorage: false) { data, error in
if error != nil {
/* data object contains your file data */
}
}
 
/* Alternatively you can use the following method as well */
confSession.loadHandout { (data, error) in
if error != nil {
/* data object contains your file data */
}
}
JavaScript
var loadCB = {
onOk : function(data) {
// data successfully loaded, do something with it
},
onError : function(error) {
// handle error here
}
};
/* you can either load it via Datastore */
var slidesURL = session.getSlidesURL();
Apiomat.Datastore.getInstance().loadResource( slidesURL, loadCB );
/* Or alternatively you can use the following method as well */
session.loadSlides( loadCB );
TypeScript
/* you can either load it via Datastore */
const data = await Datastore.Instance.loadResource(session.slidesURL);
 
/* Or alternatively you can use the following method as well */
const loadedSlides = await session.loadSlides();

Working with images

It’s possible to add a property of type “image” to your classes. If you do this, you can find some special methods in the generated code for these classes. Upload an image to an instance of your class as a byte array / array buffer. The method which adds an image is named after the pattern “Post<Propertyname>”. For our “Conference” class and its property “image” it should look like in following snippet:

For Backbone.js: Take a look at this page if you don’t know how you transform an image to the required type

In Objective-C, you have to upload the file as an instance of the class NSData.

Android
byte[] imgArr = new byte[] {0,2,6,8};
conference.postImageAsync(imgArr, new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});
Backbone.js
var filearr = [0,3,6,2];
var confSession = apiomat.ConfSession();
confSession.postHandoutAsByteArray(filearr, {
success: function(model, response, options) {
// image data posted successfully
},
error: function(model, response, options) {
// do error handling
}
});
C#
byte[] fakeImage = new byte[] {0x00, 0x30, 0x60, 0x20};
ConfSession confSession = new ConfSession();
await confSession.PostImageAsync(fakeImage);
Objective-C
 NSMutableData *imageData = [[NSMutableData alloc] init];
/* set value for property imageData */
/* save data in image property of conf object */
[conf postImageAsync:imageData andWithBlock:^(NSError *error) {
/* check error parameter */
}];
Swift
let data = NSMutableData()
let conf = Conference()
conf.postImage(data) { error in
if error == nil {
//data successfully posted
}
else {
//something went wrong, handle error
}
}
JavaScript
var imgArr = [0,2,6,2];
conference.postImage(imageArr,{
onOk: function() {
//Successfully uploaded image
},
onError: function(error) {
//do error handling
}
});
TypeScript
const imageData = [0,2,5,3]; // image data as byte array
const conference = new Conference();
await conference.postImage(imageData);

If the image was uploaded, the property ‘<PropertyName> URL’ contains an URL to the image. To get this image URL back from your object, you can do the following:

Android
//Get plain URL without concerting parameters
String imgUrl = test.getImageURL();
//Get URL with resized image to 200x100, black background, no transparency and in format jpg
String resizedImgUrl = test.getImageURL(200, 100, "ffffff", null, "jpg");
Backbone.js
//Get plain URL without concerting parameters
var imgUrl = conference.get("imageURL");
C#
// 1) Get plain URL without concerting parameters from the property
string imgUrl = conference.ImageUrl;
// 2) Get URL with resized image to 200x100, black background, no transparency and as jpg
string resizedImgUrl = conference.GetImageUrl(200, 100, "ffffff", null, "jpg");
Objective-C
/* Get plain URL without concerting parameters */
NSString *imageURL = [conf imageURL];
/* Get URL with resized image to 200x100, black background, no transparency and in format jpg */
NSString *resizedImageURL = [conf scaledImageURLWithWidth:200 andHeight:100 andBGColor:@"ffffff" andAlpha:0 andFormat:@"jpg"];
Swift
conf.loadScaledImage(width: 200, height: 100, backgroundColorAsHex: "ffffff", alpha: 0, format: "jpg") { data, error in
if error == nil {
let imageData = data
}
else {
//something went wrong, handle error
}
}
JavaScript
//Get plain URL without concerting parameters
var imgUrl = conference.getImageURL();
//Get URL with resized image to 200x100
var resizedImgUrl = conference.getImageURL(200,100);
TypeScript
//Get plain URL without concerting parameters
const imgUrl = conference.imageURL;
//Get URL with resized image to 200x100
const resizedImgUrl = conference.getImageURL(200,100);

If you only set either the width or height parameter, the image will be resized while the scale will be kept (the other parameter will be calculated accordingly).

When using the Objective-C SDK, there are 2 possibilities to download an image:

  • Use your own NSURLConnection or anything else

  • Use loadResourceAsyncWithHref of DataStore class (see section above for example)

When using a file URL in a web page you can add the URL parameter asstream=true. This changes the file handling and enables the following behaviour:

  • When clicking on the link and the file is for example a PDF, the browser detects the file type correctly and can show the PDF directly

  • When downloading the file

    • Your OS detects the file type and can propose to open the file with a program that supports the file type

    • When saving, the proposed name includes the file type, e.g. "report.pdf" instead of just "report"

Working with One/Limited Time Access Links

JavaScript
//Get token URL for file
conference.getFileTokenURL({
onOk : function(tokenUrl) {
 
},
onError : function(error) {
//do error handling
}
 
}, 3600);
//Get token URL for image
conference.getImageTokenURL({
onOk : function(tokenUrl) {
 
},
onError : function(error) {
//do error handling
}
 
}, 3600);

Handling References

By adding attributes containing references, you are able to build data structures in an object oriented manner. You can reference a single or a list of classes.

You can add a list of type ConfSession to your Conference class or set a Conference as a reference of the ConfSession class, for example. This would resemble a one-to-many relationship: [Conference]-1—n-[ConfSession].

The generated SDKs will contain several methods starting with ‘post..’, ‘load…’ or ‘remove…’ afterwards.

Add a reference

Our example Conference class contains a property ‘confSessions’ which is of type ‘Collection of ConfSession’. To add a ConfSession to an object of type Conference you can do the following:

Android
//Create and save an object of type ConfSession (see above)
//Create and save an object of type Conference (see above)
conference.postConfSessionAsync(confSession, new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});
Backbone.js
//Create and save an object of type ConfSession (see above)
//Create and save an object of type Conference (see above)
conference.postSessions(confSession, {
success: function(model, response, options) {
//Successfully saved the reference on object conference
},
error: function(model, response, options) {
//do error handling
}
});
C#
// Create and save an object of type ConfSession (see above on how to do that)
// Create and save an object of type Conference (see above on how to do that)
await conference.PostConfSessionAsync(confSession);
Objective-C
/* Create and save an object (called confSession) of type ConfSession (see above) */
/* Create and save an object (conference) of type Conference (see above) */
[conference postConfSessionsAsync:confSession andWithBlock:^(NSError *error) {
}];
Swift
/* Create and save an object (called confSession) of type ConfSession (see above) */
/* Create and save an object (conference) of type Conference (see above) */
conference.postConfSessions(confSession) { href, error in
if error == nil {
//confSession successfully posted, href contains the href to the posted reference
}
else {
//something went wrong, handle error
}
};
JavaScript
//Create and save an object of type ConfSession (see above)
//Create and save an object of type Conference (see above)
conference.postConfSessions(confSession,{
onOk: function() {
//Successfully saved the reference on object conference
},
onError: function(error) {
//do error handling
}
})
TypeScript
//Create and save an object of type ConfSession (see above)
//Create and save an object of type Conference (see above)
await conference.postConfSessions(confSession);

Get references

To retrieve the referenced object(s) you have to call the “load” function for this property. This function downloads the object(s) from the backend. Depending on the SDK, you can either get the loaded objects from the return value or from a property that stores the values after they have been loaded:

Android
//Assume that our object conference has already referenced confSessions
conference.loadConfSessionAsync(null, new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
//if there is no exception you can access the local list on conference object
List<confsession> sessions = conference.getConfSessions();
}
});
Backbone.js
//Assume that our object conference has already referenced confSessions
conference.loadSessions({
success: function(model, response, options) {
//Successfully returned references
//Access local property
var confSessions = model;
},
error: function(model, response, options) {
//do error handling
}
});
C#
// Assume that our object conference already contains referenced confSessions
// either use the return value
IList<ConfSession> sessions = await conference.LoadConfSessionAsync();
// or the property
sessions = conference.ConfSessions;
Objective-C
/* Assume that our object conference has already referenced confSessions */
[conference loadConfSessionsAsync:@"" andWithBlock:^(NSError *error) {
/* if there is no exception you can access the local list on conference object*/
NSMutableArray *loadedSessions = [conference sessions];
}];
Swift
/* Assume that our object conference has already referenced confSessions */
conference.loadConfSessions { sessions, error in
if error == nil {
let loadedSessions = sessions
}
else {
//something went wrong, handle error
}
}
JavaScript
//Assume that our object conference has already referenced confSessions
conference.loadConfSessions(undefined, {
onOk: function() {
//Successfully returned references
//Access local property
var confSessions = conference.getConfSessions();
},
onError: function(error) {
//do error handling
}
});
TypeScript
//Assume that our object conference has already referenced confSessions
const confSessions = await conference.loadConfSessions();
//Or use the property if the sessions are already loaded
const loadedSessions = conference.confSessions;

Instead of ‘null’ / 'undefined' / empty parameter list you can set a query parameter to filter the list. For this please see the Query section below or the documentation article about Queries.

Update a reference

If you change properties of an object which is already referenced to another object and send an update to the server, than the reference will also be changed. If you call “load” again you will retrieve the updated reference.

Hint: If you add an existing reference to the same object again, it will not be added as new reference again.

Delete reference

Delete a reference between objects is straightforward.

Android
//Assume that conference contains a reference to confSession
//Assume that both objects (conference, confSession) are loaded from server
conference.removeConfSessionAsync(confSession, new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
}
});
Backbone.js
//Assume that conference contains a reference to confSession
//Assume that both objects (conference, confSession) are loaded from server
conference.removeConfSessions(confSession, {
success: function(model, response) {
//successfully removed reference
},
error: function(model, response) {
//do error handling
}
});
C#
// Assume that conference contains a reference to confSession
// Assume that both objects (conference, confSession) are loaded from server
await conference.RemoveConfSessionAsync(confSession);
Objective-C
/* Assume that conference contains a reference to confSession */
/* Assume that both objects (conference, confSession) are loaded from server */
[conference removeConfSessionsAsync:confSession andWithBlock:^(NSError *error) {
}];
Swift
/* Assume that conference contains a reference to confSession */
/* Assume that both objects (conference, confSession) are loaded from server */
conference.removeConfSession(confSession) { error in
if error == nil {
//confSession successfully removed
}
else {
//something went wrong, handle error
}
}
JavaScript
// Assume that conference contains a reference to confSession
// Assume that both objects (conference, confSession) are loaded from server
conference.removeConfSessions(confSession);
TypeScript
// Assume that conference contains a reference to confSession
// Assume that both objects (conference, confSession) are loaded from server
await conference.removeConfSessions(confSession);

Embedded Objects

Instead of using the relational approach, it is also possible to embed objects within one another. For a general introduction have a look at the related documentation page.

Therefore the SDKs provide methods for reading embedded objects and also embedded collections.

Example: There's a class "Session" and a class "Room". Session has an attribute of the type Room and the attribute is configured as embedded reference. Now, if you have an object of the class Session, you can call the method loadRoomAsync(), which will return the referenced object of type Room. Just so you know what happens in the background here: Instead of loading the single Room object, a load() call on the whole "parent" (embedding) object gets executed. So the Session gets loaded in order for it to contain the latest Room JSON and then the Room gets "extracted" from it.

Embedded Objects are on client side currently only supported (partially) in the C# SQLite and Xamarin SQLite SDK.
As it is currently only possible to read embedded objects in the SDK, no post or delete methods are generated. It's also not possible to filter the elements of an embedded collection by using a query.
Please also note that offline handling using the persistent storage is not working for embedded objects and collections of embedded objects, but using the in-memory cache is already supported.

Create, update and delete actions and full offline handling support will be possible in a future version. Please talk to our sales representatives or project managers if you would like these features to be prioritized.

The static count methods will not include embedded objects or objects included within a collection of embedded objects, as they are not accessible directly anyway.

Example: A Class "Conference" has an embedded reference collection to the class "Session". So one conference has multiple sessions. Let's say there are 2 conferences saved on their own, and 20 sessions saved on their own. The conferences also have 5 sessions embedded. When calling Session.count() or sending a GET request to /count for the Session class, the result will be 20, not 30 (20+2*5). Only these 20 sessions are directly accessible (by their own ID) instead of via their parent (embedding) objects.

C# (SQLite)
// After loading an existing session you can immediately access any embedded object or collection of embedded objects
Room room = existingSession.Room;
IList<Person> attendees = existingSession.Attendees;
 
 
// Calling the load method on an embedded attribute will cause the embedding session object the be reloaded
Room room = await existingSession.loadRoomAsync().ConfigureAwait(false);
 
IList<Person> attendees = await existingSession.LoadAttendeesAsync ().ConfigureAwait(false);
TypeScript
// After loading an existing session you can immediately access any embedded object or collection of embedded objects
const room = existingSession.room;
const attendees = existingSession.attendees;
 
// Calling the load method on an embedded attribute will cause the embedding session object the be reloaded
const room = await existingSessions.loadRoom();
const attendees = await existingSession.loadAttendees();

Queries

Our backend supports a lot of query operations. For the full documentation please see here.

But let’s have a look at some examples.

Retrieving a filtered list

To get a list of objects you can set the query parameter.

Android
//Load list of conference objects filtered by name
Conference.getConferencesAsync("name like \"Backend\"", new AOMCallback<List<Conference>>() {
@Override
public void isDone(List<Conference> resultObject, boolean wasLoadedFromStorage, ApiomatRequestException exception) {
//Getting list was successful if exception == null
//You can now work with parameter objects which contains loaded elements
}
});
Backbone.js
//Load list of conference objects filtered by name
apiomat.Conferences().fetch({
search: "name like \"Backend\"",
success: function(model, response, options) {
//Getting list was successful
//You can now work with parameter objects which contains loaded elements
},
error: function(model, response, options) {
//do error handling
}
});
C#
// Load list of conference objects filtered by name
IList<Conference> loadedConferences = await Conference.GetConferencesAsync("name like \"Backend\"");
Objective-C
/* Load list of conference objects filtered by name */
[Conference getAsyncWithQuery:@"name like \"Backend\"" withBlock:^(NSMutableArray *loadedConfs, NSError *error) {
/* Getting list was successful if exception == null */
/* You can now work with parameter loadedConfs which contains filtered elements */
}];
Swift
/* Load list of conference objects filtered by name */
Conference.loadList("name like \"backend\"") { conferences, error in
if error == nil {
let loadedConferences = conferences
}
else {
//something went wrong, handle errror
}
}
JavaScript
//Load list of conference objects filtered by name
Apiomat.Conference.getConferences("name like \"Backend\"",{
onOk: function(objects) {
//Getting list was successful
//You can now work with parameter objects which contains loaded elements
},
onError: function(error) {
//do error handling
}
});
TypeScript
// Load list of conference objects filtered by name
const filteredConferences = await Conference.getConferences('name like "Backend"');

This will load a list of allConference objects, where the name includes the string ’backend’.

Retrieving filtered list of references

If you only want a subset of referenced objects, you can call the “load” method like this:

Android
//Assume that our object conference has already referenced confSessions
conference.loadConfSessionsAsync(confSession, new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
conference.getConfSessions();
}
});
Backbone.js
//Assume that our object conference has already referenced confSessions
conference.loadConfSessions({
search: "name ==\"backend\"",
success: function(model, response, options) {
//Successfully returned referenced confSessions with name 'backend'
//Access local property
var confSessions = model
},
error: function(model, response, options) {
//do error handling
}
});
C#
// Assume that our object conference already contains referenced confSessions
IList<ConfSession> sessions = await conference.LoadConfSessionAsync("topic=\"C#\"");
Objective-C
/* Assume that our object conference has already referenced confSessions */
[conference loadConfSessionsAsync:@"name like \"Backend\"" andWithBlock:^(NSError *error) {
NSMutableArray *filteredSessions = [conference sessions];
}];
Swift
/* Assume that our object conference has already referenced confSessions */
conference.loadConfSessions("name like \"backend\"") { confSessions, error in
if error == nil {
let loadedConfSessions = confSessions
}
else {
//something went wrong, handle errror
}
JavaScript
//Assume that our object conference has already referenced confSessions
conference.loadConfSessions("name ==\"backend\"", {
onOk: function() {
//Successfully returned referenced confSessions with name 'backend'
//Access local property
var confSessions = conference.getConfSessions();
},
onError: function(error) {
//do error handling
}
});
TypeScript
// Assume that our object conference already contains referenced confSessions
const filteredConfSessions = await conference.loadConfSessions('name=="backend"');

Error Handling

The error handling is quite different in the various SDKs, so instead of one common description, the description is in the different SDK tabs:

Android Text
If you use the synchronous methods you have always a try-catch block around the methods which do network requests. If an error occurs an exception of class ApiomatRequestException (which is a subclass of Exception) is thrown and catched. There are several methods to get information about details of this error. The exception object contains specific information like error code and a textual description of the reason.
 
- exception.getReason() — Gives you a textual description of the error
- exception.getExpectedReturnCode() — The expected HTTP return code
- exception.getReturnCode() — The returned statusCode
- exception.getStatus() — If is set then this method returns an instance of enum Status
 
On asynchronous requests isDone method that is executed after request finished contains also an exception object as parameter. If this object does not equal null, an error occurred and it would be nice to handle this. The given object is an instance of the previous explained class ApiomatRequestException. The following snippets gives you an idea how you can handle exception in async methods.
Android
Conference conference = new Conference ();
conference.saveAsync(new AOMEmptyCallback() {
@Override
public void isDone(boolean wasLoadedFromStorage, ApiomatRequestException exception) {
//if exception != null an error occurred
if(exception != null) {
Log.e("ConferenceSave", "An error occurred: " + e.getReason());
}
}
});
Backbone.js Text
The error handling works similar to Backbone.js default behaviour, as shown in the examples above. You can define an error callback function and access the parameters model, response and options.
Every request you send will enter the callback object you have given the function to act as a parameter. If any error occurs while sending the request, the error function will be called and the options will provide additional information you can access by evaluating the error property. This error is an object of type Apiomat.ApiomatRequestError. You can access the following functions and properties on this object:
 
- message: Gives you a textual description of the error
- statusCode: The returned status code
- getStatusObj: Give you an object of type Apiomat.Status if the statusCode can be mapped to one
- expectedCodes: Returns the HTTP status codes that this request expected
 
An example will demonstrate how to work with the error object:
Backbone.js
conference.loadConfSessions({
success: function(model, response, options) {
//success
},
error: function(model, response, options) {
//error handling
var error = options.error;
var errorCode = error.statusCode;
var description = error.message;
}
});
Backbone.js Text
The class Apiomat.Status contains references to every statusCode that can occur. Also, you can find a textual description of these codes there. You can compare the returned statusCode with a property of the type Apiomat.Status.
Backbone.js
var options = {
success: function(model, response, options) {
//successful
},
error: function(model, response, options) {
//check if error was an auth error
if(options.error.statusCode == Apiomat.Status.UNAUTHORIZED) {
//handle this error
}
}
}
C# Text
All the methods that do network I/O might throw an ApiomatRequestException. In C# there are no checked exceptions like in Java, so you don't have to use try/catch everywhere, but it's advised to do that so you can handle errors when they appear.
There are several Properties to get information about details of an error. The exception object contains specific information like error code and a textual description of the reason.
 
- exception.Reason - Gives you a textual description of the error
- exception.ExpectedReturnCode - The expected HTTP return code
- exception.ReturnCode - The returned status code
- exception.Status - Returns an instance of the enum "Status"
 
The returned status codes are not only HTTP status codes, but also some custom ones. For example the above mentioned error "ID already exists" when trying to sign up a user that has already been signed up before, is the error code 830 and Status.ID_EXISTS in the Status enum.
 
Here is an example for how to handle exceptions:
C#
Conference conference = new Conference();
try
{
await conference.SaveAsync();
}
catch (ApiomatRequestException e)
{
Console.WriteLine("An error occured during saving a conference: " + e.Reason);
// In Windows Store apps:
// System.Diagnostics.Debug.WriteLine("An error occured during saving a conference: " + e.Reason);
}
C# Text
In case you want to send another request after the error occurs, be advised that you can't use await in a catch block of an exception in C# 5 (this will probably change in C# 6 with the Roslyn .NET compiler).
If you just want to send the same or other requests asynchronously, initialize a flag with "false" before the try block, set it to "true" in the catch block and send another request if the flag evaluates to true:
C#
Conference conference = new Conference();
bool tryAgain = false;
try
{
await conference.SaveAsync();
}
catch (ApiomatRequestException e)
{
tryAgain = true;
// do what is necessary to fix the problem
// ...
}
if (tryAgain)
{
await CallMoreAsyncCode();
}
C# Text
However, you might also want to throw the exception so the caller knows about it. If you throw it in the last if-block the exception's stack trace doesn't contain the correct location where the exception happened.
To fix that, you can use the ExceptionDispatchInfo class like in the following example and also omit the flag:
C#
Conference conference = new Conference();
ExceptionDispatchInfo capturedException = null;
try
{
await conference.SaveAsync();
}
catch (ApiomatRequestException e)
{
capturedException = ExceptionDispatchInfo.Capture(e);
}
if (capturedException != null)
{
await CallMoreAsyncCode();
// Throw the exception with the captured info from above
capturedException.Throw();
}
Objective-C Text
If you use the synchronous methods you have always a try-catch block around the methods which do network requests. If an error occurs an exception of class NSException with name ApiomatRequestExceptionis thrown and catched. There are several methods to get information about details of this error. The exception object contains specific information like error code and a textual description of the reason.
 
- [exception name] or exception.name - Gives you the name of the error (should be ApiomatRequestException)
- [exception reason] or exception.reason- The returned statusCode
- [[exception userInfo] valueForKey:@"code"] or error?.userInfo["code"] - The returned statusCode (same as above)
- [[exception userInfo] valueForKey:@"message"] or error?.userInfo["message"] - Gives you a textual description of the error
 
On asynchronous requests every block that is executed after request finished contains also an NSError object as parameter. If this object not equals nil, an error occurred and it would be nice to handle this. The given error object contains specific information like error code and a textual description of the reason. The following snippets gives you an idea how you can handle exception in async methods.
Objective-C
Conference *conference = [[Conference alloc] init];
[conference saveAsyncWithBlock:^(NSError *error) {
/* If error occured the error parameter != NIL */
if(error) {
/* errorDomain == com.apiomat.frontend */
NSString *errorDomain = [error domain];
NSInteger errorCode = [error code];
NSString *reason = [[error userInfo] objectForKey:@"message"];
}
}];
Objective-C Text
The class AOMStatus contains references to every error code (statusCode) that can occur. Also, you can find a textual description of these codes there. You can compare the returned statusCode with a enum of the type AOMStatusCode. The following examples will demonstrate you the usage.
Objective-C
/* get textual description of error code */
NSInteger errorCode = 840;
NSString *errorMsg = [AOMStatus getReasonPhraseForCode:errorCode];
Swift Text
The Swift methods perform their work asynchronously and call the passed in completion handler when they are done. If an error occurs an object of Type NSError is passed to the completion handler, otherwise the argument is nil.
 
There are several methods to get information about details of this error. The error object contains specific information like error code and a textual description of the reason.
 
- error?.domain - The error domain (com.apiomat.frontend)
- error?.userInfo["code"] - The returned statusCode
- error?.userInfo["message"] - Gives you a textual description of the error
 
The following snippets gives you an idea how you can handle errors in async methods:
Swift
let conference = Conference()
conference.save { error in
if error == nil {
//successfully saved conference
}
else {
/* errorDomain == com.apiomat.frontend */
let errorDomain = error?.domain
let errorCode = error?.userInfo["code"]
let errorMessage = error?.userInfo["message"]
}
}
Swift Text
The class AOMStatus contains references to every error code (statusCode) that can occur. Also, you can find a textual description of these codes there. You can compare the returned statusCode with a enum of the type AOMStatusCode. The following examples will demonstrate you the usage.
Swift
 /* get textual description of error code */
let errorCode = 840
let errorReason = Status.reasonPhrase(forStatusCode: errorCode)
JavaScript Text
Every request you send will enter the callback object you have given the function to act as a parameter. If an error occurs while sending the request the 'onError' function of your callback object will be called with a parameter 'error'. This 'error' is an object of type Apiomat.ApiomatRequestError. You can call the following function on this object:
 
- message: Gives you a textual description of the error
- statusCode: The returned status code
- getStatusObj: Give you an object of type Apiomat.Status if the statusCode can be mapped to one
- expectedCodes: Returns the HTTP status codes that this request expected
 
An example will demonstrate the access to an 'error' object:
JavaScript
conference.loadConfSessions(undefined, {
onOk: function() {
//Successful
},
onError: function(error) {
//Error handling
var errorCode = error.statusCode;
var description = error.message;
}
});
JavaScript Text
The class 'Apiomat.Status' contains references to every statusCode that can occur. Also, you can find a textual description of these codes there. You can compare the returned statusCode with a property of the type 'Apiomat.Status'.
JavaScript
var callback = {
onOk: function() {
//Successful
},
onError: function(error) {
//Check if error was an auth error
if(error.statusCode == Apiomat.Status.UNAUTHORIZED) {
//Handling this error
}
}
}
TypeScript Text
Every request you send will return a Promise. This allows you to use .then() and .catch(e) with those methods, but the more modern way is to use await and use a try-catch block to handle errors. If an error occurs the Promise will be rejected and this rejection will be catched. The exception object contains specific information like error code and a textual description of the reason in the following properties:
 
- message: Gives you a textual description of the error
- statusCode: The returned status code
- expectedCodes: Returns the HTTP status codes that this request expected
 
An example will demonstrate the access to an 'error' object:
TypeScript
try {
await conference.loadConfSessions();
} catch (error) {
//Error handling
const errorCode = error.statusCode;
const description = error.message;
}
TypeScript Text
The class 'AOMStatus' contains references to every statusCode that can occur. Also, you can find a textual description of these codes there. You can compare the returned statusCode with a static property of the type 'AOMStatus'.
TypeScript
try {
await conference.loadConfSessions();
} catch (error) {
//Error handling
if(error.statusCode == AOMStatus.UNAUTHORIZED) {
//Handling this error
}
}

Custom Error Codes

ApiOmat supports to define and throw your own error codes within e.g. a Native Module. Those custom error codes will be delegated through each SDK to allow you to catch and handle them within your projects.

Error Code Limitation

Unfortunatly SDKs like Android, iOS or PHP do not support error codes greater than 999.

Their internal basic libraries often block HTTP responses with unsupported error codes or throw ProtocolExceptions. To avoid such complications it is suggested to follow the definition of response status codes within the HTTP/1.1 RFC.

Cache and Offline Handling

When using the Objective-C SDK, please note:

The class AOMModelStore inherits from the class NSURLCache. So if you have any problems with clear cache functions in the simulator especially for ios 9, you should use the storagePolicy: NSURLCacheStorageAllowedInMemoryOnly.

For info about how to use caching and offline data, please look here: Offline Handling in SDKs

Meta data of data model

To access information about the structure of your app, you can use the appConfig, that provides the names of all available modules including it's models and attributes in JSON format.

For example:

appConfig.json
{
"app": {
"name": "Conference",
"modules": [
{
"module": {
"name": "Basics",
"type": "STATIC",
"usedInSystem": "TEST",
"classes": [
{
"class": {
"name": "User",
"attributes": [
{
"attribute": {
"name": "userName",
"type": "String"
}
},
...snip other attributes
}
},
... snip other classes
}
},
... snip other modules
}
}

Backbone.js
var appConfig = apiomat.appConfig();
 
//now you can access the name of the app for example
var appName = appConfig.app.name;
JavaScript
var appConfig = JSON.parse(Apiomat.User.APPCONFIG);
//now you can access the name of the app for example
var appName = appConfig.app.name;
JavaScript
const appConfig = User.APPCONFIG;
//now you can access the name of the app for example
const appName = appConfig.app.name;

Adding and modifying headers

It is possible to add new or modify existing headers sent with each SDK request to ApiOmat using a special method in the Datastore class. Like the Datastore.configure..() method, setting additional request headers this way will persist until another call to the method is executed:

Android
Map<String, String> myHeaders = new HashMap<>();
myHeaders.put("cookie", "123456");
 
/* adding multiple header */
Datastore.setCustomHeaders(myHeaders);
 
/* removing all headers */
Datastore.setCustomHeaders(null);
Backbone
var myHeaders = new Array();
myHeaders.push({"foo", "bar"});
 
/* adding multiple header */
apiomat.customHeaders(myHeaders);
 
/* removing all headers */
apiomat.setCustomHeaders([]);
 
/* operation related, using the options object */
var conference = apiomat.Conference();
conference.save({customHeaders: myHeaders});
C#
/* Add custom headers */
Datastore.Instance.CustomHeaders = new Dictionary<string, string> () { {"foo", "bar"} };
 
/* Remove custom headers */
Datastore.Instance.CustomHeaders = null;
Objective-C
/* setting a custom header */
[[AOMDatastore sharedInstance].customHeaders setObject:@"Header_Value" forKey:@"Header_Name"];
 
/* removing a custom header */
[[AOMDatastore sharedInstance].customHeaders removeObjectForKey:@"Header_Name"];
 
/* adding multiple headers */
NSDictionary* headers = @{@"Header_Name1" : @"Header_Value1",
@"Header_Name2" : @"Header_Value2" };
[[AOMDatastore sharedInstance].customHeaders addEntriesFromDictionary:headers];
 
/* removing all headers */
[[AOMDatastore sharedInstance].customHeaders removeAllObjects];
Swift
/* setting a custom header */
DataStore.sharedInstance.setCustomHeaders(["cookie", "123456"])
 
/* setting multiple headers */
DataStore.sharedInstance.setCustomHeaders(["cookie", "123456", "key", "value"])
 
/* removing all headers */
DataStore.sharedInstance.setCustomHeaders(nil);
TypeScript
/* setting a custom header */
Datastore.Instance.customHeaders = {"foo": "bar"}
 
/* setting multiple headers */
Datastore.Instance.customHeaders = {
"foo": "bar",
"key": "value"
};
 
/* removing all headers */
Datastore.Instance.customHeaders = undefined;
* link only available in Enterprise Documentation