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: ConfSession
-
name (String)
-
fromTime (Date)
-
toTime (Date)
-
i18n_name (Map)
-
conference(Conference)
-
handout(File)
-
Content
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.
User user =
new
User();
user.setUserName(
"user12"
);
user.setPassword(
"123asdf"
);
Datastore.configureWithCredentials(user);
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);
User user =
new
User() { UserName =
"user12"
, Password =
"123asdf"
};
Datastore.ConfigureWithCredentials(user);
AOMUser* user = [[AOMUser alloc] init];
[user setUserName:@
"user12"
];
[user setPassword:@
"1a2s3d4f"
];
[AOMDatastore configureWithUser:user];
let user = User()
user.userName =
"yourName"
user.password =
"yourPassword"
DataStore.configureWithCredentials(user: user)
var
user =
new
Apiomat.User();
user.setUserName(
"user12"
);
user.setPassword(
"123asdf"
);
Apiomat.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.
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.
try
{
user.save();
}
catch
(ApiomatRequestException e) {
// Handle exception
}
user.SaveAsync().Wait();
@try
{
[user save];
}
@catch
(NSException *exception) {
/* An error occured */
}
Here are the asynchronous method calls for the same task:
user.saveAsync(
new
AOMEmptyCallback(){
@Override
public
void
isDone(ApiomatRequestException exception){}
});
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
}
});
await user.SaveAsync();
[user saveAsyncWithBlock:^(NSError *error) {
if
(error) {
/* request was not successful */
}
else
{
/* sign up was successful */
}
}];
user.save(loadAfterwards:
true
, completion: { error in
if
error != nil {
//handle error
}
})
user.save({
onOk:
function
() {
//Successfully sign up
},
onError:
function
(error) {
//do error handling
}
});
If the user already exists you can log in the user instead:
try
{
user.loadMe();
}
catch
(ApiomatRequestException e) {
// Handle exception
}
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
}
});
try
{
await user.LoadMeAsync();
}
catch
(ApiomatRequestException ex)
{
// Handle exception
}
user.loadMe { error in
if
error != nil {
//handle error
}
}
user.loadMe({
onOk:
function
() {
//Successfully logged in.
},
onError:
function
(error) {
//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:
final
User user =
new
User();
user.setUserName(
"_username_"
);
user.setPassword(
"_password_"
);
Datastore.configureWithCredentials(user);
user.loadMeAsync(
new
AOMEmptyCallback() {
@Override
public
void
isDone(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(ApiomatRequestException exception) {
}
});
}
}
});
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
}
});
}
});
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;
}
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) {
}];
}
}];
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
}
}
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
}
});
}
});
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:
{
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
*/
}
}
{
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:
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(ApiomatRequestException exception){}
});
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
}
});
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();
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 */
}
}];
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
}
}
var
conference =
new
Apiomat.Conference();
//you may set some attributes here
conference.save({
onOk:
function
() {
//Successfully saved
},
onError:
function
(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.
Conference conference =
new
Conference();
conference.loadAsync(href,
new
AOMEmptyCallback() {
@Override
public
void
isDone(ApiomatRequestException exception) {
//Now you're holding your object.
}
});
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
}
});
Conference conference =
new
Conference();
await conference.LoadAsync(href);
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. */
}];
let conf = Conference()
conf.load(withHref: href) { error in
if
error == nil {
//successfully reloaded your conference
}
else
{
//something went wrong, handle error
}
}
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
}
});
If you need to reload the data from server, then type in the following:
loadedConf.loadASync(
new
AOMEmptyCallback(){
@Override
public
void
isDone(ApiomatRequestException exception) {
//you reloaded your object
}
});
loadedConference.fetch({
success:
function
(model, response, options) {
//successfully saved
},
error:
function
(model, response, options) {
//do error handling
}
});
await loadedConf.LoadAsync();
[loadedConf loadAsyncWithBlock:^(NSError *error) {
/* you reloaded your object */
}];
conf.load { error in
if
error == nil {
//successfully reloaded your conference
}
else
{
//something went wrong, handle error
}
}
loadedObj.load({
onOk:
function
() {
//Successfully saved
},
onError:
function
(error) {
//do error handling
}
});
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.
conference.getConferencesAsync(
null
,
new
AOMCallback<List<Conference>>() {
@Override
public
void
isDone(List<Conference> resultObject, ApiomatRequestException exception) {
}
});
//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
}
});
IList<Conference> loadedConferences = await Conference.GetConferencesAsync();
/* Load list of conference objects */
Conference.loadList { conferences, error in
if
error == nil {
let loadedConferences = conferences
}
else
{
//something went wrong, handle errror
}
}
Apiomat.Conference.getConferences(undefined,{
onOk : function(conferences) {
// loading Conferences was successful
},
onError : function(error) {
// do error handling here
}
});
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.
//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
}
});
IList<Conference> loadedConferencesFiltered = await Conference.GetConferencesAsync(
"name==\"Microsoft Build 2014\""
);
/* 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 */
}];
/* 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
}
}
Apiomat.Conference.getConferences(
"name==\"Microsoft Build 2014\""
,{
onOk : function(conferences) {
// loading Conferences was successful
},
onError : function(error) {
// do error handling here
}
});
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:
//Load count of conference objects filtered
conference.getConferencesCountAsync(
"name like \"JavaOne\""
,
new
AOMCallback<Long>() {
@Override
public
void
isDone(Long resultObject, ApiomatRequestException exception) {
}
});
//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
}
});
long
conferenceCount = await GetConferencesCountAsync();
// Counts all conferences
long
conferenceCountFiltered = await GetConferencesCountAsync(
"name like \"JavaOne\""
);
// Counts filtered conferences
long
cnt = [Conference getCountWithQuery:@
"name like \"JavaOne\""
];
[Conference getCountAsyncWithQuery:@
"name like \"JavaOne\""
withBlock:^(
long
count, NSError *error) {
cnt = count;
err = error;
}];
// 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
}
}
//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
}
});
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:
// Get or save updatedObj
// Change sth on updatedObj
conference.saveAsync(
new
AOMEmptyCallback(){
@Override
public
void
isDone(ApiomatRequestException exception) {
//Successfully updated
}
});
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
}
});
// Get or save a conference
// ...
// Change something on the conference
// ...
// Update the conference
await conference.SaveAsync();
/* Get or save updatedObj */
/* Change sth on updatedObj */
/* udpate it on server */
[updatedObj saveAsyncWithBlock:^(NSError *error) {
}];
/* 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
}
}
// Get or save updatedObj
// Change sth on updatedObj
updatedObj.save({
onOk:
function
() {
//Successfully updated
},
onError:
function
(error) {
//do error handling
}
});
Delete an object
According to our pattern, you can delete an object simply by entering the following:
// Load object you want to delete
conference.deleteAsync(
new
AOMEmptyCallback() {
@Override
public
void
isDone(ApiomatRequestException exception) {
}
});
conference.destroy({
success :
function
(model, response, options) {
//object successfully deleted
},
error:
function
(model, response, options) {
//handle error
}
});
await conference.DeleteAsync();
//Load object you want to delete
[conf deleteAsyncWithBlock:^(NSError *error) {
/* check error if sth went wrong */
}];
//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
}
}
obj.deleteModel({
onOk:
function
() {
//Successfully deleted
},
onError:
function
(error) {
//do error handling
}
});
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:
Conference conference =
new
Conference();
conference.setName(
"Android Dev Con"
);
//save the object
var
conference = apiomat.Conference();
conference.set(
"name"
,
"JavaScript Dev Con"
);
// 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
Conference *conf = [[Conference alloc] init];
[conf setName:@
"The iOS dev conference"
];
Conference conf = Conference()
conf.name =
"The iOS dev conference"
var
conference =
new
Apiomat.Conference();
conference.setName(
"JavaScript Dev Con"
);
or set an attribute “someMap” of ApiOmat type map, which looks like this:
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);
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
}
});
// 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;
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];
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]
var
i18ns = {
"de"
:
"Wie benutze ich Properties"
,
"en"
:
"How to use properties"
}
confSession.setI18n_name(i18ns);
//save object on server
confSession.save();
Getting an attribute
Like accessing “normal” attributes, you can also access properties of ApiOmat classes.
//Make sure you loaded object conference before
String confName = conference.getName();
//Make sure you loaded object conference before (see above)
var
confName = conference.get(
"name"
);
// Make sure you loaded the conference object before
string
confName = conference.Name;
/* Make sure you loaded object conference before (see above) */
NSString *confName = [conference name];
/* Make sure you have loaded object conference before (see above) */
let confName = conf.name
//Make sure you loaded object conference before (see above)
var
confName = conference.getName();
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:
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();
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();
conference.PlaceLatitude = 10.2;
conference.PlaceLongitude = 20.3;
// Access the properties in the same way
double
latitude = conference.PlaceLatitude;
double
longitude = conference.PlaceLongitude;
[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];
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
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();
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:
// As byte array:
byte
[] filearr = {
0
,
3
,
6
,
2
};
ConfSession confsession =
new
ConfSession();
confsession.postHandoutAsync(filearr,
new
AOMEmptyCallback() {
@Override
public
void
isDone(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(ApiomatRequestException exception) {
}
});
// As file
File file =
new
File(path);
ConfSession confsession =
new
ConfSession();
confsession.postHandoutAsync(file ,
new
AOMEmptyCallback() {
@Override
public
void
isDone(ApiomatRequestException exception) {
}
});
// 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
}
}
// 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);
// 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
}
}]
// 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
}
}
// _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
}
});
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.
string slidesUrlPlain = conference.getSlidesResourceURL();
// Without the addition of API key and system
string slidesUrl = conference.getSlidesUrl();
// With the addition of API key and system
//Get plain URL without concerting parameters
var
slidesUrl = conference.get(
"slidesURL"
);
string
slidesUrlPlain = conference.SlidesURLResource;
// Without the addition of API key and system
string
slidesUrl = conference.SlidesUrl;
// With the addition of API key and system
NSString *handoutURL = [confSession handoutURL];
let handoutURL = confSession.handoutURL
var
slidesURL = session.getSlidesURL();
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().
// Via the object that contains the file:
byte
[] result = session.LoadSlidesAsync(
new
AOMEmptyCallback() {
// usePersistentStorage is optional
@Override
public
void
isDone(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(ApiomatRequestException exception) {
}
});
/* you can either load it via Datastore */
apiomat.loadResource(slidesURL, {
asByteArray:
true
,
success:
function
(data, xhr, options) {
// file content successfully loaded
Apiomat.log.info(
"file 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 loaded
Apiomat.log.info(
"file loaded successfully"
);
},
error:
function
(model, response, options) {
// handle error
}
});
// you can either load it via Datastore
String fileUrl = session.SlidesUrl;
byte
[] result = await Datastore.Instance.LoadResourceAsync(fileUrl,
false
,
false
, usePersistentStorage);
// alternatively you can use the following method as well
byte
[] result2 = await session.LoadSlidesAsync();
// usePersistentStorage is optional
/* you can either load it via Datastore */
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 */
}];
/* you can either load it via Datastore */
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 */
}
}
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 );
/* alternatively you can use the following method as well */
session.loadSlides( loadCB );
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.
byte
[] imgArr =
new
byte
[] {
0
,
2
,
6
,
8
};
conference.postImageAsync(imgArr,
new
AOMEmptyCallback() {
@Override
public
void
isDone(ApiomatRequestException exception) {
}
});
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
}
});
byte
[] fakeImage =
new
byte
[] {0x00, 0x30, 0x60, 0x20};
ConfSession confSession =
new
ConfSession();
await confSession.PostImageAsync(fakeImage);
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 */
}];
let data = NSMutableData()
let conf = Conference()
conf.postImage(data) { error in
if
error == nil {
//data successfully posted
}
else
{
//something went wrong, handle error
}
}
var
imgArr = [0,2,6,2];
conference.postImage(imageArr,{
onOk:
function
() {
//Successfully uploaded image
},
onError:
function
(error) {
//do error handling
}
});
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:
//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"
);
//Get plain URL without concerting parameters
var
imgUrl = conference.get(
"imageURL"
);
// 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"
);
/* 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"
];
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
}
}
//Get plain URL without concerting parameters
var
imgUrl = conference.getImageURL();
//Get URL with resized image to 200x100
var
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"
-
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:
//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(ApiomatRequestException exception) {
}
});
//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
}
});
// 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);
/* 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) {
}];
/* 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
}
};
//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
}
})
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:
//Assume that our object conference has already referenced confSessions
conference.loadConfSessionAsync(
null
,
new
AOMEmptyCallback() {
@Override
public
void
isDone(ApiomatRequestException exception) {
//if there is no exception you can access the local list on conference object
List<confsession> sessions = conference.getConfSessions();
}
});
//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
}
});
// 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;
/* 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];
}];
/* 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
}
}
//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
}
});
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.
//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(ApiomatRequestException exception) {
}
});
//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
}
});
// Assume that conference contains a reference to confSession
// Assume that both objects (conference, confSession) are loaded from server
await conference.RemoveConfSessionAsync(confSession);
/* Assume that conference contains a reference to confSession */
/* Assume that both objects (conference, confSession) are loaded from server */
[conference removeConfSessionsAsync:confSession andWithBlock:^(NSError *error) {
}];
/* 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
}
}
//Assume that conference contains a reference to confSession
//Assume that both objects (conference, confSession) are loaded from server
conference.removeConfSessions(confSession);
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.
//Load list of conference objects filtered by name
Conference.getConferencesAsync(
"name like \"Backend\""
,
new
AOMCallback<List<Conference>>() {
@Override
public
void
isDone(List<Conference> resultObject, ApiomatRequestException exception) {
//Getting list was successful if exception == null
//You can now work with parameter objects which contains loaded elements
}
});
//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
}
});
// Load list of conference objects filtered by name
IList<Conference> loadedConferences = await Conference.GetConferencesAsync(
"name like \"Backend\""
);
/* 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 */
}];
/* 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
}
}
//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
}
});
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:
//Assume that our object conference has already referenced confSessions
conference.loadConfSessionsAsync(confSession,
new
AOMEmptyCallback() {
@Override
public
void
isDone(ApiomatRequestException exception) {
conference.getConfSessions();
}
});
//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
}
});
// Assume that our object conference already contains referenced confSessions
IList<ConfSession> sessions = await conference.LoadConfSessionAsync(
"topic=\"C#\""
);
/* Assume that our object conference has already referenced confSessions */
[conference loadConfSessionsAsync:@
"name like \"Backend\""
andWithBlock:^(NSError *error) {
NSMutableArray *filteredSessions = [conference sessions];
}];
/* 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
}
//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
}
});
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:
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.
Conference conference =
new
Conference ();
conference.saveAsync(
new
AOMEmptyCallback() {
@Override
public
void
isDone(ApiomatRequestException exception) {
//if exception != null an error occurred
if
(exception !=
null
) {
Log.e(
"ConferenceSave"
,
"An error occurred: "
+ e.getReason());
}
}
});
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:
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;
}
});
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.
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
}
}
}
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:
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);
}
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
:
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();
}
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:
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();
}
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.
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"
];
}
}];
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.
/* get textual description of error code */
NSInteger errorCode =
840
;
NSString *errorMsg = [AOMStatus getReasonPhraseForCode:errorCode];
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:
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"
]
}
}
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.
/* get textual description of error code */
let errorCode =
840
let errorReason = Status.reasonPhrase(forStatusCode: errorCode)
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:
conference.loadConfSessions(undefined, {
onOk:
function
() {
//Successful
},
onError:
function
(error) {
//Error handling
var
errorCode = error.statusCode;
var
description = error.message;
}
});
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'
.
var
callback = {
onOk:
function
() {
//Successful
},
onError:
function
(error) {
//Check if error was an auth error
if
(error.statusCode == Apiomat.Status.UNAUTHORIZED) {
//Handling this error
}
}
}
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:
{
"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
}
}
var
appConfig = apiomat.appConfig();
//now you can access the name of the app for example
var
appName = appConfig.app.name;
var
appConfig = JSON.parse(Apiomat.User.APPCONFIG);
//now you can access the name of the app for example
var
appName = appConfig.app.name;
string
slidesUrlPlain = conference.SlidesURLResource;
// Without the addition of API key and system
string
slidesUrl = conference.SlidesUrl;
// With the addition of API key and system
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:
Map<String, String> myHeaders =
new
HashMap<>();
myHeaders.put(
"cookie"
,
"123456"
);
/* adding multiple header */
Datastore.setCustomHeaders(myHeaders);
/* removing all headers */
Datastore.setCustomHeaders(
null
);
/* Add custom headers */
Datastore.Instance.CustomHeaders =
new
Dictionary<
string
,
string
> () { {
"foo"
,
"bar"
} };
/* Remove custom headers */
Datastore.Instance.CustomHeaders =
null
;
/* 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];
/* 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);