Workflows
Initialization
In order to use the Cloudonix Mobile SDK in your application, it must first be initialized with the license key (which you receive from Cloudonix, either a trial or a production license key). The application would load the license key into the Cloudonix Mobile SDK to start the SDK initialization process. This process will verify the license, configure the SDK for the device by utilizing Cloudonix on-line device configuration service and will eventually issue the licensing completion event to the application. If the licensing process failed - for example if the license key is corrupt or has expired - then the SDK will not be operational.
Following that, the application should follow these steps:
- Register to receive unsolicited events from the Cloudonix Mobile SDK, so that the SDK can notify the application of important events such as receiving an incoming call or the remote side hanging up an active call. This is done using the
addListener()
API with an object that implements theCloudonixSDKClientListener
protocol. - Set up the SDK configuration parameters such as allowed codecs and transports, using the
setConfig()
API. - Set up the SIP account details, that will be used for making and receiving calls, using the
setConfiguration()
API. - Wait for the
onSipStarted
event to be received by theCloudonixSDKClientListener
protocol.
This initialization process can written in Swift as such:
func initCloudonixSDK() {
// load the licnse key from a resource
guard let url = Bundle.main.url(
forResource: "cloudonix_license_key",
withExtension: ".lic") else {
return
}
guard let data = try? Data(contentsOf: url) else {
return
}
var licenseKey = String(data: data, encoding: .utf8)
CloudonixSDKClient.sharedInstance().initialize(withKey: licenseKey) {
(success, error) in
if success {
// `self` should implement `CloudonixSDKClientListener`
CloudonixSDKClient.sharedInstance().add(self)
CloudonixSDKClient.sharedInstance().setConfig("USER_AGENT", value: "MyApp/1.0")
let regData = CloudonixRegistrationData()
regData.serverUrl = "sip.cloudonix.io"
regData.port = 5060
regData.transportType = TRANSPORT_TYPE_TLS
regData.domain = "example.com"
regData.username = "rowdy"
regData.password = "rawhide"
regData.displayName = "Rowdy Rawhide"
CloudonixSDKClient.sharedInstance().setConfiguration(regData)
} else {
return
}
}
}
Registration Modes Setup
The Cloudonix Mobile SDK supports both classic SIP accounts using periodic REGISTER messages to maintain a “connection” to the server, as well as Cloudonix Registration-Free mode.
Registration Mode
In a classic SIP registration use case, the application should:
- Make sure to set up a
CloudonixRegistrationData
object contains the correct SIP account details (including SIP domain name and transport details) and send it to theCloudonixSDKClient
using thesetConfiguration()
call during initialization. - Once the application's
CloudnixSDKClientListener
protocol receives theonSipStarted()
event - callregisterAccount()
to start the registration process.
After calling registerAccount()
the Cloudonix Mobile SDK will fire the onRegisterState()
event whenever the registration state changes. The application can also call isRegistered()
to check the registration status. If the application does not need to receive calls, it is possible to not call registerAccount()
- this will have the effect that the SDK will consume less resources (resources that would have been used to maintain the registration status of the account), but in such a case it is recommended to also set the CloudonixRegistrationData.workflow
field to WORKFLOW_TYPE_REGISTRATION_LESS
to allow the SDK to optimize of this use case.
Regardless if the application has called registerAccount()
or not, after the SIP stack was started, the application may call one of the dial()
methods to start a call using the configured SIP account details configured.
Example Registration
func onSipStarted() {
CloudonixSDKClient.sharedInstance().registerAccount()
}
func onRegisterState(_ result: CloudonixRegistrationState_e, expiry: Int32) {
switch result {
case REGISTRATION_SUCCESS:
print("registered")
case REGISTRATION_ERROR_CREDENTIALS:
print("auth error")
case REGISTRATION_UNREGISTERED:
print("No longer registered")
}
}
Registration-Free mode
In a Registration-Free use case, there is no need for the application to perform SIP registration as call reception is handled by the application backend using push notifications, and there is also no need to set user authorization credentials as the application will be authorizing each call separately using a Cloudonix one-time session token.
During initialization, make sure that the CloudonixRegistrationData
object contains the correct SIP account details (including SIP domain name and transport details, though username and password should not be set) and send it to the CloudonixSDKClient
using the setConfiguration()
.
At any point after the CloudonixSDKClientListener
receives the onSipStarted()
event, the application can dial or receive calls, using the dialRegistrationFree()
method.
Dialing Workflows
Dialing In Registration Mode
After the application completes setting up the SDK and configuring the SIP account details, the application may use the dial() command to start a SIP session.
The dial command will start the calling process and will cause the Cloudonix SDK to issue onCallState()
events for each stage in the calling process.
Example Dial in Registration Mode
func dial(_ phoneNumber: String) {
if (!CloudonixSDKClient.sharedInstance().isRegistered())
return
CloudonixSDKClient.sharedInstance().dial(phoneNumber)
}
func onCallState(_ callId: String!, callState: CloudonixCallState_e, contactUrl: String!) {
switch callState {
case CallState_Starting:
print("Starting call", callId, "to number:", contactUrl)
case CallState_Connecting:
print("Connecting call", callId, "to number:", contactUrl)
case CallState_Calling:
print("Calling call", callId, "to number:", contactUrl)
case CallState_Ringing:
print("Ringing call", callId, "to number:", contactUrl)
case CallState_Confirmed:
print("Connected call", callId, "to number:", contactUrl)
case CallState_Disconnected,
CallState_DisconnectedDueToBusy,
CallState_DisconnectedMediaChanged
CallState_DisconnectedDueToNetworkChange,
CallState_DisconnectedDueToNoMedia,
CallState_DisconnectedDueToTimeout:
print("Hangedup call", callId, "to number:", contactUrl)
default:
break
}
}
Dialing in Registration-Free Mode
After the application completes setting up the SDK and configuring the SIP account details (usually just the SIP server address and domain name), the application may start a Registration-Free dial by obtaining a session token from application backend (please refer to the Cloudonix Registration-Free Dialing specification for more details) and then use the dialRegistrationFree(number, token)
method to start a call.
It is also possible to set up the SIP stack only right before making a call (or receiving a call). This is useful in case the application manages multiple subscriber accounts and knows which account to use for a call only at the last second. In such a case it is important to wait for the onSipStarted()
event before issuing the dial call, for example:
Example Dial in Registration-Free
var sessionToken = ""
var msisdn = ""
func makeCall(_ phoneNumber: String, token: String, regData: CloudonixRegistrationData) {
sessionToken = token
msisdn = phoneNumber
CloudonixSDKClient.sharedInstance().add(self)
CloudonixSDKClient.sharedInstance().setConfiguration(regData)
}
func onSipStarted() {
CloudonixSDKClient.sharedInstance().dialRegistrationFree(msisdn, session: sessionToken)
}
Incoming Calls
Call Reception in Registration Mode
When a call is received, the onCallState() event will be invoked with the CallState set to CALL_STATE_INCOMING. When the application handles the event, it can call either answer(callId) or reject(callId) as required. See Call Maintenance below for additional explanation of the onCallState() event.
Eg.
func onCallState(_ callId: String!, callState: CloudonixCallState_e,
contactUrl: String!) {
if callState == CallState_Incoming {
CloudonixSDKClient.sharedInstance().answer(callId)
}
}
Call Reception in Registration-Free Mode
When a push notification for an incoming call is received, containing a Registration-Free call token, the application should make sure that onSipStarted() has already been received, and then use the dialRegistrationFree(msisdn, token) call to accept the call.
Please note that the phone number provided in the first parameter can be of any value (the token is the only value that determines the success of receiving the call), but its value must never be set to null.
Call Maintenance
During the life of a call, from beginning to end, the Cloudonix Mobile SDK will call the client’s onCallState() event handle multiple times passing the call identifier (“key”), the update call “state” and additional details about the call. The event listener should examine the call state and perform actions to reflect the current state of the call to the user, as needed.
Normally a client is expected to handle at least the following events:
CALL_STATE_INCOMING
- The client should display a UI to indicate that the call is incoming and later use the Cloudonix Mobile SDK’s answer() method to pick up the incoming SIP callCALL_STATE_CONFIRMED
- The client should display a UI to indicate the call is in progress and offer additional UI to the user to perform call operations (such as hanging up).CALL_STATE_DISCONNECTED
(and all its variations) - The client should recover and reset all resources used to handle the call and remove any “in call” UI. See the “Call States” reference table, at the end of this document, for a complete list of possible call states and what they mean.
Sending DTMF
It is common for users to want to send DTMF signals to the remote end, to signal that the user has pressed on a standard numeric keypad during the call. The client can use the Cloudonix Mobile SDK’s DTMF() method. The method call receives the call ID of the call on which to send DTMF signals, and a character representing the DTMF signal to send.
Eg.
func sendDTMF(code: Int) {
CloudonixSDKClient.sharedInstance().dtmf(currentCallId, digit: Int8(code))
}
Application Shutdown
It is important to note that when the application is moved to the background, the SDK will still maintain context to allow the application to receive calls. If the application needs to shut down completely and not receive any calls, it should call shutdown()
, after which the SDK is completely shut down and in order to start it again it needs to be reinitialized.