simple :
My issue is, need to show incoming call when the app is in Terminated state !! ⚠️⚠️
Explained :
I am working in a chat application in Flutter for both iOS and Android. It has features like voice and video calling, webRTC is used to enable this feature and everything works fine.
Now I want to enable a new feature when a call is received it should show as a incoming call notification like WhatsApp. To enable this feature, I used flutter_callkit_incoming. But at some point it didn’t work as I expected. I’m using APN service for iOS to receive notification. So I decided to do with VOIP and CXProvider in the iOS side. Apple’s CloudKit push notification console is used to send VOIP notification. It is also working and showing incoming call when a VOIP notification is received and can do function like accepting, reject and end call.
Now, the issue that I’m facing is, when the app is not in the background or terminated, incoming calling notification is not showing. App is softly waking up for a few seconds (app not opened). Log is printing upto some code. In that time I need to redirect to the app with the data received in VOIP notification or it should show incoming calling notification and on accepting it should open app call screen. I couldn’t do with the current code that I implemented.
How to enable with the current code? or Is there any other way to enable this Feature ?
added this in 'info.plist'
<key>UIBackgroundModes</key>
<array>
<string>voip</string>
<string>audio</string>
<string>fetch</string>
<string>processing</string>
<string>remote-notification</string>
</array>
Flutter side method channel handling
platform.setMethodCallHandler(_handleMethod);
Future<void> _handleMethod(MethodCall call) async {
try {
switch (call.method) {
case "onIncomingCall":
final data = Map<String, dynamic>.from(call.arguments);
CallHandler.handleIncomingCall(data['callerId'], data['callType']);
break;
case "onCallAnswered":
CallHandler.handleCallAnswered();
break;
case "onCallDeclined":
CallHandler.handleCallDeclined();
break;
case "notificationClicked":
final Map<String, dynamic> arguments =
call.arguments.runtimeType == String
? jsonDecode(call.arguments)
: call.arguments;
_navigateToMessageScreen(arguments);
break;
case "notificationAccepted":
print("Notification accepted with data: ${call.arguments}");
break;
case "showIncomingCall":
print("Notification accepted with data: ${call.arguments}");
// VoIPHandler.showIncomingCall('callerId', 'callType');
break;
default:
throw MissingPluginException();
}
print('_handle method 5');
} catch (e, trace) {
print('handl method error :: $e');
print(trace);
}
}
AppDelegate.swift – incoming call
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
if type == .voIP {
let token = pushCredentials.token.map { String(format: "%02x", $0) }.joined()
print("VoIP Token: (token)")
voipTokenString = token
}
}
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
if type == .voIP {
print("Received VoIP Push Payload in terminated state: (payload.dictionaryPayload)")
defaults?.set(payload.dictionaryPayload, forKey: "voipNotificationPayload")
showIncomingCallWithCallKit(callerId: "CallerId", callType: "audio")
completion()
}
}
private func showIncomingCallWithCallKit(callerId: String, callType: String) {
let callUpdate = CXCallUpdate()
callUpdate.remoteHandle = CXHandle(type: .generic, value: callerId)
callUpdate.hasVideo = (callType == "video")
let providerConfiguration = CXProviderConfiguration(localizedName: "Near Chat")
providerConfiguration.supportsVideo = true
providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.supportedHandleTypes = [.generic]
let provider = CXProvider(configuration: providerConfiguration)
provider.setDelegate(self, queue: nil)
provider.reportNewIncomingCall(with: UUID(), update: callUpdate) { error in
if let error = error {
print("Error reporting call: (error.localizedDescription)")
} else {
print("Incoming call successfully reported.")
}
}
}
also note that I’m getting log when app is terminated and VOIP received until
@override
void initState() {
print('splashscreen initstate');
final provider = Provider.of<homePageProvider>(context, listen: false);
Before splash screen, there is only main.dart