Apigo Platform Documentation for iOS App | Apigo.id

Apigo Starter Project for iOS Documentation

Last updated: Aug 4th, 2018

Getting Started

A framework that gives you access to the powerful Apigo cloud platform from your iOS app. For more information about Apigo and its features, see Apigo Website and Apigo Documentations.

Download
  1. Download the latest Apigo framework here.
  2. Then drag Apigo.framework into the Frameworks sections in your XCode project.
  3. Ensure the following frameworks and a library exist in your project :
    • SystemConfiguration.framework
    • Security.framework
    • QuartzCore.framework
    • CoreLocation.framework
    • CoreGraphics.framework
    • CFNetwork.framework
    • AudioToolbox.framework
    • libsqlite3.0.tbd
Setup
  1. Register first to Apigo Cloud
  2. Create an application to get applicationId and clientKey
  3. Add this line below to your Application class to initialize Apigo SDK

Apigo.initialize(withServer: "APIGO_SERVER_URL",
                applicationId: "APIGO_APPLICATION_ID",
                clientKey: "APIGO_CLIENT_KEY")

(Optional) You can add some custom setup :

  • Enable Apigo SDK debug logging by calling Apigo.+setLogLevel: before initialize SDK.
  • Apigo APLogLevel mode : none, error, warning, info, debug

Everything is done!

Object

Managing Objects

Storing data on Apigo is built around the MFObject. Each MFObject contains key-value pairs of JSON-compatible data. This data is using schema, which means that you need to specify ahead of time what keys exist on each MFObject from our Apigo Cloud. Then you can simply set a key-value pairs you want to save, and our backend will store it.

For example, let's say you set a Beacon parameters. A single MFObject could contain :


{
    "isActive":true, 
    "major":1, 
    "name":"Beacon One", 
    "minor":284, 
    "proximityUUID":"CB10023F-A318-3394-4199-A8730C7C1AEC"
}

Keys must be alphanumeric strings. Values can be NSString, NSNumber, NSDate, NSNumber, or even NSArray and NSDictionary - anything that can be JSON-encoded.

Saving Objects

Let’s say you want to save the Beacon described above to the Apigo Cloud. The interface is similar to a NSDictionary, plus the saveInBackground method:


let data = MFObject(className: "Beacon")
// set data
data["name"] = "Beacon One"
data["proximityUUID"] = "CB10023F-A318-3394-4199-A8730C7C1AEC"
data["major"] = 1
data["minor"] = 284
data["isActive"] = true
data["timestamp"] = Date()
// execute save data
data.saveInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // data saved, show success message
}

Retrieve an Object

If you need to fetch a data with the latest data that is in the cloud, you can call the fetchInBackground method like so:


// create data from existing objecId
let data = MFObject(withoutDataWithClassName: "Beacon", objectId: "objectId")
// fetching the data
data.fetchInBackground { (object, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // fetch data succeeded
}

To get the values out of the MFObject, there’s a getX method for each data type:


Bool isActive = data.object(forKey: "isActive") as Bool
Int major = data.object(forKey: "major") as Int
String name = data.object(forKey: "name") as String
Int minor = data.object(forKey: "minor") as Int
String proximityUUID = data.object(forKey: "proximityUUID") as String

If you don’t know what type of data you’re getting out, you can call object(forKey:), but then you probably have to cast it right away anyways.

The three special values have their own accessors:


String objectId = data.objectId
Date updatedAt = data.updatedAt
Date createdAt = data.createdAt

Updating Objects

After getting the data, you can update your data that stored in cloud using method saveInBackground.


var data: MFObject // fetched data
// set data
data["name"] = "Beacon Two"
data["proximityUUID"] = "CB10023F-A318-3394-4199-A8730C7C1AEC"
data["major"] = 2
data["minor"] = 284
data["isActive"] = false
data["timestamp"] = Date()
// execute save data
data.saveInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // data updated, show success message
}

Deleting Object

To delete a data from the Apigo Cloud, use method deleteInBackground:


var data: MFObject
data.deleteInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // data deleted successfully
}

Data Types

So far we’ve used values with type NSStringand NSNumber. Apigo also supports float, NSDate, and NSNull.

You can nest NSDictionary and NSArray objects to store more structured data within a single MFObject. Overall, the following types are allowed for each field in your object:

  • String => String
  • Number => primitive numeric values such as int, double, long, or float
  • Bool => boolean
  • Array => JSONArray
  • Object => JSONObject
  • Date => java.util.Date
  • Null => JSONObject.NULL

Some examples:


Int myNumber = 42
String myString = "the number is \(myNumber)"
let myDate = Date()

let myArray = [AnyObject]()
myArray.append(myString)
myArray.append(myNumber)

let myObject = [String:AnyObject]();
myObject["number"] = myNumber
myObject["string"] = myString

let bigObject = MFObject(className: "BigObject")
bigObject["myNumber"] = myNumber
bigObject["myString"] = myString
bigObject["myDate"] = myDate
bigObject["myArray"] = myArray
bigObject["myObject"] = myObject
bigObject["myNull"] = NSNull.null
bigObject.saveInBackground()

Query

Managing Queries

There are many other ways to retrieve data with Apigo Query. You can retrieve many data at once, put conditions on the data you wish to retrieve.

Basic Query

In many cases, there is a condition that need to specify which datas you want to retrieve. The MFQuery offers different ways to retrieve a list of datas. The general pattern is to create a MFQuery, put conditions on it, and then retrieve a List of matching MFObjects using the findObjectsInBackground method with a MFQueryArrayResultBlock.

For example, to retrieve Beacons data with a name, use the whereKey:equalTo: method to constrain the value for a key:


let query = MFQuery(className: "Beacon")
query.whereKey("name", equalTo: "Beacon One")
query.findObjectsInBackground { (beacons, error) in
    if let e = error as NSError? {
        // exception happen, handle the message
        return
    }
    
    // found the beacons, show in table
}

Query Constraint

There are several ways to put constraints on the datas found by a MFQuery. You can filter out datas with a particular key-value pair with whereKey:notEqualTo::


query.whereKey("name", notEqualTo: "Beacon One")

You can give multiple constraints, and datas will only be in the results if they match all of the constraints. In other words, it’s like an AND of constraints.


query.whereKey("name", notEqualTo: "Beacon One");
query.whereKey("major", greaterThan: 1)

You can limit the number of results with limit. By default, results are limited to 100, but anything from 1 to 1000 is a valid limit:


query.limit = 20 // limit to at most 20 results

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


query.skip = 10 // skip the first 10 results

For sortable types like numbers and strings, you can control the order in which results are returned:


// Sorts the results in ascending order by the beacon's major
query.orderByAscending("major");

// Sorts the results in descending order by the beacon's minor
query.order(byAscending: "minor")

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


// Sorts the results in ascending order by the beacon's major field if the previous sort keys are equal.
query.addAscendingOrder("major")

// Sorts the results in descending order by the beacon's minor field if the previous sort keys are equal.
query.addDescendingOrder("minor")

For sortable types, you can also use comparisons in queries:


// Restricts to major < 123
query.whereKey("major", lessThan: 123)

// Restricts to major <= 123
query.whereKey("major", lessThanOrEqualTo: 123)

// Restricts to major > 123
query.whereKey("major", greaterThan: 123)

// Restricts to major >= 123
query.whereKey("major", greaterThanOrEqualTo: 123)

Query on String

Use whereKey:hasPrefix: to restrict to string values that start with a particular string. Similar to a MySQL LIKE operator, this is indexed so it is efficient for large datasets:


// Finds beacon's name that start with 'Beacon'.
let query = MFQuery(className: "Beacon")
query.whereKey("name", hasPrefix: "Beacon") 

The above example will match any MFObject where the value in the name String key starts with “Beacon”. For example, both “Beacon One” and “Beacon Two” will match, but “First Beacon” or “Second Beacon” will not.
Use whereEndsWith to restrict to string values that end with a particular string.


// Finds beacon's name that end with 'One'.
let query = MFQuery(className: "Beacon")
query.whereKey("name", hasSuffix: "One")

The above example will match any MFObject where the value in the name String key ends with “One”. For example, “Beacon One” will match, but “One Beacon” will not.

Counting Objects

If you just need to count how many datas match a query, but you do not need to retrieve all the datas that match, you can use count instead of find. For example, to count how many beacons have major greater than 123:


let query = MFQuery(className: "Beacon")
query.whereKey("major", greaterThan: 123)
query.countObjectsInBackground { (count, error) in
    if let e = error as NSError? {
        // exception happen, handle the message
        return
    }
    
    // counting succeeded, show beacons count
}

User

Managing User

Apigo provide a specialized bucket User that automatically handles much of the functionality required for user management. With this bucket, you’ll be able to manage user account functionality in your app.

Signing Up

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


// create new instance of User
let user = MFUser()

// set default field
user.username = "user.one"
user.password = "user1234"
user.email = "user.one@apigo.com"

// set custom field
user["dateOfBirth"] = Date()
user["height"] = 177.5
user["weight"] = 78
user["isMarried"] = true
user["myObject"] = [String:AnyObject]()
user["myArray"] = [[String:AnyObject]]()

// execute register user asynchronous
user.signUpInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // register succeeded
}

This call will asynchronously create a new user in your Apigo App. Before it starts, the call will checks to make sure that the email are unique. Also, it also securely hashes the password in the cloud using bcrypt. We never store passwords in plaintext, nor transmit passwords back to the client in plaintext.

If a register isn’t successful, you should read the NSError that is returned. The most likely case is that the email has already been taken by another user. You should communicate this to your user clearly, and ask them to try a different email.

Logging In

After you allow users to sign up, you need be able to let them log in to their account in the future. To do this you can use several method for handling action in login process.

After you allow users to register, you need be able to let them login to their account in the future. To do this, you can use the class method logInWithUsername.


MFUser.logInWithUsername(inBackground: "myUsername", password: "myPlainPassword") { (user, error) in
    // check if there is an exception happen
    if let e = error as NSError?
        // handle the exception
        return
    }
    
    // log in succeeded
}

Session

It would be bothersome if the user had to login every time they open your app. You can avoid this by using the cached current object.

Whenever you use any signup or login methods, the user is cached on disk. You can treat this cache as a session, and automatically assume the user is logged in:


if let user = MFUser.current() {
    // user logged in, open main page
} else {
    // session not found, open login page
}

Log Out

You can clear the current user by logging them out:


MFUser.logOutInBackground { (error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // log out succeeded
}

Querying Data

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


let query = MFUser.query()
query?.whereKey("gender", equalTo: "female")
query?.findObjectsInBackground { (users, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // query succeeded, users found
}

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

Messaging Sender

Sender is number that plot by admin for sending message, you can't subscribe number that not in backend list. The number that allowed to subscribe are : FREENUM0, FREENUM1.

Properties
  • number -> String, used to store number object id.
  • price -> Number, used to store price of this number (readonly).
  • active -> Boolean, used to store status active this record (readonly).
Creating Sender

let sender = APMessageSender()
sender.number = "FREENUM1"
sender.saveInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // sender saved, show success message
}

Updating Sender

// saved sender
var sender: APMessageSender
// changing new number
sender.number = "FREENUM0"
sender.saveInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // sender updated, show success message
}

Deleting Sender

// saved sender
var sender: APMessageSender
sender.deleteInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // sender deleted, show success message
}

Querying Sender

let query = APMessageSender.query()
query.findObjectsInBackground { (senders, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // senders found, show in a table
}

Messaging Recipient

Messaging Recipient

Recipient is number that plot by user for receiving message. This module is used to send broadcast, with template, and broadcast with template.

Properties
  • number -> String, used to store number of recipient validate min.9 max.15 start with 62.
  • group -> String, used to store group name.
  • params -> Object, used to store params value of template validate json object string.
Creating Recipient

let recipient = APMessageRecipient()
recipient.number = "6281234567890"
recipient.group = "IT"
recipient.params = [
    "kode": "901"
]

recipient.saveInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // recipient saved, show success message
}

Updating Recipient

// saved sender
var recipient: APMessageRecipient
// changing data
recipient.number = "6281234234234"
recipient.group = "Staff"
recipient.params = [
    "kode": "902"
]

recipient.saveInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // recipient updated, show success message
}

Deleting Recipient

// saved recipient
var recipient: APMessageRecipient;
recipient.deleteInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // recipient deleted, show success message
}

Querying Recipient

Request:


let query = APMessageRecipient.query()
query.findObjectsInBackground { (recipients, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // recipients found, show in a table
}

Messaging Templates

Template is string content message that can possible to custom value by variable with prefix #. eg. Hi #name, please use this code #code to redeem point.

Properties
  • title -> String, used to store of title template.
  • content -> String, used to store template content.
  • approved -> Boolean, approved status of this template (readonly).
Creating Template

let template = APMessageTemplate()
template.content = "Dapatkan diskon belanja s.d 50% dengan menukarkan kode #kode ini."
template.title = "Promo Akhir Tahun"
template.saveInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // template saved, show success message
}

