Skip to content

feat(ios): add VoIP push support for PushKit / CallKit#211

Open
yash-garg wants to merge 4 commits into
famedly:mainfrom
yash-garg:yg/voip-push
Open

feat(ios): add VoIP push support for PushKit / CallKit#211
yash-garg wants to merge 4 commits into
famedly:mainfrom
yash-garg:yg/voip-push

Conversation

@yash-garg

@yash-garg yash-garg commented May 21, 2026

Copy link
Copy Markdown

Closes FM-638

I've tested this with the app and it works, the call shows up natively in iOS.

@yash-garg
yash-garg requested a review from a team as a code owner May 21, 2026 16:30
@codecov

codecov Bot commented May 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.41935% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.83%. Comparing base (b97ce1d) to head (80408d6).

Files with missing lines Patch % Lines
src/pusher.rs 78.04% 9 Missing ⚠️
src/error.rs 0.00% 4 Missing ⚠️
src/models.rs 94.11% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #211      +/-   ##
==========================================
- Coverage   90.17%   88.83%   -1.34%     
==========================================
  Files           9        9              
  Lines         529      591      +62     
==========================================
+ Hits          477      525      +48     
- Misses         52       66      +14     
Files with missing lines Coverage Δ
src/api.rs 96.87% <ø> (ø)
src/settings.rs 91.30% <ø> (ø)
src/models.rs 96.52% <94.11%> (-0.42%) ⬇️
src/error.rs 66.66% <0.00%> (-10.26%) ⬇️
src/pusher.rs 93.38% <78.04%> (-2.92%) ⬇️

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b97ce1d...80408d6. Read the comment docs.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class support for iOS VoIP pushes (PushKit/CallKit) by introducing a dedicated VoIP APNs push path and routing data_message: ios_voip devices through it, with accompanying tests and config documentation updates.

Changes:

  • Add DataMessageType::IosVoip and route VoIP devices through direct APNs regardless of notify_via.
  • Implement push_notification_voip_apns to send APNs requests with apns-push-type: voip and CallKit-related custom payload fields.
  • Add test coverage for VoIP payload generation and fallback behavior when sender_display_name / event_id are missing.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/models.rs Introduces IosVoip device message type detection via data_message: ios_voip.
src/api.rs Routes VoIP pushes to direct APNs and calls the new VoIP pusher function.
src/pusher.rs Adds VoIP-specific APNs push function and blocks VoIP from going through FCM.
src/settings.rs Allows parsing apns_push_type: voip from config.
src/error.rs Adds From<a2::Error> mapping to HedwigError for APNs payload/build errors.
tests/pusher.rs Adds tests validating VoIP payload fields and fallback behavior.
config.sample.yaml Documents VoIP routing behavior and required APNs configuration.
Cargo.toml / Cargo.lock Adds uuid dependency for generated call IDs when event_id is absent.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/pusher.rs Outdated
Comment thread src/pusher.rs
Comment thread src/settings.rs Outdated
Comment thread src/error.rs
Comment thread src/pusher.rs Outdated
Comment thread src/pusher.rs Outdated
Comment thread src/pusher.rs
Comment thread src/pusher.rs
@yash-garg
yash-garg marked this pull request as draft May 26, 2026 11:24
Comment thread src/api.rs Outdated
@yash-garg
yash-garg marked this pull request as ready for review July 16, 2026 08:05
yash-garg and others added 2 commits July 16, 2026 13:36
- Require app_id to end in '.voip' so the APNs topic always matches
  the PushKit registration
- Add ErrCode::VoipNotSupported instead of BadJson when a VoIP push
  is routed to FCM
- Log a2 errors in From<a2::Error> like the other error impls
- Fall back to 'Unknown' caller name instead of an empty string
- Document the intentionally empty handle and hardcoded isVideo
- Remove the server-side notify_via override; the app must register
  VoIP pushers with notify_via: apns
- Disallow 'voip' as a general apns_push_type config value
The flutter_callkit_incoming plugin parses the push payload id with
UUID(uuidString:), so passing the raw Matrix event_id ($abc...) crashed
the app on every VoIP push. Derive a stable UUIDv5 from the event_id
instead, preserving CallKit deduplication.
Homeservers forward custom pusher keys inside device.data, never as
top-level device fields, so a client-registered pusher could not
actually select APNs delivery. Honour notify_via from device.data
(keeping top-level for compatibility) and always route ios_voip
devices via APNs, since PushKit tokens are not valid FCM tokens.
Comment thread src/pusher.rs
}
DataMessageType::IosVoip => {
// VoIP pushes are routed through push_notification_voip_apns, not FCM.
return Err(HedwigError {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will lead to retrying the request push_max_retries times. You could do something like:

impl ErrCode {
      /// Whether retrying a push that failed with this code could ever succeed.
      #[must_use]
      pub fn is_permanent(&self) -> bool {
              matches!(self, ErrCode::BadJson | ErrCode::VoipNotSupported | ErrCode::APNSNotConfigured)
      }
}

And use it to avoid retrying such requests that will just fail every time

Comment thread src/pusher.rs
sender: &Arc<dyn APNSSender + Send + Sync>,
settings: &Settings,
) -> Result<(), HedwigError> {
if !device.app_id.starts_with(&settings.hedwig.app_id) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check is already done in 2 other places, I think it's worth extracting it into a small helper and use it everywhere now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants