How to implement SwiftUI(iOS14+) video call with Azure Communication Services
Intro
This article aims to show a work through of integrating ACS + ANH + CallKit + PushKit for SwiftUI iOS application. Yes right! all together at once. Sounds bit much though, you’ll get a nice working example after you go through this article.
ACS(Azure Communication Services) is fairly new to the industry(Pre release announced around Sep. 2020) and there are quite many voice chat service providers like Amazon, Twilio, and etc… But I chose ACS over other services because ACS integrates all the communication related services as one single service like connecting landline phone to the service(limited for now), text message, QnA chat bot. If you are keen to learn more about the service, please go to communication service overview.
Prerequisites(adopted from ACS quickstart tutorial and ANH quickstart tutorial)
- An Azure account with an active subscription. Create an account for free.
- A deployed Communication Services resource. Create a Communication Services resource.
- A
User Access Token
to enable the call client. For more information on how to get aUser Access Token
- An active Apple Developer account.
- A Mac running Xcode, along with a valid developer certificate installed into your Keychain.
- An iPhone or iPad running iOS version 10 or later.
- Your physical device registered in the Apple Portal and associated with your certificate.
For ANH prerequisites, please take option 2 of Create a certificate for Notification Hubs as we are going to use token-based authentication due to the service change for iOS13. See more detail here.
If you are having some trouble with above prerequisites, don’t worry it’s normal. Just read their manual and don’t miss what the manual says to do so then you’ll be fine. If you are still having some trouble with configuring above, please wait for my next article where I will be explaining those configuration step by step with detailed screenshots.
Project initialization
Create a new project with Product Name and Organization Identifier. You can change these name to whatever you want.
Product Name: AzureCommunicationVideoCallingSample
Organization Identifier: com.contoso
When press Command + Option + p, preview canvas will display what the app will look like. Hello, world! for now.
Now create a Podfile to include ACS and ANH pods.
platform :ios, '13.0'use_frameworks!target 'AzureCommunicationVideoCallingSample' do pod 'AzureCommunicationCalling', '~> 1.0.0-beta.8' pod 'AzureNotificationHubs-iOS'end
Install pods by ‘pod install’ command
Initial configuration
Now open the project as workspace.
Let’s create some plist to hold our ACS and ANH credentials.
Create DevSettings.plist, FirstUser.plist, SecondUser.plist and fill in values from above prerequisites section.
In order to read these plist values, we are going to create some helper methods. Create a folder named Helpers and create a file named Constants.swift.
Create another file named PListHelper.swift in same folder.
When you are done with above tasks, your folder structure will look like below.
Open AzureCommunicationVideoCallingSampleApp.swift and modify as below. We will read credentials and connection strings from DevSettings.plist, {First / Second}User.plist and fill in our constants for later use.
Tab navigations
This sample has 2 tabs. Home and Profile.
In order to create a tab navigation, we will create two views and define enum for each tab.
Create Views folder and HomeView.swift and ProfileView.swift inside the folder.
Change “Hello, World!” to tab name accordingly. for ex) “Home” and “Profile”
Next, create a folder named Enum and create Tabs.swift file with below content.
Open ContentView.swift and change as below.
Press Command + Option + P and note that tab bar navigation is applied as below.
Authentication
Create CommunicationUserTokenModel.swift and AuthenticationViewModel.swift as below.
This viewModel will initialize azure communication user token with constants that we defined earlier. You will have to modify this with your own token provider when applying to real project.
We will use getCommunicationUserToken function after implementing CallingViewModel shortly.
ViewModels
UIRepresentables
ACS renders stream with RenderView interface which conforms to UIView. In order to use this interface in SwiftUI, we have to use UIViewRepresentable.
Please visit here for detail about UIViewRepresentable.
Create VideoStreamView.swift file under Models folder and fill it with below content.
We will use this struct to present our local video and remote participants’ videos.
StreamViewModels
Create below files under Models folder.
Above classes will be handling video streams and indicators for mic / video.
Callkit manager
Create CallKitManager.swift under Models folder.
CallKit will give your app a native looking call experiences. Not only that there is one important reason that you have to use this CallKit that if you fail to report a call to CallKit after your app receives push notifications, the system will terminate your app. Read more here.
Configrue NotificationHubs
Create NotificationViewModel.swfit under ViewModels folder.
When connectToHub() function is called, your app will be connected to Azure Notification Hub and ready to receive standard push notifications. But in order to receive notifications for Calls, you will have to register voip type deviceToken for ACS separately. We’ll cover this after we configure ACS connection in next section.
Connect to Azure Communication Services
Create CallingViewModel.swift under ViewModels folder.
This class handles all events related to receiving and starting calls. On init(), we initialize PKPushRegistry with voIP pushType and sets delegate to it self. This enables to receive push notifications in PKPushRegistryDelegate. Once we get push notifications, we have to report incoming call to CallKitManger.reportNewIncomingCall() that we have created earlier.
Add ACS and PushKit delegates into CallingViewModel as below.
Note that didUpdate delegate of pushRegistry stores updated deviceToken to self.voIPToken which we will use with ACS callAgent for pushNotification register in next section.
Register push notification
Now we are done with viewModels and ready to get some call notifications.
Add checkToken() function in ContentView.swift as below and initialize ACS callAgent class. Once callAgent is initialized, connect to notification hub using connectToHub() function in NotificationViewModel.swfit. With MSNotificationHubDelegate, we will receive didSave delegate event from notification hub and we are ready to register our voip token to ACS.
Change didSave function in NotificationViewModel.swift as below.
Look for initPushNotification() function in CallingViewModel.swift and find that this func is finally accessing self.voIPToken that was stored from didUpdate delegate of pushRegistry by calling self.callAgent?.registerPushNotifications(deviceToken: self.voIPToken, completionHandler: ((Error?) -> Void)!)
Well done!! we are ready to get voip call notifications now.
Views
Since we are done with viewModels, let’s tackle some views to actually make and receive a call.
But before we start drawing our views, we have to initialize our viewModels first. Open AzureCommunicationVideoCallingSampleApp.swift and modify the content as below. We will initialize viewModels we have created earlier and set them as environmentObject so that our child views can access them.
Because this sample also covers group call, we will create Grid layout view that will automatically resize each participant’s view size.
Create Grid.swift and GridLayout.swift(adopted from Stanford cs193p GridLayout.swift)
Create StreamView.swift for rendering video streams. This view will display remoteParticipants’s mic and camera status along with video stream and display name.
Create CallView.swift, DirectCall.swift, GroupCall.swift as below.
On direct call, which is 1:1, we are using DirectCallView which has local video and full screen remote video. On the other hand to direct call, we will use GroupCallView for GroupCall which will automatically resize views according to the participants number.
Now let’s change our HomeView.swift as below. It checks whether callAgent is initialized or not and prompt user to sign In if callAgent hasn’t been initialized.
Now we will create a few more views related to profile which include sign in and update profile(user id and tags for notification hub).
Create CommunicationsSettings.swift as below.
On CommunicationsSetting.swift, you can change your own token or displayName and signOut.
Create TagsList.swift and TagRow.swift as below. This is need to manage tags for notification hub.
Create NotificationsSetting.swift as below. NotificationsSetting view is where you can set userId and manage tags to receive related notifications from hub.
Add ProfileCategory enums to Enums folder to navigate between Communications and Notifications settings. Modify Profile.swift as well.
Add SignInView.swift to handle signIn.
Add SheetType.swift enum to Enums folder. This enum will handle whether to show callview or signIn page.
And.. last… the ContentView!!!
Modify ContentView.swift as below.
Make sure the project build and everything looks ok. Run the project on real device. Change resouceName of AzureCommunicationVideoCallingSampleApp to “SecondUser” to deploy onto another device if you have one.
Call each other and see the notification wakes up the device even if it’s on lockscreen or app is terminated.
Thanks for reading this article. Integrating ACS and ANH all together at once was kinda heavy task especially there are not much of sample and documentation around.
Here is my github repository that has all the source code explained above.
Please be aware that this is more like POC and not Production ready as Azure Communication Services is not on GA yet as of Feb 19th 2021.
Hope you enjoyed my article and wish you had successfully run the application and make and receive some calls.
Thank you!