Updating Templates

// saved template
var template: APMessageTemplate
template.content = "Dapatkan diskon belanja s.d 70% dengan menukarkan kode kupon berikut #kode."
template.title = "Promo Awal Tahun"
template.saveInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // template updated, show success message
}

Deleting Templates

var template: APMessageTemplate
template.deleteInBackground { (succeed, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // template deleted, show success message
}

Querying Templates

let query = APMessageTemplate.query()
query.findObjectsInBackground { (templates, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // templates found, show in a table
}

Messaging Log

Messaging Logs

History data of sending message (SMS, MMS, and USSD)is collected in this module. User can get all of this data or spesific by Message Type.

Properties

All fields are readonly.

  • transactionId -> String, unique id that receive from third party of telco.
  • sender -> String, sender of message.
  • recipient -> String, recipient of message.
  • content -> String, content of message.
  • msgType -> String, type of message : sms, mms, or ussd.
  • sendTime -> Date, time of sending message.
  • receiveTime -> Date, time of receiving message.
  • sandBox -> Bool, type of call this api (sandBox:true --> only save data).
  • status -> String, status of sending message : sending and sent.

Querying Logs

let query = APMessageLog.query()
query.findObjectsInBackground { (logs, error) in
    // check if there is an exception happen
    if let e = error as NSError? {
        // handle the exception
        return
    }
    
    // show logs in a list
}

Send Message

Send Message

This module is handling function for sending sms, mms, or ussd with 4 types sending method :

  • Template -> This method used to sending message with template refer to templateId in _MessageTemplate.
  • BroadCast with Template -> this method used to sending message refer to _MessageRecipient and _MessageTemplate.

Send Template

Properties

  • sender -> String, sender number, refer to data in APMessageSender.
  • recipient -> String, recipient number.
  • templateId -> String, objectId, refer to data in APMessageTemplate.
  • params -> String, json object string that replace template variable eg. {"kode":"918"}.
  • sandBox -> Boolean, default false, set true if you want to store data only.

Sending Message

let sender: APMessageSender
let recipient = "628123456789"
let template: APMessageTemplate
let params = [
    "kode": "abc123"
]

let send = APMessage(directTemplateWith: sender,
                    recipient: recipient,
                    template: template,
                    parameters: params,
                    messageType: .sms,
                    sandbox: false)
send?.sendInBackground({ (log, error) in
    if let e = error as NSError? {
        // handle exception that happen
        return
    }
    
    // message sent, show succeed message
})

Send Template BroadCast

Properties

  • sender -> String, sender number, refer to data in MessageSender.
  • group -> String, group name, refer to data in MessageRecipient.
  • templateId -> String, objectId, refer to data in MessageTemplate.
  • sandBox -> Boolean, default false, set true if you want to store data only.

Sending Message

let sender: APMessageSender
let template: APMessageTemplate
let params = [
    "kode": "abc123"
]
let send = APMessageBroadcast(templateWith: sender,
                            template: template,
                            group: "IT",
                            messageType: .sms,
                            sandbox: false)
send?.sendInBackground({ (log, error) in
    if let e = error as NSError? {
        // handle exception that happen
        return
    }
    
    // message sent, show succeed message
})

MessagingError

Error Codes

The following is a list of all the error codes that can be returned by the Apigo API - Messaging.

Name Error Code Description
IllegalSenderNumber 1001 illegal sender number.
MissingSenderNumber 1002 missing sender number.
DontHaveActiveSenderList 1003 don't have any active sender list.
SenderAlreadyExist 1004 sender number already exist.
MissingRecipientNumber 1005 missing recipient number.
IllegalRecipientNumber 1006 illegal recipient number.
InvalidParamsFormat 1007 invalid params format.
ObjectParamsMustBeString 1008 object value params must be string.
MissingContentMessage 1009 missing message content.
OverLimitContent 1010 max length of body is 160.
MissingTemplateId 1011 missing templateId.
InvalidTemplateId 1012 invalid template id.
InactiveTemplate 1013 inactive template.
SensitiveWord 1014 message contain sensitive word.
IllegalNotifyUrl 1015 illegal notify url.
DontHaveActiveGroupList 1016 don't have any active group list.
MissingGroupName 1017 missing group name.
MissingMsgType 1018 missing message type.
InvalidMsgType 1019 invalid message type.
OtherCause -1 custom errors.