Yes — CallKit will return callUUIDAlreadyExists on the second
report, which suppresses the second ring. However, on iOS 16+
we observed that the duplicate reportNewIncomingCall still
produces a brief banner flash (~80–120 ms on A13-class silicon)
before CallKit rejects it. On newer silicon the flash is
imperceptible but a subtle haptic tick still plays.
To make the duplicate report truly invisible, three things
together:
Deterministic UUID derivation from the call ID (SHA-256), so
both transport paths converge on the same UUID without any
coordination.
A phantom CXCallUpdate with empty caller info on the duplicate
path — if the race slips through, what flashes is an empty
card, not "Unknown".
Immediately in the completion block, a historical end:
reportCall(with: uuid, endedAt: Date(timeIntervalSinceNow: -3600), reason: .answeredElsewhere)
The .answeredElsewhere + historical endedAt combination is
the only end reason we found that CallKit dismisses with zero
transient UI — no flash, no haptic, no Missed entry in Recents.
We tried .remoteEnded, .failed, .declinedElsewhere, and
.unanswered — each has a visible or audible side-effect.
Full writeup with drop-in Swift code, reproduction guide, and
the full list of alternatives we tried and rejected:
https://github.com/sefatanis/ios-callkit-duplicate-ring-suppression
Tested on iOS 14.8 – 18.1.
Topic:
App & System Services
SubTopic:
General
Tags: