how to integrate Callkit with Agora VOiP in swift 4 iOS? - iphone

I want to integrate apple Callkit with Agora VOiP in swift 4 iOS.
Please give any suggestions How can I do that.

To integrate voip, you will have to use both, callKit and PushKit.
CallKit will be used to show native call screen and handlers during in call transition while Pushkit will be used to invoke app, when app is killed.
Its easy to integrate:-
Enable background modes in info.plist and check option "App provides Voice over IP services".
Import Callkit in the viewcontroller viewdidload/ any init method of anyclass you would use to Implement CXProviderDelegate functions. Through this you will configure call objects, when to report in comming call, accept actions, reject action etc.
Implement the following functions:
func providerDidReset(_ provider: CXProvider) {
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
action.fulfill()
}
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
action.fulfill()
}
Now import Pushkit and implement PKPushRegistryDelegate functions.
a.)Configure pushkit like this
let registry = PKPushRegistry(queue: nil)
registry.delegate = self
registry.desiredPushTypes = [PKPushType.voIP]
b.) implement pushkit token function. You may have to update to server for delivering voip push notifications
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
print(pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined())
}
c. Now when you receive incomming notification, implement this fuction
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: #escaping () -> Void) {
let config = CXProviderConfiguration(localizedName: "App name")
config.iconTemplateImageData = UIImagePNGRepresentation(UIImage(named: "any image name")!)
config.supportsVideo = true;
let provider = CXProvider(configuration: config)
provider.setDelegate(self, queue: nil)
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .generic, value: "Caller name")
update.hasVideo = true
provider.reportNewIncomingCall(with: UUID(), update: update, completion: { error in })
}
Go to developer portal and generate VoIP Services certificate and install it.
Enable push notifications under Capabilities.
This was a basic code over view. You will have to add cases to simulating incomming call and other customizations. I hope this will help you to move further.

Related

ATTrackingManager stopped working in iOS 15

