Cisco Spark

SDK for iOS

Integrate Cisco Spark into your iOS apps quickly with the Cisco Spark SDK for iOS

Features

Integrate the iOS SDK into iOS apps to place and receive Spark video calls:

  • Audio and/or video 1:1 calling
  • Dial by email, Spark User ID, or SIP address
  • Call and event controls, including DTMF
  • Audio and video call control
Getting Started Guide

The Spark iOS SDK is the easiest way to integrate Cisco Spark into your iOS app.

Overview

  • Create Spark rooms
  • Create Spark teams
  • Add users/members/moderators into rooms/teams
  • Post messages/share attachments
  • Make and receive audio/video calls

Requirements

  • iOS app using Swift 3
  • Xcode 8.0 or later
  • iOS 9 or later
  • CocoaPods
  • Spark Account
  • Use of this SDK requires the spark:all scope

Step 1: Prepare the Workspace

The easiest way to integrate the Cisco Spark iOS SDK into your app is to add it to your project with CocoaPods. Follow these steps to create a new Xcode workspace that will use CocoaPods to install the SDK.

Installation and Setup of CocoaPods

Using Terminal, install the CocoaPods Ruby gem and perform initial configuration:

gem install cocoapods
pod setup

Workspace Creation

Open Xcode and create a new project:

  • Click File > New > Project..., select the iOS > Application > Single View Application template, and click Next.
  • Set the Product Name to "SparkDemoApp", Organization Name to your name, Organization Identifier to "com.example", and Language to Swift. Click Next.
  • Select a destination directory for the project and click Create.

In a few steps we'll use CocoaPods to create a new Xcode Workspace for us. For now, close the new project by clicking File > Close Project.

Create a new file named Podfile in the SparkDemoApp directory with the following contents:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!

target 'SparkDemoApp' do
pod 'SparkSDK'
end

Using Terminal, navigate to the SparkDemoApp directory and install the Spark iOS SDK (specified in the Podfile):

pod install

After installation is complete, CocoaPods will create a new SparkDemoApp.xcworkspace file for the project. From now on, open and use this file instead of the original Xcode project file, otherwise you will face issues with Framework dependencies.

Importing the Spark SDK

If you are creating a new app, import the Spark SDK library by adding the following line to your ViewController.swift file:

import SparkSDK

You can then add buttons to the storyboard's canvas, connect them to new actions in the View Controller, and start using the Spark SDK's functionality to interact with Cisco Spark.

If you are adding the Spark SDK to an already functional app, you can simply import the library in your Swift files to start using the SDK.

Keep reading for details about how to use the Spark SDK with your application, starting with authenticating the user, then moving on to creating rooms and sending messages.

Step 2: App Integration and OAuth 2

You must first register a Spark Integration before your application can use Spark on behalf of a user. Once registration is complete, you will get a Client ID and Client Secret for use with the app. These can be used to generate a token with the proper scopes, but luckily the iOS SDK has a method which will perform this step for you:

class LoginViewController: UIViewController {

  @IBAction func loginWithSpark(sender: AnyObject) {
    let clientId = "..."
    let clientSecret = "..."
    let scope = "spark:all"
    let redirectUri = "Sparkdemoapp://response"

    let authenticator = OAuthAuthenticator(clientId: clientId, clientSecret: clientSecret, scope: scope, redirectUri: redirectUri)
    let spark = Spark(authenticator: authenticator)

    if !authenticator.authorized {
      authenticator.authorize(parentViewController: self) { success in
        if !success {
          print("User not authorized")
        }
      }
    }
  }
}

Step 3: Let's Try Some Spark Messaging

Now that you're authenticated, you can now use Spark. It's easy to create a room, add users, and post messages using the SDK.

To create a room:

spark.rooms.create(title: "Hello World") { response in
  switch response.result {
  case .success(let room):
      // ...
  case .failure(let error):
      // ...
  }
}

To add users to the room:

spark.memberships.create(roomId: roomId, personEmail: email) { response in
  switch response.result {
  case .success(let membership):
      // ...
  case .failure(let error):
      // ...
  }
}

Post a message into the room:

spark.messages.post(personEmail: email, text: "Hello there") { response in
  switch response.result {
  case .success(let message):
      // ...
  case .failure(let error):
      // ...
  }
}

Add a message with an attachment:

spark.messages.post(personEmail: email, files: "http://example.com/hello_world.jpg") { response in
  switch response.result {
  case .success(let message):
      // ...
  case .failure(let error):
      // ...
  }
}

Teams are quite useful if you want to create a set of rooms for only certain members of the team. Teams also have an independent membership management interface for inviting/deleting/listing users and adding a moderator to the team room.

To create a team:

spark.teams.create(name: "Hello World") { response in
  switch response.result {
  case .success(let team):
      // ...
  case .failure(let error):
      // ...
  }
}

To add users to the team:

spark.teamMemberships.create(teamId: teamId, personEmail: email) { response in
  switch response.result {
  case .success(let membership):
      // ...
  case .failure(let error):
      // ...
  }
}

