From fafc4809041c9e75d7d6b36f3ec542ef7a0a4c2a Mon Sep 17 00:00:00 2001 From: sravan Date: Sat, 1 Aug 2026 13:09:25 +0530 Subject: [PATCH] Fix crash after multiple calls: always reportNewIncomingCall for every VoIP push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iOS 13+ terminates the app if a PushKit VoIP push doesn't call reportNewIncomingCall before completion(). My earlier "no re-ring blip" change made the cancel handler SKIP the report for already-known/ended calls — which is exactly what iOS kills the app for, surfacing as a crash after several calls (a cancel push for a prior call's UUID). Revert to ALWAYS report then immediately end: for a still-active/ringing UUID the report errors harmlessly (no second ring) and reportCall ends it; only a late duplicate cancel shows a brief, unavoidable blip. A crash is far worse than a blip. Co-Authored-By: Claude Opus 4.8 --- .../NativeCallPlugin/NativeCallPlugin.swift | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/mobile/plugins/native-call/ios/Sources/NativeCallPlugin/NativeCallPlugin.swift b/mobile/plugins/native-call/ios/Sources/NativeCallPlugin/NativeCallPlugin.swift index 6961d8a..49c4a9d 100644 --- a/mobile/plugins/native-call/ios/Sources/NativeCallPlugin/NativeCallPlugin.swift +++ b/mobile/plugins/native-call/ios/Sources/NativeCallPlugin/NativeCallPlugin.swift @@ -211,27 +211,21 @@ public class NativeCallPlugin: CAPPlugin, CAPBridgedPlugin, PKPushRegistryDelega // reporting a NEW incoming call is what caused the "rings back for a second" blip on a call we've // already handled. So: if (dict["type"] as? String) == "cancel" { - // (a) already ended here → nothing to do (re-reporting would blip). - if endedCalls.contains(uuid) { completion(); return } - // (b) a call we already know (still ringing OR active) → end it WITHOUT reporting a new incoming, - // so there's no blip. reportCall(endedAt:) cleanly dismisses the CallKit UI. - if calls[uuid] != nil || activeUUID == uuid { - var ev = calls[uuid] ?? [:]; ev["callUUID"] = uuid.uuidString - calls.removeValue(forKey: uuid) - endedCalls.insert(uuid) - if activeUUID == uuid { disconnectRoom(); activeUUID = nil } - provider?.reportCall(with: uuid, endedAt: Date(), reason: .remoteEnded) - notifyListeners("endCall", data: ev) // if it was active, leave the meeting window too - completion() - return - } - // (c) unknown call (this very push relaunched the app) → we MUST report, then immediately end. - // A tiny unavoidable blip, but only in this rare cold case. + // iOS 13+ TERMINATES the app if a VoIP push doesn't result in reportNewIncomingCall before + // completion() — that's the crash after several calls (my earlier "no-blip" optimization SKIPPED + // the report for known/ended calls, which iOS kills for). So ALWAYS report, then immediately end: + // * uuid already ringing/active → the report errors harmlessly (NO second ring), reportCall ends it. + // * late/duplicate cancel (done) → a brief, unavoidable blip (acceptable; a crash is not). let u = CXCallUpdate() u.remoteHandle = CXHandle(type: .generic, value: (dict["callerName"] as? String) ?? "Call") + let wasActive = (activeUUID == uuid) + var ev = calls[uuid] ?? [:]; ev["callUUID"] = uuid.uuidString + calls.removeValue(forKey: uuid) endedCalls.insert(uuid) + if wasActive { disconnectRoom(); activeUUID = nil } provider?.reportNewIncomingCall(with: uuid, update: u) { [weak self] _ in self?.provider?.reportCall(with: uuid, endedAt: Date(), reason: .remoteEnded) + if wasActive { self?.notifyListeners("endCall", data: ev) } // active call cancelled → leave the meeting completion() } return