Getting Started with iOS libSoftphone¶
This section describes the steps to create an iOS application that can place and receive calls using libSoftphone SDK.
Adding SoftphoneSwift Dependency¶
To add a package dependency to the Xcode project:
Open your Xcode project.
Select the Package Dependencies tab.
Select the + button.
In the Enter Package URL field, add the URL of the libSoftphone Swift package https://github.com/acrobits/SoftphoneSwiftPackage-saas.git.
Select the Add Package button.
After adding the dependency, Xcode automatically includes the Softphone
dependencies.
Initializing the SDK¶
Use the initialize
method to initialize SoftphoneBridge
. This method takes XML string as a parameter.
This string represents an XML structure containing the license key and the configuration of the SDK.
1let license = """
2 <root>
3 <saas>
4 <identifier>your license key</identifier>
5 </saas>
6 </root>
7 """
8
9// This method is used for initializing an instance of SDK
10SoftphoneBridge.initialize(license)
Note
Replace your license key between the <identifier>
XML tags with a valid license key obtained in the Acrobits licensing portal.
This key is required for initializing the SDK.
See step 1 in the Quick Start Guide.
Reporting Application States¶
libSoftphone SDK must have accurate information about the current state of the application to handle SIP registration, push calls, and overall SDK functionality properly.
To report the application state changes to the SDK, call the update
method of the SoftphoneBridge.instance().state()
instance.
This method takes the InstanceStateType
enum as a parameter. This enum contains all possible application states.
The following code snippet illustrates the functions that handle different application state transitions using the update
method:
1func applicationDidBecomeActive(_ application: UIApplication) {
2 let bg = UIApplication.shared.applicationState == .background
3 SoftphoneBridge.instance().state().update(bg ? InstanceState_Background : InstanceState_Active)
4}
5
6func applicationWillResignActive(_ application: UIApplication) {
7 SoftphoneBridge.instance().state().update(InstanceState_Inactive)
8}
9
10func applicationDidEnterBackground(_ application: UIApplication) {
11 SoftphoneBridge.instance().state().update(InstanceState_Background)
12}
13
14func applicationWillEnterForeground(_ application: UIApplication) {
15 SoftphoneBridge.instance().state().update(InstanceState_Active)
16}
Managing SIP Account¶
Set up a SIP account before you can place calls. SIP accounts are specified in XML format.
To view all the recognized XML nodes and values that can be used to configure SIP accounts, go to the Account XML documentation at doc.acrobits.net/cloudsoftphone/account.html#account-xml.
Creating a SIP Account¶
There are two ways to create a SIP account:
Include the following code to create an account with a basic configuration containing the username, password, and host:
1let xml = XmlTree(name: "account") 2xml?.setAttribute(name: "id", value: "sip") 3xml?.setNodeValue(value: "Sip Account", name: "title") 4xml?.setNodeValue(value: "1125", name: "username") 5xml?.setNodeValue(value: "misscom", name: "password") 6xml?.setNodeValue(value: "pbx.acrobits.cz", name: "host") 7xml?.setNodeValue(value: "udp", name: "transport") 8 9SoftphoneBridge.instance().registration().saveAccount(xml) 10SoftphoneBridge.instance().registration().updateAll()If you have a XML string for an account, parse the XML string using the
XmlTree
class, and then pass it to thesaveAccount
method.1let sipAccount = """ 2 <account id=\"sip\"> 3 <title>Sip Account</title> 4 <username>1125</username> 5 <password>misscom</password> 6 <host>pbx.acrobits.cz</host> 7 <transport>udp</transport> 8 </account> 9""" 10 11let xml = XmlTree.parse(sipAccount) 12SoftphoneBridge.instance().registration().saveAccount(xml) 13SoftphoneBridge.instance().registration().updateAll()
Note the following:
The
id
attribute must be specified to identify an account. If not, a unique one is generated for that account upon calling thesaveAccount
method.Calling
SoftphoneBridge.instance().registration().saveAccount()
with an XML that has the sameid
as an existing account replaces the existing account with the new values.If you save a new account with an
id
that matches the ID of an existing account but different account configuration, the new account (re)registers asynchronously.The account settings are stored on disk, so you do not have to recreate the account when the application restarts.
Deleting an Account¶
Call the deleteAccount
method of the SoftphoneBridge.instance().registration()
instance to delete accounts.
This method takes account ID as a parameter.
1SoftphoneBridge.instance().registration().deleteAccount("sip")
Observing Events from SDK¶
You can implement your own Softphone Delegates to receive notifications about important events that occur within the SDK.
To set up the delegates for the SDK, create the
SoftphoneObserverProxyBridge
instance and configure it to act as the delegate for thesoftphone
instance. Set an observer immediately after the SDK initialization.The following example code snippet demonstrates the setup:
1let softphoneObserverProxy = SoftphoneObserverProxyBridge() 2softphoneObserverProxy.delegate = self; 3SoftphoneBridge.instance().setObserver(softphoneObserverProxy)You can implement the
SoftphoneDelegateBridge
protocol and all the methods of this protocol in your class. Some of these methods are optional. The following code snippet gives examples of implementing theSoftphoneDelegateBridge
protocol:1// This delegate will be called when the registration state changes 2func onRegistrationStateChanged(state: RegistratorStateType, accountId: String!) { 3 4} 5 6// This delegate will be called when a new event arrives (i.e. incoming call/message) 7func onNewEvent(_ event: SoftphoneEvent!) { 8 9} 10 11// This delegate will be called when the state of the call changes 12func onCallStateChanged(state: CallStateType, call: SoftphoneCallEvent!) { 13 14} 15 16// This delegate will be called when the network change is detected 17func onNetworkChangeDetected(_ network: NetworkType) { 18 19} 20 21// This delegate will be called when the hold state of a call is changed 22func onHoldStateChanged(states: CallHoldStates!, call: SoftphoneCallEvent!) { 23 24}Set up
Softphone_Cx_Delegate
so that you can manage the interaction with iOS CallKit:
To modify
CXCallUpdate
before it is reported to the system.To modify
CXProviderConfiguration
created by the SDK or to create a new one.The following code snippet illustrates setting up the delegate for the
Softphone_Cx
instance.1Softphone_Cx.instance().delegate = self
Placing a Call¶
Use the SDK to enable the application to make calls.
To place calls:
Create
SoftphoneCallEvent
with a specifiedaccountId
anduri
.Associate the
SoftphoneCallEvent
object with a specific stream.Send the call event using the
post
method provided by theSoftphoneBridge.instance().events()
instance.The method returns the status of the post as the
EventsPostResult
enum.
The following snippet code illustrates the process of initiating a call:
1func call(number: String) {
2 if number.isEmpty {
3 return;
4 }
5
6 let call = SoftphoneCallEvent.create(withAccountId: "sip", uri: number)
7 let stream = SoftphoneEventStream.load(SoftphoneStreamQuery.legacyCallHistoryStreamKey())
8
9 call?.setStream(stream)
10 call?.transients.set("voiceCall", forKey: "dialAction")
11
12 let result = SoftphoneBridge.instance().events().post(call)
13
14 if result != PostResult_Success {
15 print("Failed to post call event")
16 }
17}
Managing Preferences¶
The SDK provides the app-specific preferences function to manage the Preferences menu items in the application setting.
To do so, use the SoftphoneBridge.instance().settings().getPreferences()
instance, which provides access to both read-write and read-only properties related to preferences.
Reading Preferences¶
You can read various properties and check the state of a preference, such as SIP logging, acoustic echo suppression, and pressing keypad volume.
For example, to check if SIP traffic logging is enabled, use the following codes:
1if SoftphoneBridge.instance().settings().getPreferences().trafficLogging {
2 print("SIP traffic logging is enabled")
3}
Changing Preferences¶
You can modify various properties and change the value of a preference.
For example, the following code snippet demonstrates that setting the trafficLogging
property as true
to enable SIP traffic logging:
1SoftphoneBridge.instance().settings().getPreferences().trafficLogging = true
Using Call Event History¶
Use the Call Event History feature to store call history in the device’s local storage and to display a list of recent calls in your app. This feature is disabled by default.
To enable the Call Event History feature, add the following code in the app’s provisioning XML file:
1<root> 2 <saas> 3 <identifier>your license key</identifier> 4 </saas> 5 <prefKeys> 6 <prop name="historyStorageType" default="sql"/> 7 </prefKeys> 8</root>You can refer to the Initializing the SDK section to review the steps for supplying the provisioning file during SDK initialization.
Add the Call Event History listeners. Refer to the Observing Events from SDK section to set your own Softphone Delegate for the SDK.
The following code snippet shows how to register the
onEventsChanged
method of theSoftphoneDelegateBridge
protocol for iOS:1extension AppDelegate: SoftphoneDelegateBridge { 2 3 // onEventChanged methods and onMissedCalls will be called by the SDK as this class is now the delegate of the SoftphoneBridge. 4 5 func onEventsChanged(events: SoftphoneChangedEvents!, streams: SoftphoneChangedStreams!) { 6 // From here, you can trigger a refresh of the call history list in your app 7 8 // get changed events - only set if changedEvents.many is false 9 let changedIds = events.eventIds // id's of changed events 10 // check if many events changed, if true, changedIds is nil 11 let manyChanged = events.many 12 // check if any call event changed 13 let callEventsChanged = streams.streamKeys.contains(StreamQuery.legacyCallHistoryStreamKey()) 14 } 15}To display the call history in your app, perform a query to the device’s local storage and fetch all call events according to your filter and paging requirements, as shown in the following code snipet:
1let query = SoftphoneQuery() 2query.streamKey = SoftphoneStreamQuery.legacyCallHistoryStreamKey() 3let missedCallValue = Int32(CallResultType_Missed.rawValue) 4let attr = SoftphoneQueryAttr(key: "callResult", value: String(missedCallValue)) 5if let attr = attr { 6 query.withAttributes = [attr] 7} 8 9// optional paging for query results 10let paging = SoftphonePaging() 11/*paging.after = ...; 12paging.before = ...; 13paging.olderThan = ...; 14paging.offset = ...; 15paging.limit = ...; 16etc...*/ 17 18// fetch the call events 19let fetchResult = SoftphoneBridge.instance().events().fetch(query, paging: paging) 20 21// get the missed call count 22let missedCallsCount = fetchResult?.items.count ?? 0 23 24// get an array of missed calls 25let missedCalls = fetchResult?.items.compactMap { item -> SoftphoneCallEvent? in 26 guard let fetchItem = item as? SoftphoneFetchItem else { return nil } 27 return fetchItem.event.asCall() 28} ?? []
Handle Missed Calls¶
The SDK APIs make missed call management straightforward.
Displaying Missed Call Badge Count¶
To observe changes in the badge count, override the function onBadgeCountChanged()
from the SoftphoneBadgeCountChangeDelegate
.
1// Register self as the delegate for badge count changes
2SoftphoneBadgeManager.instance().registerBadgeCountDelegate(self)
3
4// Implement the callback method
5func onBadgeCountChanged() {
6 // Code to handle badge count change
7}
To get the badge count for the calls
channel, follow these steps:
1// Create a badge address for the calls channel
2let badgeAddress = SoftphoneBadgeAddress(channel: SoftphoneBadgeAddress.calls)
3
4// Get the badge count for the specified address
5let missedCallCount = SoftphoneBadgeManager.instance().getBadgeCount(address: badgeAddress)
To set the badge count for the calls
channel, use the following code:
1// Create a badge address for the calls channel
2let badgeAddress = SoftphoneBadgeAddress(channel: SoftphoneBadgeAddress.calls)
3
4// Set the badge count for the specified address to 3
5SoftphoneBadgeManager.instance().setBadgeCount(address: badgeAddress, count: 3)
To reset the badge count for the calls
channel to zero, use the following code:
1// Create a badge address for the calls channel
2let badgeAddress = SoftphoneBadgeAddress(channel: SoftphoneBadgeAddress.calls)
3
4// Reset the badge count for the specified address
5SoftphoneBadgeManager.instance().resetBadgeCount(address: badgeAddress)
Displaying Missed Call Notifications¶
Use the simple onMissedCalls
method to show notifications for missed calls in your app.
This method is triggered when a new missed call is detected and only when the app is running.
The following code snippet shows how to override the onMissedCalls
method of the SoftphoneDelegateBridge
protocol and show a notification with the total missed call count on iOS:
1extension AppDelegate: SoftphoneDelegateBridge {
2
3 func onMissedCalls(_ callEvents: [SoftphoneCallEvent]!) {
4 for call in callEvents {
5 showMissedCallNotification(call: call)
6 }
7 }
8
9 func showMissedCallNotification(call: SoftphoneCallEvent) {
10 let content = UNMutableNotificationContent()
11 content.title = call.getRemoteUser(index: 0).displayName
12 content.body = "Missed Call"
13
14 let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
15
16 UNUserNotificationCenter.current().add(request) { error in
17 if let error = error {
18 debugPrint("Notification request error: \(error.localizedDescription)")
19 }
20 }
21 }
22}
Displaying the List of All Missed Calls¶
Refer to the previous Using Call Event History section to see how to retrieve the list of all missed calls.
Enabling Logging¶
Enable logging to capture important events and activities that occur within the SDK.
By default, the logger is disabled. To enable logging, set the
trafficLogging
preference totrue
, as shown in the following code snippet:1SoftphoneBridge.instance().getPreferences().trafficLogging = trueTo disable logging, set the
trafficLogging
preference tofalse
.To direct the SDK logging into the iOS Console, define your own
NSLogSerializer
with a custom prefix (<your prefix>
), as shown in the following code snippet:1let serializer = NSLogSerializer(prefix: "<your prefix>") 2SoftphoneBridge.instance().log().setCustomSink(serializer)To retrieve logs at any point, use the following code snippet:
1let log = SoftphoneBridge.instance().log().get() 2print(log)
Setting up for Push Notifications¶
Ensure you already set up the VoIP push notifications in both your project and server prior to enabling push notifications. If the setup is not completed yet, go to the iOS Push notifications section for detailed instructions.
The push notifications function is disabled by default in libSoftphone SDK. Modify the provisioning XML file to enable and use this function with the application.
Important
Ensure that your application correctly reports its current state to the SDK. This is crucial for the push notifications to work properly. See Reporting Application States for the setup.
To set the SDK registration with the push servers, add the following snippet to the
prefKeys
node in the provisioning XML file.1<provisioning> 2 <saas> 3 <identifier>saas-identifier</identifier> 4 </saas> 5 6 <prefKeys> 7 <prop name="sipisDisabled" default="0"/> 8 <prop name="pushNotificationsEnabled" default="1"/> 9 </prefKeys> 10</provisioning>This snippet is passed to the
SoftphoneBridge.initialize
method to enable push notifications when libSoftphone SDK initializes.Enable push notifications so that the application can receive calls. To do so, call the
setRegistrationId
method on theSoftphoneBridge.instance().notifications().push()
instance. This method is used to pass the token data and the usage information to the SDK, as illustrated in the following code snippet:1func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) { 2 SoftphoneBridge.instance().notifications().push().setRegistrationId(pushCredentials.token, usage: PushTokenUsage_IncomingCall) 3}
Handling Push Notifications for Incoming Calls¶
After setting up the push notifications functions, pass the push notification payload to the SDK so that the SDK can handle push notifications for incoming calls.
To do so, call the handle
method on SoftphoneBridge.instance().notifications().push()
instance, as illustrated in the following code snippet:
1func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
2 let xml = Dictionary<AnyHashable, Any>.xmlFromDictionary(payload.dictionaryPayload)
3 SoftphoneBridge.instance().notifications().push().handle(xml, usage: PushTokenUsage_IncomingCall, completion: nil)
4}