Skip to content

Commit f8610ee

Browse files
authored
Merge pull request #222652 from raosanat/callkit-doc
Callkit doc
2 parents 1b293c8 + 346556f commit f8610ee

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
---
2+
ms.date: 01/06/2023
3+
ms.topic: how-to
4+
author: raosanat
5+
ms.author: sanathr
6+
title: CallKit integration in ACS Calling SDK
7+
ms.service: azure-communication-services
8+
ms.subservice: calling
9+
description: Steps on how to integrate CallKit with ACS Calling SDK
10+
---
11+
12+
# Integrate with CallKit
13+
14+
In this document, we'll go through how to integrate CallKit with your iOS application.
15+
16+
> [!NOTE]
17+
> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. To use this api please use 'beta' release of Azure Communication Services Calling iOS SDK
18+
19+
## Prerequisites
20+
21+
- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F).
22+
- A deployed Communication Services resource. [Create a Communication Services resource](../../quickstarts/create-communication-resource.md).
23+
- A user access token to enable the calling client. For more information, see [Create and manage access tokens](../../quickstarts/access-tokens.md).
24+
- Optional: Complete the quickstart to [add voice calling to your application](../../quickstarts/voice-video-calling/getting-started-with-calling.md)
25+
26+
## CallKit Integration (within SDK)
27+
28+
CallKit Integration in the ACS iOS SDK handles interaction with CallKit for us. To perform any call operations like mute/unmute, hold/resume, we only need to call the API on the ACS SDK.
29+
30+
### Initialize call agent with CallKitOptions
31+
32+
With configured instance of `CallKitOptions`, we can create the `CallAgent` with handling of `CallKit`.
33+
34+
```Swift
35+
let options = CallAgentOptions()
36+
let callKitOptions = CallKitOptions(with: createProviderConfig())
37+
options.callKitOptions = callKitOptions
38+
39+
// Configure the properties of `CallKitOptions` instance here
40+
41+
self.callClient!.createCallAgent(userCredential: userCredential,
42+
options: options,
43+
completionHandler: { (callAgent, error) in
44+
// Initialization
45+
})
46+
```
47+
48+
### Specify call recipient info for outgoing calls
49+
50+
First we need to create an instance of `StartCallOptions()` for outgoing calls, or `JoinCallOptions()` for group call:
51+
```Swift
52+
let options = StartCallOptions()
53+
```
54+
or
55+
```Swift
56+
let options = JoinCallOptions()
57+
```
58+
Then create an instance of `CallKitRemoteInfo`
59+
```Swift
60+
options.callKitRemoteInfo = CallKitRemoteInfo()
61+
```
62+
63+
1. Assign value for `callKitRemoteInfo.displayNameForCallKit` to customize display name for call recipients and configure `CXHandle` value. This value specified in `displayNameForCallKit` is exactly how it will show up in the last dialed call log.
64+
65+
```Swift
66+
options.callKitRemoteInfo.displayNameForCallKit = "DISPLAY_NAME"
67+
```
68+
2. Assign the `cxHandle` value is what the application will receive when user calls back on that contact
69+
```Swift
70+
options.callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")
71+
```
72+
73+
### Specify call recipient info for incoming calls
74+
75+
First we need to create an instance of `CallKitOptions`:
76+
77+
```Swift
78+
let callKitOptions = CallKitOptions(with: createProviderConfig())
79+
```
80+
81+
Configure the properties of `CallKitOptions` instance:
82+
83+
Block that is passed to variable `provideRemoteInfo` will be called by the SDK when we receive an incoming call and we need to get a display name for the incoming caller, which we need to pass to the CallKit.
84+
85+
```Swift
86+
callKitOptions.provideRemoteInfo = self.provideCallKitRemoteInfo
87+
88+
func provideCallKitRemoteInfo(callerInfo: CallerInfo) -> CallKitRemoteInfo
89+
{
90+
let callKitRemoteInfo = CallKitRemoteInfo()
91+
callKitRemoteInfo.displayName = "CALL_TO_PHONENUMBER_BY_APP"
92+
callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")
93+
return callKitRemoteInfo
94+
}
95+
```
96+
97+
### Configure audio session
98+
99+
Configure audio session will be called before placing or accepting incoming call and before resuming the call after it has been put on hold.
100+
101+
```Swift
102+
callKitOptions.configureAudioSession = self.configureAudioSession
103+
104+
public func configureAudioSession() -> Error? {
105+
let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
106+
var configError: Error?
107+
do {
108+
try audioSession.setCategory(.playAndRecord)
109+
} catch {
110+
configError = error
111+
}
112+
return configError
113+
}
114+
```
115+
116+
NOTE: In cases where Contoso has already configured audio sessions DO NOT provide `nil` but return `nil` error in the block
117+
118+
```Swift
119+
callKitOptions.configureAudioSession = self.configureAudioSession
120+
121+
public func configureAudioSession() -> Error? {
122+
return nil
123+
}
124+
```
125+
if `nil` is provided for `configureAudioSession` then SDK will call the default implementation in the SDK.
126+
127+
### Handle incoming push notification payload
128+
129+
When the app receives incoming push notification payload, we need to call `handlePush` to process it. ACS Calling SDK will then raise the `IncomingCall` event.
130+
131+
```Swift
132+
public func handlePushNotification(_ pushPayload: PKPushPayload)
133+
{
134+
let callNotification = PushNotificationInfo.fromDictionary(pushPayload.dictionaryPayload)
135+
if let agent = self.callAgent {
136+
agent.handlePush(notification: callNotification) { (error) in }
137+
}
138+
}
139+
140+
// Event raised by the SDK
141+
public func callAgent(_ callAgent: CallAgent, didRecieveIncomingCall incomingcall: IncomingCall) {
142+
}
143+
```
144+
145+
We can use `reportIncomingCallFromKillState` to handle push notifications when the app is closed.
146+
`reportIncomingCallFromKillState` API shouldn't be called if `CallAgent` instance is already available when push is received.
147+
148+
```Swift
149+
if let agent = self.callAgent {
150+
/* App is not in a killed state */
151+
agent.handlePush(notification: callNotification) { (error) in }
152+
} else {
153+
/* App is in a killed state */
154+
CallClient.reportIncomingCallFromKillState(with: callNotification, callKitOptions: callKitOptions) { (error) in
155+
if (error == nil) {
156+
DispatchQueue.global().async {
157+
self.callClient = CallClient()
158+
let options = CallAgentOptions()
159+
let callKitOptions = CallKitOptions(with: createProviderConfig())
160+
callKitOptions.provideRemoteInfo = self.provideCallKitRemoteInfo
161+
callKitOptions.configureAudioSession = self.configureAudioSession
162+
options.callKitOptions = callKitOptions
163+
self.callClient!.createCallAgent(userCredential: userCredential,
164+
options: options,
165+
completionHandler: { (callAgent, error) in
166+
if (error == nil) {
167+
self.callAgent = callAgent
168+
self.callAgent!.handlePush(notification: callNotification) { (error) in }
169+
}
170+
})
171+
}
172+
} else {
173+
os_log("SDK couldn't handle push notification KILL mode reportToCallKit FAILED", log:self.log)
174+
}
175+
}
176+
}
177+
```
178+
179+
## CallKit Integration (within App)
180+
181+
If you wish to integrate the CallKit within the app and not use the CallKit implementation in the SDK, please take a look at the quickstart sample [here](https://github.com/Azure-Samples/communication-services-ios-quickstarts/tree/main/Add%20Video%20Calling).
182+
But one of the important things to take care of is to start the audio at the right time. Like following
183+
184+
```Swift
185+
let mutedAudioOptions = AudioOptions()
186+
mutedAudioOptions.speakerMuted = true
187+
mutedAudioOptions.muted = true
188+
189+
let copyStartCallOptions = StartCallOptions()
190+
copyStartCallOptions.audioOptions = mutedAudioOptions
191+
192+
callAgent.startCall(participants: participants,
193+
options: copyStartCallOptions,
194+
completionHandler: completionBlock)
195+
```
196+
197+
Muting speaker and microphone will ensure that physical audio devices aren't used until the CallKit calls the `didActivateAudioSession` on `CXProviderDelegate`. Otherwise the call may get dropped or no audio will be flowing.
198+
199+
```Swift
200+
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
201+
activeCall.unmute { error in
202+
if error == nil {
203+
print("Successfully unmuted mic")
204+
activeCall.speaker(mute: false) { error in
205+
if error == nil {
206+
print("Successfully unmuted speaker")
207+
}
208+
}
209+
}
210+
}
211+
}
212+
```
213+
214+
> [!NOTE]
215+
> In some cases CallKit doesn't call `didActivateAudioSession` even though the app has elevated audio permissions, in that case the audio will stay muted until the call back is received. And the UI has to reflect the state of the speaker and microphone. The remote participant/s in the call will see that the user has muted audio as well. User will have to manually unmute in those cases.
216+
217+
## Next steps
218+
- [Learn how to manage video](./manage-video.md)
219+
- [Learn how to manage calls](./manage-calls.md)
220+
- [Learn how to record calls](./record-calls.md)

articles/communication-services/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ items:
401401
href: how-tos/calling-sdk/browser-support.md
402402
- name: Verify if an application is active in multiple tabs of a browser
403403
href: how-tos/calling-sdk/is-sdk-active-in-multiple-tabs.md
404+
- name: Integrate with CallKit in iOS
405+
href: how-tos/calling-sdk/callkit-integration.md
404406
- name: Using the UI Library
405407
items:
406408
- name: Setup Localization

0 commit comments

Comments
 (0)