
Share:
)
Abdul ist ein Developer Advocate für Vonage. Er hat einen Hintergrund als iOS-Ingenieur im Bereich Verbraucherprodukte. In seiner Freizeit fährt er gerne Rad, hört Musik und berät diejenigen, die gerade ihre Reise in die Technologiebranche beginnen.
Wie man VoIP-Push-Benachrichtigungen mit iOS Callkit handhabt
Lesedauer: 7 Minuten
In diesem Lernprogramm werden Sie CallKit verwenden, um die VoIP-Push-Benachrichtigungen zu verarbeiten, die an ein iOS-Gerät gesendet werden, wenn Sie das Vonage Client SDK für iOS. VoIP-Push-Benachrichtigungen sind die primäre Methode für den Empfang eingehender Anrufe mit dem Client SDK, da sie das Gerät unabhängig davon erreichen, ob sich Ihre Anwendung im Vordergrund befindet oder nicht. Mit CallKit können Sie Ihre iOS-Anwendung in das System integrieren, so dass Ihre Anwendung wie ein nativer iOS-Telefonanruf aussehen kann. Die Verwendung des Vonage Client SDK mit CallKit ermöglicht es Ihnen, Anrufe in Ihre Anwendung zu integrieren und gleichzeitig eine konsistente und vertraute Schnittstelle für eingehende Anrufe zu haben.
Voraussetzungen
Ein Vonage API-Konto. Wenn Sie noch kein Konto haben, können Sie sich heute anmelden.
Ein Apple Developer-Konto und ein iOS-Gerät (oder einen Simulator in Xcode 14 und höher).
Ein GitHub-Konto.
Xcode 12 und Swift 5 oder höher.
Cocoapods um das Vonage Client SDK für iOS zu installieren.
Unser Command Line Interface. Sie können es installieren mit
npm install -g @vonage/cli
.
Das Starter-Projekt
Dieser Blog baut auf dem Blog "Entgegennahme eines Telefonanrufs in der App" aus dem Vonage-Entwicklerportal auf. Dieses Tutorial beginnt mit dem fertigen Zustand des Tutorial-Projekts. Wenn Sie bereits mit der Erstellung einer Vonage Client SDK Sprachanwendung vertraut sind, können Sie das Starter Projekt von GitHub klonen.
Push-Zertifikate einrichten
Es gibt zwei Arten von Push-Benachrichtigungen, die Sie in einer iOS-App verwenden können: VoIP-Pushes mit PushKit oder User Notifications. Dieses Tutorial konzentriert sich auf VoIP-Push-Benachrichtigungen. Der Apple Push-Benachrichtigungsdienst (APNs) verwendet zertifikatsbasierte Authentifizierung, um die Verbindungen zwischen APNs und Vonage-Servern zu sichern. Sie müssen also ein Zertifikat erstellen und es auf die Vonage-Server hochladen, damit Vonage bei einem eingehenden Anruf einen Push an das Gerät senden kann.
Hinzufügen einer Push-Benachrichtigungsfunktion
Um Push-Benachrichtigungen verwenden zu können, müssen Sie die Push-Benachrichtigungsfunktion zu Ihrem Xcode-Projekt hinzufügen. Vergewissern Sie sich, dass Sie in Ihrem Apple-Entwicklerkonto in Xcode über die Einstellungen angemeldet sind. Wenn ja, wählen Sie Ihr Ziel aus und wählen Sie dann Signierung & Fähigkeiten:
signing and capabilities tag
Wählen Sie dann Fähigkeit hinzufügen und fügen Sie die Push-Benachrichtigungen und Hintergrund-Modi Fähigkeiten hinzu:
add capability button
Wählen Sie unter der Funktion Hintergrundmodi Sprache über IP und Hintergrundverarbeitung. Wenn Xcode die Signierung Ihrer Anwendung automatisch verwaltet, wird das mit Ihrem Bundle Identifier verknüpfte Bereitstellungsprofil aktualisiert, um die Fähigkeiten einzubeziehen.
Wenn Sie VoIP-Push-Benachrichtigungen verwenden, müssen Sie das CallKit-Framework einsetzen. Verknüpfen Sie es mit Ihrem Projekt, indem Sie es unter Frameworks, Bibliotheken und eingebettete Inhalte unter Allgemein:
add callkit framework
Erzeugen eines Push-Zertifikats
Um ein Push-Zertifikat zu erstellen, müssen Sie sich in Ihrem Apple-Entwickler-Account anmelden und die Seite Zertifikate, Kennungen und Profile gehen und ein neues Zertifikat hinzufügen:
add certificate button
Wählen Sie Apple Push-Benachrichtigungsdienst SSL (Sandbox & Produktion) und fahren Sie fort.
Certificate wizard checkbox
Sie müssen nun die App-ID für die App auswählen, der Sie VoIP-Push-Benachrichtigungen hinzufügen möchten, und fortfahren. Wenn Ihre App nicht aufgeführt ist, müssen Sie eine App-ID erstellen. Xcode kann dies für Sie tun, wenn es Ihre Signierung automatisch verwaltet. Andernfalls können Sie eine neue App-ID auf der Seite Zertifikate, Bezeichner & Profile Seite unter Bezeichner erstellen. Stellen Sie dabei sicher, dass Sie die Push-Benachrichtigungsfunktion auswählen.
Sie werden aufgefordert, eine Certificate Signing Request (CSR) hochzuladen. Sie können die Anweisungen auf der Hilfe-Website von Apple folgen, um eine CSR auf Ihrem Mac zu erstellen. Sobald die CSR hochgeladen ist, können Sie das Zertifikat herunterladen. Doppelklicken Sie auf die .cer
Datei, um es im Schlüsselbund zu installieren.
Um das Push-Zertifikat in dem Format zu erhalten, das von den Vonage-Servern benötigt wird, müssen Sie es exportieren. Suchen Sie Ihr VoIP-Services-Zertifikat in Keychain Access und exportieren Sie es mit der rechten Maustaste. Benennen Sie den Export applecert
und wählen Sie .p12
als Format:
keychain access export
Ihr Push-Zertifikat hochladen
Sie laden hoch. Ihr Zertifikat zu Vonage über das API-Dashboard. Öffnen Sie Ihre Anwendung auf dem Dashboardund öffnen Sie dann die Option "Enable Push Benachrichtigungen" Registerkarte:
Push Upload on the dashboard
Sie können Ihr Zertifikat hochladen .p12
aus dem vorherigen Schritt hochladen und bei Bedarf ein Passwort hinzufügen.
Die Klasse ClientManager
Erstellen Sie eine neue Swift-Datei (CMD + N
) und rufen Sie diese auf ClientManager
. Diese Klasse wird den Code kapseln, der für die Schnittstelle zum Client-SDK benötigt wird, da Sie in zukünftigen Schritten an mehreren Stellen Informationen vom Client-SDK abrufen müssen:
Ersetzen Sie ALICE_JWT
durch das JWT, das Sie zuvor generiert haben. In einer Produktionsumgebung würden Sie hier ein JWT von Ihrem Authentifizierungsserver/Endpunkt abrufen.
Mit dieser neuen Klasse müssen Sie den Aufruf des Client-SDK-Codes von der ViewController
Klasse in die ClientManager
Klasse verschieben. Die beiden Klassen kommunizieren mit den ClientManagerDelegate
Beobachtern. Nehmen Sie die folgenden Änderungen an Ihrer ViewController
Klasse vor:
class ViewController: UIViewController {
private let connectionStatusLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
ClientManager.shared.delegate = self
connectionStatusLabel.text = ""
connectionStatusLabel.textAlignment = .center
connectionStatusLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(connectionStatusLabel)
view.addConstraints([
connectionStatusLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
connectionStatusLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
}
}
extension ViewController: ClientManagerDelegate {
func clientStatusUpdated(_ clientManager: ClientManager, status: String) {
DispatchQueue.main.async {
self.connectionStatusLabel.text = status
}
}
}
Für Push-Benachrichtigungen registrieren
Der nächste Schritt besteht darin, ein Gerät für Push-Benachrichtigungen zu registrieren, damit Vonage weiß, an welches Gerät die Push-Benachrichtigung für welchen Benutzer gesendet werden soll. In der ClientManager
Klasse fügen Sie die pushToken
Eigenschaft und die folgenden Funktionen, um das Push-Token des Geräts zu verarbeiten:
final class ClientManager: NSObject {
public var pushToken: Data?
weak var delegate: ClientManagerDelegate?
...
func invalidatePushToken(_ completion: (() -> Void)? = nil) {
print("VPush: Invalidate token")
if let deviceId = UserDefaults.standard.object(forKey: Constants.deviceId) as? String {
client.unregisterDeviceTokens(byDeviceId: deviceId) { error in
if error == nil {
self.pushToken = nil
UserDefaults.standard.removeObject(forKey: Constants.pushToken)
UserDefaults.standard.removeObject(forKey: Constants.deviceId)
completion?()
}
}
} else {
completion?()
}
}
private func registerPushIfNeeded(with token: Data) {
shouldRegisterToken(with: token) { shouldRegister in
if shouldRegister {
self.client.registerVoipToken(token, isSandbox: true) { error, deviceId in
if error == nil {
print("VPush: push token registered")
UserDefaults.standard.setValue(token, forKey: Constants.pushToken)
UserDefaults.standard.setValue(deviceId, forKey: Constants.deviceId)
} else {
print("VPush: registration error: \(String(describing: error))")
return
}
}
}
}
}
private func shouldRegisterToken(with token: Data, completion: @escaping (Bool) -> Void) {
let storedToken = UserDefaults.standard.object(forKey: Constants.pushToken) as? Data
if let storedToken = storedToken, storedToken == token {
completion(false)
return
}
invalidatePushToken {
completion(true)
}
}
Die Funktion registerPushIfNeeded
Funktion nimmt ein Token und verwendet dann die shouldRegisterToken
Funktion, um zu prüfen, ob das Token bereits registriert wurde. Wenn dies nicht der Fall ist, registerVoipToken
auf dem Client die Push-Benachrichtigung bei Vonage registrieren. In der AppDelegate
Klasse können Sie sich nun für VoIP-Push-Benachrichtigungen registrieren. Importieren Sie PushKit
am Anfang der Datei:
import PushKit
Fügen Sie eine lokale Instanz der ClientManager
Klasse:
class AppDelegate: UIResponder, UIApplicationDelegate {
...
private let clientManager = ClientManager.shared
...
}
Erstellen Sie eine neue Erweiterung am Ende der Datei, die eine Funktion zur Registrierung des Geräts für Push-Benachrichtigungen enthält:
extension AppDelegate: PKPushRegistryDelegate {
func registerForVoIPPushes() {
let voipRegistry = PKPushRegistry(queue: nil)
voipRegistry.delegate = self
voipRegistry.desiredPushTypes = [PKPushType.voIP]
}
}
Aktualisieren Sie die didFinishLaunchingWithOptions
Funktion zum Aufruf der registerForVoIPPushes
Funktion aufzurufen und sich beim Client SDK anzumelden:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
AVAudioSession.sharedInstance().requestRecordPermission { (granted:Bool) in
print("Allow microphone use. Response: \(granted)")
}
registerForVoIPPushes()
clientManager.login()
return true
}
Fügen Sie die PKPushRegistryDelegate
Funktionen für die Registrierung von Push-Benachrichtigungen in die Erweiterung ein:
extension AppDelegate: PKPushRegistryDelegate {
...
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
clientManager.pushToken = pushCredentials.token
}
func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
clientManager.invalidatePushToken(nil)
}
}
Das Push-Token wird als Eigenschaft der Klasse ClientManager
Klasse gespeichert, da Sie das Token nur dann bei Vonage registrieren wollen, wenn der Kunde angemeldet ist, bearbeiten Sie also die login
Funktion in der ClientManager
Klasse, um dies zu handhaben:
func login(isPushLogin: Bool = false) {
print("VPush: Login - isPush:", isPushLogin)
guard !isActiveCall else { return }
ongoingPushLogin = isPushLogin
getJWT { jwt in
self.client.createSession(jwt) { error, sessionID in
let statusText: String
if error == nil {
statusText = "Connected"
if isPushLogin {
self.handlePushLogin()
} else {
self.handleLogin()
}
} else {
statusText = error!.localizedDescription
}
self.delegate?.clientStatusUpdated(self, status: statusText)
}
}
}
private func handlePushLogin() {
ongoingPushLogin = false
if let storedAction = storedAction {
storedAction()
}
}
private func handleLogin() {
if let token = pushToken {
registerPushIfNeeded(with: token)
}
}
Eingehende Push-Benachrichtigungen verarbeiten
Wenn das Gerät registriert ist, kann es nun Push-Benachrichtigungen von Vonage empfangen. Das Client SDK verfügt über Funktionen zur Überprüfung, ob eine Push-Benachrichtigung die erwartete Nutzlast ist, und zur Verarbeitung der Nutzlast. Wenn processCallInvitePushData
aufgerufen wird, wandelt es die Nutzlast in einen Aufruf um, der auf der didReceiveInviteForCall
Funktion des VGVoiceClientDelegate
.
Wie bei der Registrierung eines Push-Tokens wollen Sie einen eingehenden Push nur dann verarbeiten, wenn das Client SDK angemeldet wurde. Implementieren Sie die Funktionen der ClientManager
Klasse zusammen mit einer lokalen Variable, um einen eingehenden Push zu speichern:
final class ClientManager: NSObject {
...
private var ongoingPushLogin = false
private var ongoingPushKitCompletion: () -> Void = { }
private var storedAction: (() -> Void)?
...
func isVonagePush(with userInfo: [AnyHashable : Any]) -> Bool {
VGVoiceClient.vonagePushType(userInfo) == .unknown ? false : true
}
func processPushPayload(with payload: [AnyHashable : Any], pushKitCompletion: @escaping () -> Void) -> String? {
self.ongoingPushKitCompletion = pushKitCompletion
return client.processCallInvitePushData(payload)
}
...
}
Die PKPushRegistryDelegate
hat eine Funktion, die aufgerufen wird, wenn ein Push eingeht, der didReceiveIncomingPushWith
zur Erweiterung hinzufügen PKPushRegistryDelegate
in der AppDelegate.swift
Datei:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
if clientManager.isVonagePush(with: payload.dictionaryPayload) {
clientManager.login(isPushLogin: true)
_ = clientManager.processPushPayload(with: payload.dictionaryPayload, pushKitCompletion: completion)
}
}
Es wird empfohlen, eine Anmeldung durchzuführen, wenn Sie eine eingehende VoIP-Push-Benachrichtigung haben, weshalb login
hier aufgerufen wird. Dies verwendet die Logik in der ClientManager
Klasse, die Informationen über einen Push speichert, der nach der Anmeldung verwendet werden soll. Die Logik wird zu einem späteren Zeitpunkt implementiert.
Wenn Ihre iOS-Anwendung eine eingehende VoIP-Push-Benachrichtigung erhält, müssen Sie diese mit der CXProvider
Klasse im CallKit-Framework behandeln. Erstellen Sie eine neue Swift-Datei (CMD + N) mit dem Namen ProviderDelegate
:
import CallKit
import AVFoundation
import VonageClientSDKVoice
final class ProviderDelegate: NSObject {
private let provider: CXProvider
private let callController = CXCallController()
private var activeCall: UUID? = nil
override init() {
provider = CXProvider(configuration: ProviderDelegate.providerConfiguration)
super.init()
provider.setDelegate(self, queue: nil)
}
static var providerConfiguration: CXProviderConfiguration = {
let providerConfiguration = CXProviderConfiguration()
providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.supportedHandleTypes = [.generic, .phoneNumber]
return providerConfiguration
}()
}
Die callController
Eigenschaft ist ein CXCallController
Objekt, das von der Klasse verwendet wird, um Benutzeraktionen auf der CallKit-Benutzeroberfläche zu verarbeiten. Als nächstes erstellen Sie eine Erweiterung am Ende der Datei, um die CXProviderDelegate
:
extension ProviderDelegate: CXProviderDelegate {
func providerDidReset(_ provider: CXProvider) {
activeCall = nil
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
ClientManager.shared.answer(activeCall!.uuidString.lowercased()) { error in
if error == nil {
action.fulfill()
} else {
action.fail()
}
}
}
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
hangup(action: action)
}
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
VGVoiceClient.enableAudio(audioSession)
}
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
VGVoiceClient.disableAudio(audioSession)
}
}
Wenn CallKit die Audiositzung aktiviert und deaktiviert, aktivieren und deaktivieren die Delegatenfunktionen das Client-SDK-Audio über die CallKit-Audiositzung.
Wenn die CallKit-Benutzeroberfläche den Anruf beantwortet, ruft sie die CXAnswerCallAction
Delegaten-Funktion auf. Diese ruft die answer
Funktion auf, die Sie in einem späteren Schritt in der ClientManager
in einem späteren Schritt implementieren werden.
CXEndCallAction
wird aufgerufen, wenn der Anruf von der CallKit UI beendet wird, die die hangup
Funktion aufruft, die Sie als nächstes implementieren werden. Implementieren Sie den Rest der benötigten Funktionen in der ProviderDelegate
Klasse:
final class ProviderDelegate: NSObject {
...
func reportCall(_ callID: String, caller: String, completion: @escaping () -> Void) {
activeCall = UUID(uuidString: callID)
let update = CXCallUpdate()
update.localizedCallerName = caller
provider.reportNewIncomingCall(with: activeCall!, update: update) { error in
if error == nil {
completion()
}
}
}
func didReceiveHangup(_ callID: String) {
let uuid = UUID(uuidString: callID)!
provider.reportCall(with: uuid, endedAt: Date.now, reason: .remoteEnded)
}
func reportFailedCall(_ callID: String) {
let uuid = UUID(uuidString: callID)!
provider.reportCall(with: uuid, endedAt: Date.now, reason: .failed)
}
private func hangup(action: CXEndCallAction) {
if activeCall == nil {
endCallTransaction(action: action)
} else {
ClientManager.shared.reject(activeCall!.uuidString.lowercased()) { error in
if error == nil {
self.endCallTransaction(action: action)
}
}
}
}
private func endCallTransaction(action: CXEndCallAction) {
self.callController.request(CXTransaction(action: action)) { error in
if error == nil {
self.activeCall = nil
action.fulfill()
} else {
action.fail()
}
}
}
}
Die Funktion reportCall
Funktion ruft reportNewIncomingCall
die die CallKit-System-UI auslöst, die anderen Funktionen dienen entweder der Aktualisierung oder dem Beenden von Aufrufen. Jetzt, wo die ProviderDelegate
Klasse vollständig ist, können Sie die ClientManager
Klasse aktualisieren, um sie zu verwenden. Fügen Sie die providerDelegate
Eigenschaft zum Client Manager hinzu:
final class ClientManager: NSObject {
...
private let providerDelegate = ProviderDelegate()
...
}
Dann implementieren Sie die VGVoiceClientDelegate
:
extension ClientManager: VGVoiceClientDelegate {
func voiceClient(_ client: VGVoiceClient, didReceiveInviteForCall callId: VGCallId, from caller: String, with type: VGVoiceChannelType) {
print("VPush: Received invite", callId)
providerDelegate.reportCall(callId, caller: caller, completion: ongoingPushKitCompletion)
}
func voiceClient(_ client: VGVoiceClient, didReceiveHangupForCall callId: VGCallId, withQuality callQuality: VGRTCQuality, reason: VGHangupReason) {
print("VPush: Received hangup")
isActiveCall = false
providerDelegate.didReceiveHangup(callId)
}
func voiceClient(_ client: VGVoiceClient, didReceiveInviteCancelForCall callId: String, with reason: VGVoiceInviteCancelReason) {
print("VPush: Received invite cancel")
providerDelegate.reportFailedCall(callId)
}
func client(_ client: VGBaseClient, didReceiveSessionErrorWith reason: VGSessionErrorReason) {
let reasonString: String!
switch reason {
case .tokenExpired:
reasonString = "Expired Token"
case .pingTimeout, .transportClosed:
reasonString = "Network Error"
default:
reasonString = "Unknown"
}
delegate?.clientStatusUpdated(self, status: reasonString)
}
}
Nachdem das SDK den Aufruf verarbeitet hat, erhalten Sie eine Aufrufaufforderung für die didReceiveInviteForCall
Delegatenfunktion, die ihrerseits den Anruf meldet. didReceiveHangupForCall
und didReceiveInviteCancelForCall
rufen ebenfalls ihre jeweiligen Funktionen auf dem Provider-Delegaten auf. Zur Vervollständigung der ClientManager
Klasse zu vervollständigen, fügen Sie die answer
und reject
Funktionen hinzu:
func answer(_ callID: String, completion: @escaping (Error?) -> Void) {
let answerAction = {
print("VPush: Answer", callID)
self.isActiveCall = true
self.client.answer(callID, callback: completion)
}
if ongoingPushLogin {
print("VPush: Storing answer")
storedAction = answerAction
} else {
answerAction()
}
}
func reject(_ callID: String, completion: @escaping (Error?) -> Void) {
let rejectAction = {
print("VPush: Reject", callID)
self.isActiveCall = false
self.client.reject(callID, callback: completion)
}
if ongoingPushLogin {
print("VPush: Storing Reject")
storedAction = rejectAction
} else {
rejectAction()
}
}
Auch hier können Sie einen Anruf nur annehmen oder ablehnen, nachdem Sie sich beim Client SDK angemeldet haben. Beide Funktionen verwenden die ongoingPushLogin
um zu prüfen, ob die Anmeldung erfolgreich abgeschlossen wurde; wenn nicht, wird die Aktion mit storedAction
. Wenn Sie sich die handlePushLogin
Funktion ansehen, können Sie sehen, dass sie nach Abschluss der Anmeldung eine gespeicherte Aktion aufruft, falls eine solche vorhanden ist.
Probieren Sie es aus
Erstellen Sie das Projekt und führen Sie es auf Ihrem iOS-Gerät (oder Simulator in Xcode 14 und höher) aus (CMD + R), akzeptieren Sie die Mikrofonberechtigungen und sperren Sie das Gerät. Rufen Sie dann die Nummer an, die mit Ihrer Vonage-Anwendung von vorhin verknüpft ist. Sie werden den eingehenden Anruf direkt auf Ihrem Sperrbildschirm sehen; sobald Sie ihn annehmen, wird der bekannte iOS-Anrufbildschirm angezeigt:
incoming call with locked screen
active call from locked screen
Wenn Sie die Anrufprotokolle auf dem Gerät überprüfen, wird der Anruf auch dort aufgeführt.
Was kommt als Nächstes?
Sie finden das fertige Projekt auf GitHub. Mit dem Client SDK und CallKit können Sie noch viel mehr tun; Sie können CallKit für ausgehende Anrufe verwenden. Erfahren Sie mehr über das Client SDK auf der Vonage Client SDK Übersicht und CallKit auf developer.apple.com.
Share:
)
Abdul ist ein Developer Advocate für Vonage. Er hat einen Hintergrund als iOS-Ingenieur im Bereich Verbraucherprodukte. In seiner Freizeit fährt er gerne Rad, hört Musik und berät diejenigen, die gerade ihre Reise in die Technologiebranche beginnen.