Set moderator for the team:

spark.teamMemberships.create(teamId: teamId, personEmail: email, isModerator: true) { response in
  switch response.result {
  case .success(let membership):
      // ...
  case .failure(let error):
      // ...
  }
}

Complete code snippet for rooms and team memberships:

// IM example
spark.rooms.create(title: "Hello World") { response in
  switch response.result {
  case .success(let room):
    print("\(room.title!), created \(room.created!): \(room.id!)")
    if let email = EmailAddress.fromString("coworker@acm.com"), let roomId = room.id {
      spark.memberships.create(roomId: roomId, personEmail: email) { response in
        spark.memberships.list { response in
          if let memberships = response.result.data {
            for membership in memberships {
              print("\(String(describing: membership.personEmail?.toString()))")
            }
          }
        }
        spark.messages.post(roomId: roomId, text: "Hello World") { response in

        }
      }
    }
  case .failure(let error):
    print("Error: \(error.localizedDescription)")
  }
}

Step 4: Spark Audio/Video Calling

This is the most significant SDK feature which enables users to make and receive audio/video calls using Spark. Calling in the SDK is very easy to use.

First, we need to register the device:

spark.phone.register() { error in
  if let error = error {
    ... // Device was not registered, and no calls can be sent or received
  } else {
    ... // Successfully registered device
  }
}

Once registration is complete, make a call:

// Make a call
spark.phone.dial("coworker@acm.com", option: MediaOption.audioVideo(local: ..., remote: ...)) { ret in
  switch ret {
  case .success(let call):
    call.onConnected = {

    }
    call.onDisconnected = { reason in

    }
  case .failure(let error):
    // failure
  }
}

The calls can be made to Spark users/devices, Telepresence systems, SIP devices, and regular telephones. If the user calls a telephone system such as an IVR, the SDK also supports DTMF transport so users can navigate IVR menus. iOS users can also view the content shared from any Telepresence system. It is a fully-integrated collaboration SDK.

To send DTMF, simply invoke call.send(dtmf:completionHandler:):

// Send DTMF
if let dtmfEvent = dialButton.text {
  call.send(dtmf: dtmfEvent, completionHandler: nil)
}

In order to receive a call, the callback function callIncoming() is used.

Handle the incoming call event:

// Recieve a call
spark.phone.onIncoming = { call in
  call.answer(option: MediaOption.audioVideo(local: ..., remote: ...)) { error in
  if let error = error {
    // success
  }
  else {
    // failure
  }
}

Complete Demo App

A complete demo application is available to see the complete functionality of the SDK.

Incoming Call Notification Guide

The iOS SDK provides the ability to make and receive calls via Cisco Spark. If your iOS app is running in the foreground on the user's device, the iOS SDK provides the phone.onIncoming callback to allow your app to handle new incoming calls. However, to alert users of incoming calls while your app is in the background or not running, you will need to use Spark Webhooks and the Apple Push Notification service (APNs). Spark Webhook events generated for new calls will be sent to your service for processing before using APNs to notify the user of the new call.

This guide will provide more detail about handling incoming calls while your app is in the background or not running. See the Phone SDK API reference for more details about handling incoming calls while your app is in the foreground.

Getting Started

Before we get started, please review the following:

  • You should be familiar with Spark Webhooks, including how to create them and how to handle their payloads. For more detailed information about Webhooks, please see the Webhooks Explained guide.
  • An external server or service is required to receive the events from Spark and to send remote notifications to APNs.
  • Your service will need to process and store iOS device tokens for use with APNs.

To generate iOS notifications for incoming calls, a webhook will be used to generate an event when an incoming call is received for a particular user. This event will be sent to your service as an HTTP POST of JSON data. This JSON data will include information about the new call.

When a user enables notifications in your app, your service should keep track of the user's Spark personId and their unique device token from APNs. Both of these pieces of information will be needed to process the webhook and generate the notification. When a webhook event is received by your service, it will use this information to determine which device to notify via APNs.

Registering with Apple Push Notification Service

To support remote push notifications, your app must have the proper entitlements. See Apple's documentation about push notifications for more information.

If your app hasn't already requested permission from the user to display notifications, you will first need to prompt them for permission.

UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in
  // enable or disable features based on user's response
}

When permission has been granted, a unique identifier will be generated for this particular installation of your app. This token will be needed for remote notifications via APNs. If registration is successful, the app calls your app delegate object's application(_:didRegisterForRemoteNotificationsWithDeviceToken:) method. Use this method to send the personId of the Spark user and the unique token to your service. Both of these values will be needed to process webhook events and generate notifications.

Creating Webhooks

You can create a new webhook for the user to notify your service of new calls with the iOS SDK. Configure the webhook to use your server as the targetUrl. To limit this webhook to only incoming calls, use the new callMembership webhook resource and look for created events. A specific filter will limit results to notification events (state=notified) for the authenticated user (personId=me).

