// ios/CallKitManager.swift
import CallKit
import PushKit
@objc(CallKitManager)
class CallKitManager: NSObject, CXProviderDelegate, PKPushRegistryDelegate {
static let shared = CallKitManager()
private let provider: CXProvider
private let callController = CXCallController()
private var voipRegistry: PKPushRegistry?
override init() {
let config = CXProviderConfiguration()
config.supportsVideo = true
config.maximumCallsPerCallGroup = 1
config.supportedHandleTypes = [.generic]
provider = CXProvider(configuration: config)
super.init()
provider.setDelegate(self, queue: nil)
}
@objc func registerForVoIPPushes() {
voipRegistry = PKPushRegistry(queue: .main)
voipRegistry?.delegate = self
voipRegistry?.desiredPushTypes = [.voIP]
}
// PKPushRegistryDelegate
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
let token = pushCredentials.token.map { String(format: "%02x", $0) }.joined()
// Send token to CometChat
NotificationCenter.default.post(
name: NSNotification.Name("VoIPTokenReceived"),
object: nil,
userInfo: ["token": token]
)
}
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
guard type == .voIP else { return }
let callerId = payload.dictionaryPayload["callerId"] as? String ?? "Unknown"
let callerName = payload.dictionaryPayload["callerName"] as? String ?? "Unknown"
let sessionId = payload.dictionaryPayload["sessionId"] as? String ?? ""
let hasVideo = payload.dictionaryPayload["hasVideo"] as? Bool ?? false
reportIncomingCall(
uuid: UUID(),
handle: callerId,
callerName: callerName,
hasVideo: hasVideo
) { error in
completion()
}
}
func reportIncomingCall(uuid: UUID, handle: String, callerName: String, hasVideo: Bool, completion: @escaping (Error?) -> Void) {
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .generic, value: handle)
update.localizedCallerName = callerName
update.hasVideo = hasVideo
provider.reportNewIncomingCall(with: uuid, update: update) { error in
completion(error)
}
}
// CXProviderDelegate
func providerDidReset(_ provider: CXProvider) {}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
// Notify React Native to accept the call
NotificationCenter.default.post(
name: NSNotification.Name("CallKitAnswerCall"),
object: nil,
userInfo: ["callUUID": action.callUUID.uuidString]
)
action.fulfill()
}
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
// Notify React Native to end the call
NotificationCenter.default.post(
name: NSNotification.Name("CallKitEndCall"),
object: nil,
userInfo: ["callUUID": action.callUUID.uuidString]
)
action.fulfill()
}
}