ATTrackingManager.requestTrackingAuthorization stopped working on ios 15. Application rejected from Apple.
According to the discussion in Apple Developer Forum, you need to add delay for about one second when calling requestTrackingAuthorization.
https://developer.apple.com/forums/thread/690607
Example:
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in
// Tracking authorization completed. Start loading ads here.
// loadAd()
})
})
P.S.
Also if you have requesting push notification permission, firstly you need request push notification then request tracking authorization with a delay =>
private func requestPushNotificationPermission() {
let center = UNUserNotificationCenter.current()
UNUserNotificationCenter.current().delegate = self
center.requestAuthorization(options: [.sound, .alert, .badge], completionHandler: { (granted, error) in
if #available(iOS 14.0, *) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in
// Tracking authorization completed. Start loading ads here.
// loadAd()
})
})
}})
UIApplication.shared.registerForRemoteNotifications()
}
The problem has been solved, just call it in applicationDidBecomeActive:
https://developer.apple.com/forums/thread/690762
Follow by apple doc:
Calls to the API only prompt when the application state is UIApplicationStateActive.
So, we need to call ATTrackingManager.requestTrackingAuthorization on
applicationDidBecomeActive of AppDelegate.
But If you're using scenes (see Scenes), UIKit will not call this method. Use sceneDidBecomeActive(_:) instead to restart any tasks or refresh your app’s user interface. UIKit posts a didBecomeActiveNotification regardless of whether your app uses scenes.
So, my approach is to register on addObserver on didFinishLaunchingWithOptions such as:
NotificationCenter.default.addObserver(self, selector: #selector(handleRequestEvent), name: UIApplication.didBecomeActiveNotification, object: nil)
on handleRequestEvent:
requestPermission() // func call ATTrackingManager.requestTrackingAuthorization NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)
Hope this helps. It's work for me.
Make sure your iPhone's Settings -> Privacy -> Tracking is enabled. Otherwise, it won't prompt for request Aurthorization.

Urban Airship receivedBackgroundNotification never called

After reading all the guides, and after checking hundred of articles on the internet, I'm quite sure that the method receivedBackgroundNotification is never called.
Everything works perfect, but when the app is in background, a notification is shown and this method never is called. Seems to be impossible to get it working.
Assuming all the normal operations and the basic configuration is well done and is working, what can I do to intercept and manage background push notifications with this library?
I will appreciate a lot any help.
Make sure you have the following configured:
Remote notifications background mode enabled in the target's capabilities
Background app refreshed is enabled on your test device
Assuming you are trying to use the UAPushNotificationDelegate, make sure you either have automatic setup enabled or you are forwarding all the proper methods to UA SDK.
Apple will only wake up your application if you send the push notification with content-available=1 in the payload. The option is exposed in the composer as "background processing" or you can set it in the iOS overrides when using the push api.
In Urban Airship iOS SDK v.13.4.0 if func receivedBackgroundNotification(_ notificationContent: UANotificationContent, completionHandler: #escaping (UIBackgroundFetchResult) -> Void) is not called you can handle notifications in func receivedNotificationResponse(_ notificationResponse: UANotificationResponse, completionHandler: #escaping () -> Void) if you display notifications as alerts. Remember that you should only handle the notifications with actionIdentifier == UNNotificationDefaultActionIdentifier (the user opened the app from the notification interface).
This is an example of UAPushNotificationDelegate implementation:
extension MyPushNotificationDelegate: UAPushNotificationDelegate {
public func receivedForegroundNotification(_ notificationContent: UANotificationContent, completionHandler: #escaping () -> Void) {
completionHandler()
}
public func receivedBackgroundNotification(_ notificationContent: UANotificationContent, completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
completionHandler(.newData)
}
public func receivedNotificationResponse(_ notificationResponse: UANotificationResponse, completionHandler: #escaping () -> Void) {
guard notificationResponse.actionIdentifier == UNNotificationDefaultActionIdentifier else {
completionHandler()
return
}
someFunc() {
completionHandler()
}
}
public func extend(_ options: UNNotificationPresentationOptions = [], notification: UNNotification) -> UNNotificationPresentationOptions {
[.alert, .badge, .sound]
}
}

CallKit - How to bring the CXCallController to the front

I'm creating a VOIP App.
Most of the logic is similar to the Raywenderlich Tutorial.
My issue is that after accepting the call, the CXCallController is not on top of the App, but next to it in the "task manager":
The problem with that is, that once you have accepted the call, the CXCallController is not the top most controller, but the actual App.
So you can't tell that the call is taking place.
And to perform actions regarding the call - for example hang up, hold, mute you would have to go through the task manager to open the CXCallController. Not very user friendly.
How do I bring the CXCallController to the front after the user has accepted the call?
Provider delegate called after the user has answered the call:
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
guard let call = callManager.getCall(with: action.callUUID) else {
action.fail()
return
}
configureAudioSession()
self.incommingCall = call
action.fulfill()
}
Callback after the audio session has been created:
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
incommingCall?.answerCall(with: audioSession) { success in
if success {
self.incommingCall?.startAudio()
}
}
}
Start call logic using TokBox VOIP Service:
func answerCall(with audioSession: AVAudioSession, completion: ((_ success: Bool) -> Void)?) {
OTAudioDeviceManager.setAudioDevice(OTDefaultAudioDevice.sharedInstance(with: audioSession))
if session == nil {
session = OTSession(apiKey: self.apiKey, sessionId: self.sessionID, delegate: self)
}
answerCallCompletion = completion
var error: OTError?
hasStartedConnecting = true
session?.connect(withToken: self.token, error: &error)
if error != nil {
CoreServices.catchError(error: error, at: #function)
}
}
The call itself works fine. Two parties are able to communicate, start and end calls. The only issue is the described CXCallController behavior.
Is this expected behavior? Or how do I bring the CXCallController to the front?
Help is very appreciated.
This is the expected behaviour of the callkit. Callkit allows us to answer or decline the call when the app is receiving the incoming call. After answering the call, the app has to manage all the actions.
when the phone is locked, receive the incoming call, CXCallController will be visible, after answering the call, still you can see the CXCallController in the lock mode. After unlocking the phone, CXCallController will be invisible, the app has to manage the hangup, mute or hold options.
when the phone is not locked, receive the incoming call, CXCallController will be visible, after answering the call, CXCallController will be invisible, the app has to manage the hangup, mute or hold options.

How to receive PUSH Notifications properly?

I am writing VoIP application using Xcode 9.2, Swift 4.0, minimum iOS version is 10.3. I want to receive incoming call even iPhone is in sleeping mode (just like WhatsApp or Viber). I already know that I must use CallKit for it. But I do not know how to receive PUSH Notifications correctly. I mean, I have two ways how to receive them. First is in AppDelegate.swift, please, look (this method is working):
import UIKit
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Getting permissions for Notifications
//222
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
}
application.registerForRemoteNotifications()
//222
return true
}
//For PUSH Notifications settings
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("NOTIFICATION ERROR: \(error)")
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let pushToken = deviceToken.reduce("", {$0 + String(format: "%02X", $1)}).lowercased()
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
// Receiving call?
}
}
But, no matter, what I am doing, it does not trigger incoming call if iPhone is sleeping. Lately I saw that there is another method to receive PUSH Notifications. Please, look:
import UIKit
import CallKit
import PushKit
class ViewController: UIViewController, CXProviderDelegate, PKPushRegistryDelegate {
override func viewDidLoad() {
let registry = PKPushRegistry(queue: nil)
registry.delegate = self
registry.desiredPushTypes = [PKPushType.voIP]
}
func providerDidReset(_ provider: CXProvider) {
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
action.fulfill()
}
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
action.fulfill()
}
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
print(pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined())
}
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: #escaping () -> Void) {
// Receive call?
}
}
The problem is that in first method I have this PUSH token: 9bcf73ac3d6a68be07c26d8b6a972f5b716cd5308f1a19048b62d19b9b8d4bd1
Second method returns me this PUSH token:
1e40fafb2963161b1bcf1bdde00d625a879c6934e19e2fb32f62b1c9272e956f
And they are not equal! How can this be? So, main question is: how to receive PUSH Notifications and what is difference between AppDelegate and PushKit methods and which token is correct?
Thank you for any help!
There are two types of Notifications (in fact more, but, in this case, just two): PUSH Notifications and VoIP Notifications. They have different tokens and different system of receiving. VoIP services exists only for calls (Apple CallKit), opposite PUSH service exists for messages and other notifications. So, in fact, both of this methods are necessary and one does not replaces another. And both of this methods requiring different Certificates in Apple Developer Account: Apple Push Notification service SSL for PUSH Notifications and VoIP Services Certificate for VoIP.
Thank you everyone for help! Be free to edit this if you see a error.
First of all remove this line application.registerForRemoteNotifications()
Now comes to puskit below method will have right token
func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, forType type: PKPushType) {
print("\(#function) voip token: \(credentials.token)")
let deviceToken = credentials.token.reduce("", {$0 + String(format: "%02X", $1) })
print("\(#function) token is: \(deviceToken)")}
and Push-kit framework does not have UI like remote or local notification you need to trigger local notification every time when below method called
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: #escaping () -> Void) {
// Receive call?
}

get VOIP Token on ios10

I can't have the voip token with this function
func registerVoipNotifications() {
let mainQueue = dispatch_get_main_queue()
let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
voipRegistry.delegate = self
voipRegistry.desiredPushTypes = [PKPushTypeVoIP]
}
and
func pushRegistry(registry: PKPushRegistry,
didUpdatePushCredentials credentials: PKPushCredentials,
forType type: String) {
//print out the VoIP token. We will use this to test the nofications.
let voipToken = credentials.token
self.getSipSetting("\(voipToken)")
}
but i can't enter in 'didUpdatePushCredentials'.
With ios9, i don't have this problem.
Do you have an idea ?
Have you enabled the Push notifications in your project Capabilities?
have you added the SSL certificates (Apple Push Notification service SSL Certificates) in your app id configuration?