fix(ios): keep CallKit call active on answer so the app foregrounds

Answering did not open the app: ending the CallKit call immediately (to free the mic)
made iOS cancel the app launch before it foregrounded. Keep the call ACTIVE on answer
— fulfilling an active-call answer is what foregrounds/unlocks the app — and fire the
answerCall event so the WebView joins. didActivate stays a no-op (don't fight WebKit's
mic). The CallKit call is ended later when the WebView call ends.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 20:44:09 +05:30
parent 6096b62368
commit 9dc253143e
@@ -175,16 +175,12 @@ public class NativeCallPlugin: CAPPlugin, CAPBridgedPlugin, PKPushRegistryDelega
var data: [String: Any] = calls[uuid] ?? [:] var data: [String: Any] = calls[uuid] ?? [:]
data["callUUID"] = uuid.uuidString data["callUUID"] = uuid.uuidString
action.fulfill() action.fulfill()
// RING-ONLY MODEL: CallKit was used purely for the incoming ring. End the CallKit call NOW so iOS // Keep the CallKit call ACTIVE fulfilling the answer for an ACTIVE call is what foregrounds/unlocks
// RELEASES the microphone an active CallKit call reserves the mic and the WebView's WebRTC would get // the app and gives it in-call context (ending it here made iOS cancel the app launch the app never
// a dead mic. Then hand the call to the WebView, giving iOS a beat to tear down the CallKit audio // opened). The WebView carries the media; we deliberately do NOT reconfigure the audio session
// session before the WebView grabs the mic. Background audio is preserved by the 'voip' background mode. // (didActivate is a no-op) so we don't fight WebKit's WebRTC mic. The CallKit call is ended later when
provider.reportCall(with: uuid, endedAt: Date(), reason: .remoteEnded) // the WebView call ends (the dm-call/group-call inactive event JS calls endCall()).
calls.removeValue(forKey: uuid) notifyListeners("answerCall", data: data)
endedCalls.insert(uuid)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
self?.notifyListeners("answerCall", data: data)
}
} }
public func provider(_ provider: CXProvider, perform action: CXEndCallAction) { public func provider(_ provider: CXProvider, perform action: CXEndCallAction) {