Closed melio-matt closed 3 months ago
Did you follow the packages' setup on iOS and enabled correct background modes in info.plist
?
<key>UIBackgroundModes</key>
<array>
<string>processing</string>
<string>remote-notification</string>
<string>voip</string>
</array>
Also it'd be great to share the crash report, thanks!
Those options are in my background modes along with fetch and audio as well.
I've attached the crashlog.
This SO post seems similar to what you are facing: https://stackoverflow.com/questions/71198108/ios-voip-pushkit-app-is-crashing-while-in-background-works-ok-in-foreground
Make sure you're handling incoming calls properly and not using blocking async/await operations before reporting the call through CallKit.
From the plugin's documentation, there is this brief example:
...
// Handle incoming pushes
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
print("didReceiveIncomingPushWith")
guard type == .voIP else { return }
let id = payload.dictionaryPayload["id"] as? String ?? ""
let callerName = payload.dictionaryPayload["callerName"] as? String ?? ""
let userId = payload.dictionaryPayload["callerId"] as? String ?? ""
let handle = payload.dictionaryPayload["handle"] as? String ?? ""
let isVideo = payload.dictionaryPayload["isVideo"] as? Bool ?? false
let data = flutter_callkeep.Data(id: id, callerName: callerName, handle: handle, hasVideo: isVideo)
//set more data
data.extra = ["userId": callerId, "platform": "ios"]
data.appName = "Done"
//data.iconName = ...
//data.....
SwiftCallKeepPlugin.sharedInstance?.displayIncomingCall(data, fromPushKit: true)
}
...
Try to call SwiftCallKeepPlugin.sharedInstance?.displayIncomingCall(data, fromPushKit: true)
early
I took a copy of the pushRegistry method from the documentation as you pasted above and the only changes I've made is to generate the id with a UUID rather than taking from the payload as my server doesn't send a UUID and extracting a few other bits of data from the payload and passing that through in data.extra.
On the Flutter side main method is an async method and has awaits in it but there isn't anything in the AppDelegate on the Swift side.
Here is another similar SO post describing the exact issue and symptoms you are facing: https://stackoverflow.com/questions/60828988/ios-13-reason-killing-app-because-it-never-posted-an-incoming-call-to-the-syst
Could extracting your data or generating the UUID be async in some sort? can you share that part of your code?
This issue is not Flutter related so using async there is no issue, it's that the pushRegistry(_:didReceiveIncomingPushWith:for:completion:)
method was completed before the call is reporting through CallKit
.
Here is my pushRegistery method:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
print("didReceiveIncomingPushWith")
guard type == .voIP else { return }
// just generate a UUID as we don't get sent anything that is one
let id = UUID().uuidString;
let callerName = payload.dictionaryPayload["caller_name"] as? String ?? ""
// let userId = payload.dictionaryPayload["callerId"] as? String ?? ""
let handle = payload.dictionaryPayload["handle"] as? String ?? ""
let isVideo = payload.dictionaryPayload["has_video"] as? Bool ?? false
// extract the call specific data we send in the push
let aor = payload.dictionaryPayload["AOR"] as? String ?? "";
let reason = payload.dictionaryPayload["REASON"] as? String ?? "";
let callId = payload.dictionaryPayload["CALLID"] as? String ?? "";
let callerId = payload.dictionaryPayload["caller_id"] as? String ?? "";
let data = flutter_callkeep.Data(id: id, callerName: callerName, handle: handle, hasVideo: isVideo)
//set more data
data.extra = ["callerId": callerId, "aor": aor, "reason": reason, "callId": callId, "platform": "ios"]
data.appName = "CallStation"
//data.iconName = ...
//data.....
SwiftCallKeepPlugin.sharedInstance?.displayIncomingCall(data, fromPushKit: true)
}
As you can see the UUID is a sync method call and the data extraction is more repetition from the sample code.
Just found an additional log entry. This occurs when when the app is running in the background and not terminated. From the code in my previous post I see the "didReceiveIncomingPushWith" get printed to the console. This is the followed but two copies of the following.
Error Domain=NSOSStatusErrorDomain Code=560557684 "Session activation failed" UserInfo={NSLocalizedDescription=Session activation failed}
The next entry in the console is a print out from the CallEventHandler for onCallIncoming but the call progresses fine and works.
I suppose this might be the error printout from the configureAudioSession function in SwiftCallKeepPlugin.
Think I might checkout a copy of CallKeep so I can alter things and test.
The Session activation error occurs in the configureAudioSession
method that is called twice from the displayIncomingCall
method. It errors on the line:
try session.setActive(data?.audioSessionActive ?? true)
Since it is doing a try it does continue on and the call does cause the phone to ring and I can answer and speak on the call.
Not sure that this has anything to do with it not working when the app is terminated though and starts up when the push message is received.
I've discovered the issue. I had some other code just before I setup flutter_callkeep that was registering with PushKit. In a non-terminated scenario this was fine both could register and their callbacks were called. However in a terminated state on the first one got the push token and it did not start CallKit. Sorry for this, it does now appear to be working.
No problem, glad you got it working!
When the app has been terminated either by the user from the app switcher or by the OS itself (normally power saving or memory) and then call comes in the VoIP push notification is sent. This results in the phone itself displaying a crash dialog that is then reported to TestFlight. Inspection of the crash indicates that the push notification has not been handled.
If my app is running, whether in the foreground or the background (not terminated), it all works and CallKit is called, the native interface for answering a call is displayed. The call can be answered in the normal way.
From the crash report it says the crash occurred in Thread 0 and it sort of looks like it tried to start the app. Is this something that works but there is something I should be aware of that I either should or should not do?
thanks Matthew