spark.webhooks.create(name: "Incoming Call Webhook", targetUrl: "https://example.com/incoming_call_handler", resource: "callMemberships", event: "created", filter: "state=notified&personId=me") { res in
    switch res.result {
        case .success(let webhook):
            // perform positive action
        case .failure(let error):
            // perform negative action
    }
}

After creating the webhook, your service will now be notified of incoming calls for this user.

Service Integration

Webhook events will be sent to your service as an HTTP POST of JSON data. The payload will look something like this:

{
    "id": "Y2lzY29zcGFyazovL3VzL1dFQkhPT0svZjRlNjA1NjAtNjYwMi00ZmIwLWEyNWEtOTQ5ODgxNjA5NDk3",
    "name": "Incoming Call Webhook",
    "targetUrl": "https://example.com/incoming_call_handler",
    "resource": "callMemberships",
    "event": "created",
    "filter": "state=notified&personId=Y2lzY29zcGFyazovL3VzL1BFT1BMRS9lM2EyNjA4OC1hNmRiLTQxZjgtOTliMC1hNTEyMzkyYzAwOTg",
    "orgId": "Y2lzY29zcGFyazovL3VzL09SR0FOSVpBVElPTi8xZWI2NWZkZi05NjQzLTQxN2YtOTk3NC1hZDcyY2FlMGUxMGY",
    "createdBy": "Y2lzY29zcGFyazovL3VzL1BFT1BMRS8xZjdkZTVjYi04NTYxLTQ2NzEtYmMwMy1iYzk3NDMxNDQ0MmQ",
    "appId": "Y2lzY29zcGFyazovL3VzL0FQUExJQ0FUSU9OL0MyNzljYjMwYzAyOTE4MGJiNGJkYWViYjA2MWI3OTY1Y2RhMzliNjAyOTdjODUwM2YyNjZhYmY2NmM5OTllYzFm",
    "ownedBy": "creator",
    "status": "active",
    "created": "2016-11-02T16:35:11.636Z",
    "actorId": "Y2lzY29zcGFyazovL3VzL1BFT1BMRS8xZjdkZTVjYi04NTYxLTQ2NzEtYmMwMy1iYzk3NDMxNDQ0MmQ",
    "data": {
        "id": "Y2lzY29zcGFyazovL3VzL01FU1NBR0UvMzIzZWUyZjAtOWFhZC0xMWU1LTg1YmYtMWRhZjhkNDJlZjlj",
        "callId": "Y2lzY29zcGFyazovL3VzL1JPT00vY2RlMWRkNDAtMmYwZC0xMWU1LWJhOWMtN2I2NTU2ZDIyMDdi",
        "personId": "Y2lzY29zcGFyazovL3VzL1BFT1BMRS9lM2EyNjA4OC1hNmRiLTQxZjgtOTliMC1hNTEyMzkyYzAwOTg",
        "personOrgId": "Y2lzY29zcGFyazovL3VzL09SR0FOSVpBVElPTi8xZWI2NWZkZi05NjQzLTQxN2YtOTk3NC1hZDcyY2FlMGUxMGY",
        "personEmail": "person@example.com",
        "state": "notified",
        "totalJoinDuration": 0,
        "isInitiator": false,
        "created": "2016-11-02T16:40:07.435Z"
    }
}

Using this payload, you can use the personId to determine which device, or devices, should be notified via APNs of the new call.

iOS App Notifications

After your service receives a webhook notification and determines the proper device, or devices, to notify, it will then need to send an event to APNs.

Please see Apple's documentation for Creating the Remote Notification Payload for use with APNs and Communicating with APNs to send the notification.

Once the notification is received and processed by the device, your app can then respond to the incoming call and prompt the user to accept or deny the call.

Troubleshooting the iOS SDK

If you're having trouble with the iOS SDK, here's some more information to help troubleshoot the issue.

SDK Requirements

Review the following SDK requirements to make sure you're using the correct minimum versions of macOS, Xcode, etc.:

  • A macOS computer running Xcode 8 or later
  • Your app must be targeted for iOS 9 or later
  • CocoaPods for installing the SDK
  • A Cisco Spark account and integration with the spark:all scope

Additional Logging

You can add additional logging to your application to help narrow down any issues with the SDK.

Adding a Custom Logger

class MyLogger: Logger {
  func log(message: LogMessage) {
    // print(message.description)
  }
}
spark.logger = MyLogger()

Adding Console Logging

spark.consoleLogger = LogLevel.all

Firewall Ports

The iOS SDK makes use of the following network ports. If you're encountering connection issues, make sure there aren't any firewalls blocking or preventing communication over these ports.

Service Protocol Port(s)
Messaging HTTPS 443
Notifications WebSocket 443
Calls HTTPS 443
Media RTP/SRTP over UDP/TCP 33434-33598, 8000-8100 or 33434 (shared port)

SDK Dependencies

For more information about dependencies of the iOS SDK, please refer to their documentation:

App Crashes

If your app is crashing, crash reports may help you determine the root cause. Please see Apple's Understanding and Analyzing Application Crash Reports for more information about diagnosing iOS app crashes.

Getting Support

If you're stumped, contact the Spark for DevSupport team for more help with the SDK.