Display the system-calling UI for your app’s VoIP services and coordinate your calling services with other apps and the system using CallKit.

Posts under CallKit tag

202 Posts

Post

Replies

Boosts

Views

Activity

CallKit does not activate audio session with higher probability after upgrading to iOS 18.4.1
Hi, We've noticed that this issue occurs more frequently after upgrading to iOS 18.4.1 and can result in one-way audio. Our app uses CallKit with WebRTC to establish VoIP connections. However, on iOS 18.4.1, CallKit no longer triggers: func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) We're currently comparing the occurrence rate across different iOS versions to better understand the impact. Could you please help analyze the root cause of this issue?
40
1
5.8k
Sep ’26
Questions about VoIP Push compliance rules and CallKit handling
Hello everyone, I’m an iOS developer working on a real-time communication app that supports VoIP calls using CallKit. The app has been in production for more than 5 years. Over the years, some users have occasionally reported that they do not receive incoming call pushes. We have tried multiple optimizations on both the client and server side, but the improvement has been limited. From Apple documentation and discussions online, I understand that iOS may restrict VoIP pushes if the system detects violations of VoIP push usage rules (for example, not presenting a CallKit call after receiving a VoIP push). However, the exact rules and thresholds for these violations are not clearly documented, so I’d like to ask a few questions to better understand the expected behavior. Below is a simplified description of our current call flow. Call Flow Caller When the user initiates a call: We do not use CallKit The call is handled entirely using a custom in-app call UI Callee When the user receives a call: Device locked or app in background A VoIP push wakes the app The app presents the CallKit incoming call UI App in foreground The server still sends a VoIP push The app first reports the call to CallKit After a very short delay, the app programmatically ends the CallKit call Then a custom in-app call UI is presented via the app's long connection The reason we always send a VoIP push (even when the app is in the foreground) is that we want to maximize call delivery reliability.
7
0
1.6k
Sep ’26
iOS 27 Public Beta 6: CallKit UI remains after ending two outgoing calls in modified Apple Speakerbox sample
Summary On an iPhone 17 running iOS 27 Public Beta 6, the system CallKit UI remains visible after ending two outgoing calls individually. This is reproducible using Apple's “Making and Receiving VoIP Calls with CallKit” sample (Speakerbox), with only two minimal changes to support multiple simultaneous calls. After both calls end, Speakerbox displays “No Calls”, but the system CallKit UI still shows 111, and the Dynamic Island retains a call-related status. Environment Device: iPhone 17 (physical device) OS: iOS 27 Public Beta 6 App: Apple's Speakerbox sample with the modifications below Backend: None. The sample's original simulated calls are used; no external VoIP SDK is involved. Changes to the original sample Only two files were modified. 1. ProviderDelegate.swift Added: providerConfiguration.maximumCallGroups = 3 The original setting remains unchanged: providerConfiguration.maximumCallsPerCallGroup = 1 2. SpeakerboxCallManager.swift Before starting a new outgoing call, hold existing calls that are not already on hold. The hold actions and new start action are submitted in the same transaction: let transaction = CXTransaction() for call in calls where !call.isOnHold { transaction.addAction( CXSetHeldCallAction(call: call.uuid, onHold: true) ) } transaction.addAction(startCallAction) requestTransaction(transaction) No changes were made to the simulated connection timing, startedConnectingAt/connectedAt reporting, CXEndCallAction handling, call removal, or audio handling. The original end-call handler finds the call by UUID, calls endSpeakerboxCall(), fulfills the end action, and removes the call from the app's list. Steps to reproduce Launch the modified Speakerbox sample. Create an outgoing call to 111. Create a second outgoing call to 222. The first call is placed on hold. Wait until Speakerbox shows 111 — On Hold and 222 — Active, with call durations displayed. In Speakerbox, select 222 - End Call. Select 111 - End Call. Confirm that Speakerbox displays “No Calls”. Open the app switcher and inspect the system CallKit UI. Also inspect the Dynamic Island. Open the remaining system call UI and tap End. Expected result After both calls end, the system should no longer display an ongoing call in the CallKit UI or Dynamic Island. Actual result Speakerbox displays “No Calls”. The system CallKit UI continues to display the first call, 111. The Dynamic Island continues to show a call-related “Hold” status. Tapping End in the remaining system call UI does not clear the residual state during the recording. The issue occurs even after waiting for both simulated calls to connect before ending them. This is the system-provided CallKit UI, not an active-call view implemented by the sample app. Reproduction video Recording: 4b03d30e507b874d9c19c0523632e9d6.mp4 00:24–00:26 (approximately): Both calls are present; 111 is on hold and 222 is active. 00:26–00:31 (approximately): End 222, then end 111. Speakerbox displays “No Calls”. 00:31–01:03 (approximately): The system CallKit UI and Dynamic Island retain the call state despite further attempts to end it. Questions Is this an iOS 27 regression, or is additional lifecycle handling required when extending Speakerbox to support multiple calls? Please advise what additional diagnostics would help investigate why the system UI retains an ongoing-call state after both calls have been ended using the sample's original end-call handling. Source and video downloads https://github.com/willbin/callkit-ios27-repro/releases/tag/repro-v1
4
0
1k
Sep ’26
LiveCommunicationKit: In‑call banner missing in foreground, only appears after app goes background (iPhone14 Pro)
Environment: Device: iPhone 14 Pro iOS version: 26 SDK: LiveCommunicationKit App status: Foreground / Active state Problem reproduction: Intermittent, happens on some devices, not 100% reproducible on all test devices. Expected behavior: Incoming call banner should present when app is active (foreground). Actual behavior: No system incoming‑call banner while app is foreground. Banner appears only after pressing home‑button / switching app to background. Additional notes: Our app uses LiveCommunicationKit because VoIP PushKit is not permitted for China mainland App Store distribution. Call reporting completes without error; no error returned from LiveCommunicationKit API. private func _lckReportIncomingCall(call: Call?, uuid: UUID, handle: String, hasVideo: Bool, displayName: String) { guard let manager = conversationManager else { Log.error("[ProviderDelegate-LCK] ConversationManager is nil, falling back") _fallbackReportIncomingCall(call: call, uuid: uuid, handle: handle, hasVideo: hasVideo, displayName: displayName) return } let callInfo = callInfos[uuid] let callId = callInfo?.callId ?? "" let remoteHandle = Handle(type: .generic, value: handle, displayName: displayName) let update = Conversation.Update(localMember: nil, members: [remoteHandle], activeRemoteMembers: [remoteHandle]) // 冷启动时 iOS PKPushRegistry 要求在 2 秒内上报来电,否则杀进程。 // Task { @MainActor } 是异步的,调试时主线程 RunLoop 来不及调度就超时了。 // 这里用 DispatchSemaphore 在后台线程同步等待 LCK 上报完成。 let semaphore = DispatchSemaphore(value: 0) var lckError: Error? DispatchQueue.main.async { Task { @MainActor in do { try await manager.reportNewIncomingConversation(uuid: uuid, update: update) Log.info("[ProviderDelegate-LCK] Reported incoming conversation: callId=\(callId), uuid=\(uuid)") if TelecomManager.shared.endCallkit { CoreContext.shared.doOnCoreQueue(synchronous: true) { core in let linphoneCall = core.getCallByCallid(callId: callId) if linphoneCall?.state == .PushIncomingReceived { try? linphoneCall?.terminate() } } } } catch { Log.error("[ProviderDelegate-LCK] Failed to report incoming conversation: \(error)") lckError = error } semaphore.signal() } } let waitResult = semaphore.wait(timeout: .now() + 5) if waitResult == .timedOut { Log.error("[ProviderDelegate-LCK] Timed out waiting for LCK report, falling back") _fallbackReportIncomingCall(call: call, uuid: uuid, handle: handle, hasVideo: hasVideo, displayName: displayName) } else if let error = lckError { Log.error("[ProviderDelegate-LCK] LCK report failed: \(error), declining SIP call") // 来电被拒(勿扰/黑名单等),decline 掉 SIP 侧 CoreContext.shared.doOnCoreQueue(synchronous: true) { _ in try? call?.decline(reason: .Busy) } } }
1
0
682
Sep ’26
CXEndCallAction fails with unknownCallUUID (Code=4) when app rejects/ends a call reported via CallKit
Summary: When our VoIP app attempts to reject/end an incoming call, CXCallController.request(_:completion:) fails with: Error Domain=com.apple.CallKit.error.requesttransaction Code=4 "(null)" CXErrorCodeRequestTransactionError, Code 4 = unknownCallUUID — the call UUID did not match a call known to the call kit. As a result, the Incoming call screen continue to displayed and leaving a stale/ghost call in CallKit. Environment: iOS version: 26 / 18 Device model: iPhone 14 Steps to Reproduce: Receive an incoming VoIP call (reported to CallKit via CXProvider.reportNewIncomingCall). Reject the call programmatically OR manual end call Observe: app-side call state transitions to ended/rejected, but the native CallKit call UI/state does not clear — CXEndCallAction request fails with unknownCallUUID. [fill in: reproduction rate — always / intermittent / specific timing window] Full error log: 2026-08-13T13:41:16:902+0530 Thread 915 — [CallKitManager requestTransaction:]_block_invoke: Error requesting transaction: [<CXTransaction 0x14f1eb060 UUID=E1785E55-24CF-49E6-8225-372E7F5F0F9D isComplete=0 actions=( "<CXEndCallAction 0x141d18190 UUID=BBE9EB13-EFF5-49F1-99DC-96A6218C9031 state=0 commitDate=(null) callUUID=7C8A16B2-4CF4-4F66-A588-5A663A6D52D4 dateEnded=(null)>" )>] Error Domain=com.apple.CallKit.error.requesttransaction Code=4 "(null)" Questions : Can CXCallController.request(_:completion:) legitimately fail with unknownCallUUID if CXEndCallAction is issued before the reportNewIncomingCall completion handler has returned — i.e., is there a required ordering/synchronization guarantee we're missing? Are there known conditions (OS version-specific behavior, app suspension/background state, Do Not Disturb / Focus interaction, multiple concurrent CXProviders) under which CallKit's internal call controller can "lose" a UUID that was previously reported, causing a subsequent valid action to be rejected as unknown? Is there a supported way to query CXCallController/CXProvider for the current set of UUIDs it considers "known" at a given time, to diagnose this class of mismatch before issuing an action? Once unknownCallUUID occurs, is there a recommended recovery path to force-clear a stale call from CallKit's UI/state from the app side, short of ending all calls?
1
0
742
Sep ’26
Live Caller ID Lookup: is there any way to check onboarding status after approval?
We have a Live Caller ID Lookup deployment that has been blocked for weeks, and the core difficulty is not technical. It is that we cannot find out anything about the state of our onboarding submission. Here is where we stand. We submitted the onboarding form and our configuration was approved on 8 August. That approval email is the only communication we have ever received about this feature. Our service is deployed and verified: the OHTTP gateway negotiates HTTP/2, serves a valid key configuration, the issuer directory returns 200 and Apple polls it continuously, the DNS TXT record is in place, and the validation identity is in our corpus. Development builds work end to end and display caller names correctly on incoming calls. App Store builds do not. Every authenticated request fails on the device in roughly 47 ms, before anything leaves the phone: ciphermld(CipherML) requestData(byKeywords:shardIds:clientConfig:) threw NSURLErrorDomain Code=-1009 _NSURLErrorPrivacyProxyFailureKey = true nw_endpoint_proxy_handler_should_use_proxy: "Proxies not present, but required to fail closed" The network path is healthy in the same moment, and other processes on the device do receive proxy configuration. Only our extension's bundle identifier never does. We reproduced this on Wi-Fi and on cellular, and after a device restart. We have since learned, from a maintainer replying to an issue we opened on the pir-service-example repository, that "approved" in the CloudKit console is not the same thing as "successfully onboarded", and that we are still in the first state. That explains the behavior completely. What we cannot explain is how a provider is supposed to discover this. There is no status field in the console, no notification when the state changes, and nothing in the onboarding documentation that mentions a second stage exists. We wrote to Apple several times over these weeks. We were told more than once that an internal team would look into it, and we never received a reply. Apple Support told other providers in the same situation that this is a technical matter beyond their scope. Looking through the pir-service-example issues, this pattern is common. One provider reported waiting three months without a response. Another reported that form to production took almost four months. Another only discovered the feature had been enabled by noticing traffic arriving at their own server. My questions: Is there any supported way to check the onboarding state of a submitted configuration, or to ask about one that appears stuck? If not, is anything planned? Is the transition from "approved" to "successfully onboarded" expected to be automatic, and roughly how long should it take? For anyone who has been through this: did you eventually get a notification, or did you find out by watching your own server logs? We are not asking for our submission to be prioritized. We would simply like to know whether we are waiting on something or whether something needs to be resubmitted, and right now there is no way to tell the difference. Our paying subscribers cannot use the feature, and we have not been able to give them an accurate answer either. Thank you.
2
0
719
Sep ’26
Live Caller ID Lookup: App Store build fails with "Proxies not present, but required to fail closed" while development build works
Summary Our Live Caller ID Lookup deployment works end-to-end from an Xcode-signed build but fails on the App Store build of the same app, on the same device, same network, same Apple ID. On the production build every authenticated request fails locally in ~47 ms, before any packet leaves the device. Our Identity & Trust configuration in CloudKit Console has read "Approved" since 2026-08-08. Device log at the moment of failure nw_endpoint_proxy_handler_should_use_proxy: "Proxies not present, but required to fail closed" ciphermld(CipherML) <Error>: queryStatus(for:options:) threw an error: Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." NSErrorFailingURLKey=https://<our-issuer-host>/token-key-for-user-token _NSURLErrorPrivacyProxyFailureKey=true NSUnderlyingError={NSPOSIXErrorDomain Code=50 "Network is down"} (_NSURLErrorNWPathKey=satisfied, Wi-Fi up, LQM: good) Control experiment: the device's privacy proxy stack is healthy at that exact moment 0.93 seconds before our extension is refused, the same device attaches a proxy to another connection without any problem: 11:51:59.417 mDNSResponder nw_endpoint_proxy_add_config_if_applicable Adding proxy masque Proxy: https://mask.icloud.com:443 11:52:00.337 ciphermld [C15 ... bundle id: <our extension>, proxy fail closed] start 11:52:00.346 ciphermld Proxies not present, but required to fail closed System state in the same capture: Privacy Proxy Service Status: 1 Privacy Proxy User Tier: 1 Privacy Proxy Networks: (<Cellular, Active>, <WiFi, Active>) Counts across the capture: nw_endpoint_proxy_add_config_if_applicable appears twice (mDNSResponder and Safari SafeBrowsing), while Proxies not present appears 16 times, all from ciphermld, all for our extension's bundle id. No connection made on behalf of our extension is ever given a proxy configuration. Reproduced on two networks and after a device restart, with the device charging and Low Power Mode off. Apple's own endpoint already recognises our issuer 11:51:59.405 ciphermld(CipherML) Fetching token issuer directory from https://gateway.icloud.com/pat-issuer-directory?issuer=<our-issuer-host> 11:52:00.332 200, 1511 bytes, 928 ms Our validation Bearer token from the onboarding submission also returns 200 on /token-key-for-user-token when Apple's validator calls it, which we observe on our server several times a day. A second observation that may be the key In apple/pir-service-example issue #139 an Apple engineer stated that once onboarding is complete, "all requests ... will automatically use OHTTP even when it is a development build". We captured our development build today with full device logging. Every one of its connections goes directly to our PIR host over QUIC, with no proxy fail closed stamp, and the words ohttp, oblivious, mask.icloud and our OHTTP gateway hostname appear zero times in the capture: 16 connections, all: Hostname#<our-pir-host>:443, attribution: developer "proxy fail closed": 0 "Proxies not present": 0 If a completed onboarding forces OHTTP even for development builds, then our development build going direct indicates the relay configuration was never distributed for our extension's bundle id, despite the console showing "Approved". Fleet-level data, not a single device On the day of release, 40 distinct premium accounts on the current App Store build fetched their user tier token from our backend. Across that entire day our PIR service saw zero Privacy Pass issuances and zero queries from App Store builds. For contrast, a single development-signed device produced 144 /issue requests in one day earlier in the month. What we already checked and fixed on our side HTTP/2 ALPN negotiates on all three hosts (service, issuer, gateway). The DNS TXT ownership record is in place. The OHTTP gateway serves a valid key config (X25519 / HKDF-SHA256 / AES-128-GCM). The PIR use case names match the required <extension bundle id>.block / .identity format exactly. The validation identity +1 408 555 1212 "Johnny Appleseed" is present in the live corpus. We found and fixed a real defect of our own: our OHTTP gateway's allowed target origin list was missing the issuer host. We corrected it and restarted the gateway (its HPKE key config was byte-identical afterwards, so no key rotation occurred). The failure reproduced with an identical signature seven minutes later, and the device log confirms these hops never touch our gateway anyway. Questions Is relay / privacy proxy provisioning for a production bundle id a separate step after the Identity & Trust configuration is approved, and what is the expected propagation time? Is there any way for a developer to check provisioning status? Right now an "Approved" badge and a completely non-functional production build look identical from our side. Is there a supported way to exercise the production privacy proxy path before shipping to the App Store? Development builds bypass it by design, so the first real test of the production path was made by paying customers. This seems likely to affect other adopters. Is provisioning keyed on the extension bundle id, the app bundle id, or the team id? Other system consumers on the same device receive proxy configurations normally. We accidentally submitted two identical configurations three minutes apart on 2026-08-08 and both show "Approved". Can a duplicate block or confuse provisioning, and should one be revoked? Environment: iPhone 17 Pro Max, iOS 26.6.1, Xcode 26.5 (17F42). App Store build version 14 (36). Feedback Assistant report with full device logs: FB24501570 GitHub issue with the same detail: https://github.com/apple/pir-service-example/issues/162 Configuration ID: 693155e2-b8ba-4b47-9b6c-64fd97677386 and a1959377-8069-4225-87b9-c1ff81016611
2
0
493
Sep ’26
provider(_:didActivate:) callback intermittently not triggered, causing widespread audio loss for users
Hi everyone, I am facing a critical issue where the CallKit provider delegate method provider(_:didActivate:) is intermittently not triggered. This occasionally results in a total loss of audio during some VoIP calls, while other calls work perfectly fine. Here is the sequence of steps I am currently implementing: Report Incoming Call: The app receives a VoIP push notification and reports the call using reportNewIncomingCall(with:update:completion:). Answer Action: The user taps the answer button, and the app processes the CXAnswerCallAction. Configure Audio Session: Inside the provider delegate, I configure the AVAudioSession category and mode (e.g., setting category to .playAndRecord and mode to .voiceChat). Note: As per Apple's guidelines, I do not call setActive(true) manually, expecting CallKit to activate it automatically. Despite following this standard flow, there are times when provider(_:didActivate:) is skipped entirely, meaning the audio engine fails to initialize for that specific call session. We are currently receiving a large volume of user complaints regarding this issue, as it heavily impacts the core calling experience in production. Could an Apple engineer or anyone from the community look into this? Any insights into what might be causing CallKit to occasionally fail to activate the audio session or how to work around this would be highly appreciated. Thank you!
5
0
1.3k
Aug ’26
Callkit returns error code=106 which is not on official document
Callkit returns error code=106. I can't find what this error code means in official documents. What is this error code? Has anybody seen this error code so far? My app is VoIP app using APNs VoIP push. Below is log example of my app. 2026/04/26 09:01:45.752 [436:103]CallManager incomingCall(::::_:completion:):228: error Error Domain=com.apple.CallKit.error.incomingcall Code=106 "(null)"
1
0
543
Aug ’26
Live Caller ID Lookup Extension - No option in phone settings
iOS 18 (22A3354) will not offer a option in settings (> Apps > Phone) after calling openSettings to enable live caller id lookup extension. iPhone and MacBook are in the same network. The PIRService runs on MacBook and is reachable via iPhone Safari (via http://MacBookPro:8080/). Hummingbird print log: hb_method=GET hb_uri=/ [Hummingbird] Request. After deploying the application via Xcode to the iPhone no requests are printed in the terminal. The extension was added like documented and bundle id is also checked multiple times. issuerRequestUri in service-config.json is http://MacBookPro:8080/issue. As far as I can tell, everything has been set up in accordance with the Testing Live Caller ID instructions. Is there something missing?
10
0
2.5k
Aug ’26
Live Caller ID Lookup request stuck in review - how to verify our endpoints pass validation?
We submitted Live Caller ID Lookup requests for two apps and both remain “In Review.” Our PIR server, Privacy Pass issuer and OHTTP gateway are deployed, DNS TXT records are published for both bundle identifiers, and the test number returns correctly. Is there a way to verify from our side that Apple’s automated endpoint validation passes? Any common misconfiguration that causes a request to sit without feedback?
0
0
704
Aug ’26
How to collect statistics for Message Filter Extension and Call Blocking Extension?
Hi everyone, I'm developing an iOS app that includes both a Message Filter Extension and a Call Blocking Extension. I'd like to understand whether there is any supported way to collect or access statistics for these extensions, such as: Number of messages filtered (allowed vs. junk) Number of blocked calls Number of identified calls Extension invocation count Filtering success/failure metrics Any analytics or usage data exposed by the system I understand that these extensions are privacy-sensitive, so I'm not expecting access to user content. I'm specifically looking for aggregate statistics or system-provided metrics that an app can legally and technically access. My questions are: Does iOS expose any APIs or callbacks for collecting usage statistics from a Message Filter Extension? Is there any way to determine how many calls were blocked or identified by a Call Blocking Extension? Are there any recommended Apple-supported approaches for tracking extension usage without violating user privacy? If no APIs are available, is this limitation intentional due to the privacy model of these extensions? I'd appreciate any guidance or best practices from Apple engineers or developers who have worked with these extensions. Thank you!
4
0
1.3k
Jul ’26
How should apps handle deprecated INStartAudioCallIntentIdentifier and INStartVideoCallIntentIdentifier from Recents?
Hello, I am currently developing call-related features for our app, and I have a question regarding one of the APIs. When the app is launched from the Recents list by selecting a recent call, the activityType of the userActivity is provided as either INStartAudioCallIntentIdentifier or INStartVideoCallIntentIdentifier. However, I understand that these identifiers have been deprecated since iOS 13, and the documentation recommends using INStartCallIntentIdentifier instead. The issue is that when the app is launched from the Recents list, INStartCallIntentIdentifier is never provided. Instead, the deprecated identifiers (INStartAudioCallIntentIdentifier and INStartVideoCallIntentIdentifier) continue to be delivered. I have reviewed the available documentation, but it is not clear how developers are expected to handle this situation. Could you please advise on the recommended approach for supporting this flow? Is it expected that applications continue to handle the deprecated identifiers in this case, or is there another recommended implementation? I would greatly appreciate any guidance you can provide. Thank you.
0
0
631
Jul ’26
iOS 26.5: Xfinity Mobile 'Spam' call filtering not silencing calls / not sending to voicemail (CallKit bug)
On my iPhone (iOS 26.5), the Call Filtering "Spam" setting under Settings → Apps → Phone isn't working as described. The setting's own text says calls identified as spam or fraud by Xfinity Mobile will be silenced, sent to voicemail, and moved to the Spam list, but in practice these calls ring through normally with no silencing at all, even though the toggle is enabled and Unknown Callers is off. This looks related to a broader CallKit regression that's been reported on Apple's Developer Forums since the iOS 26 beta cycle (threads 794740, 799694, and 800415), where call-blocking and filtering entries are being ignored or applied inconsistently; other developers have already filed Feedback IDs FB19140680, FB19140594, and FB20276986 for related symptoms. It would be great to get this fixed, since it defeats the purpose of carrier-level spam filtering on the phone.
0
0
548
Jul ’26
AVRoutePickerView's presented route list is automatically dismissed when CXProvider reports the call as connected (`reportOutgoingCall(with:connectedAt:)`)
Hi, I want my user to be able to change their audio output device while calling someone, even before the call is established. But I've run into an issue combining CallKit and AVRoutePickerView. On iOS 26 (Xcode 26.5, tested on iPhone 13 Mini): when my AVRoutePickerView's route list is presented while my call is still connecting (not yet established), the presented list is automatically dismissed the moment the app reports the call as connected via CXProvider.reportOutgoingCall(with:connectedAt:). That doesn't seem natural to me — it should stay open, since the user is actively interacting with unrelated system UI at that moment. This happens even when nothing about the AVAudioSession itself changes: no category change, no route change, no didActivate/didDeactivate callback fires around that time. The dismissal is caused by the CallKit "connected" state transition itself, but I don't know why, or whether it's intentional. Steps to reproduce (sample project below): Tap "Start Fake Call". Within 5 seconds, tap the small route picker button and leave its list open. Wait — at t+5s the app reports the call connected via reportOutgoingCall(connectedAt:), and the list closes itself right after. Is this normal? And is there an official, CallKit-sanctioned way to report a call's connected state without this side effect — some way to keep the route picker stable across that transition? The two workarounds I've found both have real downsides: Calling reportOutgoingCall(connectedAt:) at the very start of the call, before the callee has actually answered , but then CallKit no longer reflects the real call state (Recents/duration would include ringing time, and calls that are never answered would still show as "connected"). Disabling audio device selection entirely until the call is established... works, but hurts the user experience, since users may want to switch output before the call connects. Here's the minimal reproduction: import CallKit import SwiftUI struct ContentView: View { @StateObject private var demo = CallKitDemo() var body: some View { VStack(spacing: 24) { Text(demo.statusText) .font(.system(.body, design: .monospaced)) .multilineTextAlignment(.center) Button("Start Fake Call") { demo.startFakeCall() } .disabled(demo.isCallInProgress) RoutePickerView() .frame(width: 60, height: 60) } .padding() } } private struct RoutePickerView: UIViewRepresentable { func makeUIView(context: Context) -> AVRoutePickerView { let picker = AVRoutePickerView() picker.delegate = context.coordinator return picker } func updateUIView(_ uiView: AVRoutePickerView, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator() } final class Coordinator: NSObject, AVRoutePickerViewDelegate { func routePickerViewWillBeginPresentingRoutes(_ routePickerView: AVRoutePickerView) { print("[REPRO]", Date(), "picker WILL present routes") } func routePickerViewDidEndPresentingRoutes(_ routePickerView: AVRoutePickerView) { print("[REPRO]", Date(), "picker DID end presenting routes <- dismissed here") } } } @MainActor final class CallKitDemo: NSObject, ObservableObject { @Published var statusText = "Idle" @Published var isCallInProgress = false private let provider: CXProvider = { let configuration = CXProviderConfiguration() configuration.supportsVideo = false configuration.maximumCallGroups = 1 configuration.maximumCallsPerCallGroup = 1 configuration.supportedHandleTypes = [.generic] return CXProvider(configuration: configuration) }() private let callController = CXCallController() private var currentCallUUID: UUID? override init() { super.init() provider.setDelegate(self, queue: nil) } func startFakeCall() { let uuid = UUID() currentCallUUID = uuid isCallInProgress = true statusText = "Requesting CXStartCallAction..." let handle = CXHandle(type: .generic, value: "repro-call") let startAction = CXStartCallAction(call: uuid, handle: handle) callController.request(CXTransaction(action: startAction)) { error in if let error { print("[REPRO]", Date(), "CXStartCallAction request failed:", error) } } DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in guard let self, currentCallUUID == uuid else { return } print("[REPRO]", Date(), "reportOutgoingCall(connectedAt:)") statusText = "Reported connected at t+5s" provider.reportOutgoingCall(with: uuid, connectedAt: Date()) } DispatchQueue.main.asyncAfter(deadline: .now() + 8) { [weak self] in guard let self, currentCallUUID == uuid else { return } callController.request(CXTransaction(action: CXEndCallAction(call: uuid))) { _ in } currentCallUUID = nil isCallInProgress = false statusText = "Call ended" } } } extension CallKitDemo: CXProviderDelegate { func providerDidReset(_ provider: CXProvider) {} func provider(_ provider: CXProvider, perform action: CXStartCallAction) { action.fulfill() } func provider(_ provider: CXProvider, perform action: CXEndCallAction) { action.fulfill() } func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { print("[REPRO]", Date(), "didActivate") } func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { print("[REPRO]", Date(), "didDeactivate") } } Thanks for your help !
1
0
1.3k
Jul ’26
CallKit CXCallObserver.calls reports stale `hasEnded` status
Hello Apple Developer Support Team, I am experiencing an issue with the CallKit framework on iOS where the CXCallObserver.calls property does not accurately reflect the active call status in real-time. The Issue: Even when there are no active CallKit calls (all calls have been hung up), accessing callObserver.calls still returns CXCall objects where hasEnded is false. This leads to false positives when we check: callObserver.calls.contains { !$0.hasEnded } Our Setup: We maintain a strong reference to CXCallObserver inside a singleton manager. We have assigned the CXCallObserverDelegate and it is receiving events. We noticed a significant delay (sometimes permanently on simulator/certain devices) between the call actually ending on the system level and the calls array being cleared. Questions: Is the CXCallObserver.calls array designed to be an asynchronous snapshot, or should it guarantee real-time consistency with the actual system call state? Is this a known caching issue with callservicesd where ended calls are not immediately deallocated from the observer’s calls array? What is the recommended best practice for determining if there is an active call on the device without relying on the potentially stale callObserver.calls property? Thank you for your support, and I look forward to your guidance. Best regards,
1
0
803
Jul ’26
CallKit does not activate audio session with higher probability after upgrading to iOS 18.4.1
Hi, We've noticed that this issue occurs more frequently after upgrading to iOS 18.4.1 and can result in one-way audio. Our app uses CallKit with WebRTC to establish VoIP connections. However, on iOS 18.4.1, CallKit no longer triggers: func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) We're currently comparing the occurrence rate across different iOS versions to better understand the impact. Could you please help analyze the root cause of this issue?
Replies
40
Boosts
1
Views
5.8k
Activity
Sep ’26
Questions about VoIP Push compliance rules and CallKit handling
Hello everyone, I’m an iOS developer working on a real-time communication app that supports VoIP calls using CallKit. The app has been in production for more than 5 years. Over the years, some users have occasionally reported that they do not receive incoming call pushes. We have tried multiple optimizations on both the client and server side, but the improvement has been limited. From Apple documentation and discussions online, I understand that iOS may restrict VoIP pushes if the system detects violations of VoIP push usage rules (for example, not presenting a CallKit call after receiving a VoIP push). However, the exact rules and thresholds for these violations are not clearly documented, so I’d like to ask a few questions to better understand the expected behavior. Below is a simplified description of our current call flow. Call Flow Caller When the user initiates a call: We do not use CallKit The call is handled entirely using a custom in-app call UI Callee When the user receives a call: Device locked or app in background A VoIP push wakes the app The app presents the CallKit incoming call UI App in foreground The server still sends a VoIP push The app first reports the call to CallKit After a very short delay, the app programmatically ends the CallKit call Then a custom in-app call UI is presented via the app's long connection The reason we always send a VoIP push (even when the app is in the foreground) is that we want to maximize call delivery reliability.
Replies
7
Boosts
0
Views
1.6k
Activity
Sep ’26
iOS 27 Public Beta 6: CallKit UI remains after ending two outgoing calls in modified Apple Speakerbox sample
Summary On an iPhone 17 running iOS 27 Public Beta 6, the system CallKit UI remains visible after ending two outgoing calls individually. This is reproducible using Apple's “Making and Receiving VoIP Calls with CallKit” sample (Speakerbox), with only two minimal changes to support multiple simultaneous calls. After both calls end, Speakerbox displays “No Calls”, but the system CallKit UI still shows 111, and the Dynamic Island retains a call-related status. Environment Device: iPhone 17 (physical device) OS: iOS 27 Public Beta 6 App: Apple's Speakerbox sample with the modifications below Backend: None. The sample's original simulated calls are used; no external VoIP SDK is involved. Changes to the original sample Only two files were modified. 1. ProviderDelegate.swift Added: providerConfiguration.maximumCallGroups = 3 The original setting remains unchanged: providerConfiguration.maximumCallsPerCallGroup = 1 2. SpeakerboxCallManager.swift Before starting a new outgoing call, hold existing calls that are not already on hold. The hold actions and new start action are submitted in the same transaction: let transaction = CXTransaction() for call in calls where !call.isOnHold { transaction.addAction( CXSetHeldCallAction(call: call.uuid, onHold: true) ) } transaction.addAction(startCallAction) requestTransaction(transaction) No changes were made to the simulated connection timing, startedConnectingAt/connectedAt reporting, CXEndCallAction handling, call removal, or audio handling. The original end-call handler finds the call by UUID, calls endSpeakerboxCall(), fulfills the end action, and removes the call from the app's list. Steps to reproduce Launch the modified Speakerbox sample. Create an outgoing call to 111. Create a second outgoing call to 222. The first call is placed on hold. Wait until Speakerbox shows 111 — On Hold and 222 — Active, with call durations displayed. In Speakerbox, select 222 - End Call. Select 111 - End Call. Confirm that Speakerbox displays “No Calls”. Open the app switcher and inspect the system CallKit UI. Also inspect the Dynamic Island. Open the remaining system call UI and tap End. Expected result After both calls end, the system should no longer display an ongoing call in the CallKit UI or Dynamic Island. Actual result Speakerbox displays “No Calls”. The system CallKit UI continues to display the first call, 111. The Dynamic Island continues to show a call-related “Hold” status. Tapping End in the remaining system call UI does not clear the residual state during the recording. The issue occurs even after waiting for both simulated calls to connect before ending them. This is the system-provided CallKit UI, not an active-call view implemented by the sample app. Reproduction video Recording: 4b03d30e507b874d9c19c0523632e9d6.mp4 00:24–00:26 (approximately): Both calls are present; 111 is on hold and 222 is active. 00:26–00:31 (approximately): End 222, then end 111. Speakerbox displays “No Calls”. 00:31–01:03 (approximately): The system CallKit UI and Dynamic Island retain the call state despite further attempts to end it. Questions Is this an iOS 27 regression, or is additional lifecycle handling required when extending Speakerbox to support multiple calls? Please advise what additional diagnostics would help investigate why the system UI retains an ongoing-call state after both calls have been ended using the sample's original end-call handling. Source and video downloads https://github.com/willbin/callkit-ios27-repro/releases/tag/repro-v1
Replies
4
Boosts
0
Views
1k
Activity
Sep ’26
LiveCommunicationKit: In‑call banner missing in foreground, only appears after app goes background (iPhone14 Pro)
Environment: Device: iPhone 14 Pro iOS version: 26 SDK: LiveCommunicationKit App status: Foreground / Active state Problem reproduction: Intermittent, happens on some devices, not 100% reproducible on all test devices. Expected behavior: Incoming call banner should present when app is active (foreground). Actual behavior: No system incoming‑call banner while app is foreground. Banner appears only after pressing home‑button / switching app to background. Additional notes: Our app uses LiveCommunicationKit because VoIP PushKit is not permitted for China mainland App Store distribution. Call reporting completes without error; no error returned from LiveCommunicationKit API. private func _lckReportIncomingCall(call: Call?, uuid: UUID, handle: String, hasVideo: Bool, displayName: String) { guard let manager = conversationManager else { Log.error("[ProviderDelegate-LCK] ConversationManager is nil, falling back") _fallbackReportIncomingCall(call: call, uuid: uuid, handle: handle, hasVideo: hasVideo, displayName: displayName) return } let callInfo = callInfos[uuid] let callId = callInfo?.callId ?? "" let remoteHandle = Handle(type: .generic, value: handle, displayName: displayName) let update = Conversation.Update(localMember: nil, members: [remoteHandle], activeRemoteMembers: [remoteHandle]) // 冷启动时 iOS PKPushRegistry 要求在 2 秒内上报来电,否则杀进程。 // Task { @MainActor } 是异步的,调试时主线程 RunLoop 来不及调度就超时了。 // 这里用 DispatchSemaphore 在后台线程同步等待 LCK 上报完成。 let semaphore = DispatchSemaphore(value: 0) var lckError: Error? DispatchQueue.main.async { Task { @MainActor in do { try await manager.reportNewIncomingConversation(uuid: uuid, update: update) Log.info("[ProviderDelegate-LCK] Reported incoming conversation: callId=\(callId), uuid=\(uuid)") if TelecomManager.shared.endCallkit { CoreContext.shared.doOnCoreQueue(synchronous: true) { core in let linphoneCall = core.getCallByCallid(callId: callId) if linphoneCall?.state == .PushIncomingReceived { try? linphoneCall?.terminate() } } } } catch { Log.error("[ProviderDelegate-LCK] Failed to report incoming conversation: \(error)") lckError = error } semaphore.signal() } } let waitResult = semaphore.wait(timeout: .now() + 5) if waitResult == .timedOut { Log.error("[ProviderDelegate-LCK] Timed out waiting for LCK report, falling back") _fallbackReportIncomingCall(call: call, uuid: uuid, handle: handle, hasVideo: hasVideo, displayName: displayName) } else if let error = lckError { Log.error("[ProviderDelegate-LCK] LCK report failed: \(error), declining SIP call") // 来电被拒(勿扰/黑名单等),decline 掉 SIP 侧 CoreContext.shared.doOnCoreQueue(synchronous: true) { _ in try? call?.decline(reason: .Busy) } } }
Replies
1
Boosts
0
Views
682
Activity
Sep ’26
CXEndCallAction fails with unknownCallUUID (Code=4) when app rejects/ends a call reported via CallKit
Summary: When our VoIP app attempts to reject/end an incoming call, CXCallController.request(_:completion:) fails with: Error Domain=com.apple.CallKit.error.requesttransaction Code=4 "(null)" CXErrorCodeRequestTransactionError, Code 4 = unknownCallUUID — the call UUID did not match a call known to the call kit. As a result, the Incoming call screen continue to displayed and leaving a stale/ghost call in CallKit. Environment: iOS version: 26 / 18 Device model: iPhone 14 Steps to Reproduce: Receive an incoming VoIP call (reported to CallKit via CXProvider.reportNewIncomingCall). Reject the call programmatically OR manual end call Observe: app-side call state transitions to ended/rejected, but the native CallKit call UI/state does not clear — CXEndCallAction request fails with unknownCallUUID. [fill in: reproduction rate — always / intermittent / specific timing window] Full error log: 2026-08-13T13:41:16:902+0530 Thread 915 — [CallKitManager requestTransaction:]_block_invoke: Error requesting transaction: [<CXTransaction 0x14f1eb060 UUID=E1785E55-24CF-49E6-8225-372E7F5F0F9D isComplete=0 actions=( "<CXEndCallAction 0x141d18190 UUID=BBE9EB13-EFF5-49F1-99DC-96A6218C9031 state=0 commitDate=(null) callUUID=7C8A16B2-4CF4-4F66-A588-5A663A6D52D4 dateEnded=(null)>" )>] Error Domain=com.apple.CallKit.error.requesttransaction Code=4 "(null)" Questions : Can CXCallController.request(_:completion:) legitimately fail with unknownCallUUID if CXEndCallAction is issued before the reportNewIncomingCall completion handler has returned — i.e., is there a required ordering/synchronization guarantee we're missing? Are there known conditions (OS version-specific behavior, app suspension/background state, Do Not Disturb / Focus interaction, multiple concurrent CXProviders) under which CallKit's internal call controller can "lose" a UUID that was previously reported, causing a subsequent valid action to be rejected as unknown? Is there a supported way to query CXCallController/CXProvider for the current set of UUIDs it considers "known" at a given time, to diagnose this class of mismatch before issuing an action? Once unknownCallUUID occurs, is there a recommended recovery path to force-clear a stale call from CallKit's UI/state from the app side, short of ending all calls?
Replies
1
Boosts
0
Views
742
Activity
Sep ’26
Live Caller ID Lookup: is there any way to check onboarding status after approval?
We have a Live Caller ID Lookup deployment that has been blocked for weeks, and the core difficulty is not technical. It is that we cannot find out anything about the state of our onboarding submission. Here is where we stand. We submitted the onboarding form and our configuration was approved on 8 August. That approval email is the only communication we have ever received about this feature. Our service is deployed and verified: the OHTTP gateway negotiates HTTP/2, serves a valid key configuration, the issuer directory returns 200 and Apple polls it continuously, the DNS TXT record is in place, and the validation identity is in our corpus. Development builds work end to end and display caller names correctly on incoming calls. App Store builds do not. Every authenticated request fails on the device in roughly 47 ms, before anything leaves the phone: ciphermld(CipherML) requestData(byKeywords:shardIds:clientConfig:) threw NSURLErrorDomain Code=-1009 _NSURLErrorPrivacyProxyFailureKey = true nw_endpoint_proxy_handler_should_use_proxy: "Proxies not present, but required to fail closed" The network path is healthy in the same moment, and other processes on the device do receive proxy configuration. Only our extension's bundle identifier never does. We reproduced this on Wi-Fi and on cellular, and after a device restart. We have since learned, from a maintainer replying to an issue we opened on the pir-service-example repository, that "approved" in the CloudKit console is not the same thing as "successfully onboarded", and that we are still in the first state. That explains the behavior completely. What we cannot explain is how a provider is supposed to discover this. There is no status field in the console, no notification when the state changes, and nothing in the onboarding documentation that mentions a second stage exists. We wrote to Apple several times over these weeks. We were told more than once that an internal team would look into it, and we never received a reply. Apple Support told other providers in the same situation that this is a technical matter beyond their scope. Looking through the pir-service-example issues, this pattern is common. One provider reported waiting three months without a response. Another reported that form to production took almost four months. Another only discovered the feature had been enabled by noticing traffic arriving at their own server. My questions: Is there any supported way to check the onboarding state of a submitted configuration, or to ask about one that appears stuck? If not, is anything planned? Is the transition from "approved" to "successfully onboarded" expected to be automatic, and roughly how long should it take? For anyone who has been through this: did you eventually get a notification, or did you find out by watching your own server logs? We are not asking for our submission to be prioritized. We would simply like to know whether we are waiting on something or whether something needs to be resubmitted, and right now there is no way to tell the difference. Our paying subscribers cannot use the feature, and we have not been able to give them an accurate answer either. Thank you.
Replies
2
Boosts
0
Views
719
Activity
Sep ’26
Live Caller ID Lookup: App Store build fails with "Proxies not present, but required to fail closed" while development build works
Summary Our Live Caller ID Lookup deployment works end-to-end from an Xcode-signed build but fails on the App Store build of the same app, on the same device, same network, same Apple ID. On the production build every authenticated request fails locally in ~47 ms, before any packet leaves the device. Our Identity & Trust configuration in CloudKit Console has read "Approved" since 2026-08-08. Device log at the moment of failure nw_endpoint_proxy_handler_should_use_proxy: "Proxies not present, but required to fail closed" ciphermld(CipherML) <Error>: queryStatus(for:options:) threw an error: Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." NSErrorFailingURLKey=https://<our-issuer-host>/token-key-for-user-token _NSURLErrorPrivacyProxyFailureKey=true NSUnderlyingError={NSPOSIXErrorDomain Code=50 "Network is down"} (_NSURLErrorNWPathKey=satisfied, Wi-Fi up, LQM: good) Control experiment: the device's privacy proxy stack is healthy at that exact moment 0.93 seconds before our extension is refused, the same device attaches a proxy to another connection without any problem: 11:51:59.417 mDNSResponder nw_endpoint_proxy_add_config_if_applicable Adding proxy masque Proxy: https://mask.icloud.com:443 11:52:00.337 ciphermld [C15 ... bundle id: <our extension>, proxy fail closed] start 11:52:00.346 ciphermld Proxies not present, but required to fail closed System state in the same capture: Privacy Proxy Service Status: 1 Privacy Proxy User Tier: 1 Privacy Proxy Networks: (<Cellular, Active>, <WiFi, Active>) Counts across the capture: nw_endpoint_proxy_add_config_if_applicable appears twice (mDNSResponder and Safari SafeBrowsing), while Proxies not present appears 16 times, all from ciphermld, all for our extension's bundle id. No connection made on behalf of our extension is ever given a proxy configuration. Reproduced on two networks and after a device restart, with the device charging and Low Power Mode off. Apple's own endpoint already recognises our issuer 11:51:59.405 ciphermld(CipherML) Fetching token issuer directory from https://gateway.icloud.com/pat-issuer-directory?issuer=<our-issuer-host> 11:52:00.332 200, 1511 bytes, 928 ms Our validation Bearer token from the onboarding submission also returns 200 on /token-key-for-user-token when Apple's validator calls it, which we observe on our server several times a day. A second observation that may be the key In apple/pir-service-example issue #139 an Apple engineer stated that once onboarding is complete, "all requests ... will automatically use OHTTP even when it is a development build". We captured our development build today with full device logging. Every one of its connections goes directly to our PIR host over QUIC, with no proxy fail closed stamp, and the words ohttp, oblivious, mask.icloud and our OHTTP gateway hostname appear zero times in the capture: 16 connections, all: Hostname#<our-pir-host>:443, attribution: developer "proxy fail closed": 0 "Proxies not present": 0 If a completed onboarding forces OHTTP even for development builds, then our development build going direct indicates the relay configuration was never distributed for our extension's bundle id, despite the console showing "Approved". Fleet-level data, not a single device On the day of release, 40 distinct premium accounts on the current App Store build fetched their user tier token from our backend. Across that entire day our PIR service saw zero Privacy Pass issuances and zero queries from App Store builds. For contrast, a single development-signed device produced 144 /issue requests in one day earlier in the month. What we already checked and fixed on our side HTTP/2 ALPN negotiates on all three hosts (service, issuer, gateway). The DNS TXT ownership record is in place. The OHTTP gateway serves a valid key config (X25519 / HKDF-SHA256 / AES-128-GCM). The PIR use case names match the required <extension bundle id>.block / .identity format exactly. The validation identity +1 408 555 1212 "Johnny Appleseed" is present in the live corpus. We found and fixed a real defect of our own: our OHTTP gateway's allowed target origin list was missing the issuer host. We corrected it and restarted the gateway (its HPKE key config was byte-identical afterwards, so no key rotation occurred). The failure reproduced with an identical signature seven minutes later, and the device log confirms these hops never touch our gateway anyway. Questions Is relay / privacy proxy provisioning for a production bundle id a separate step after the Identity & Trust configuration is approved, and what is the expected propagation time? Is there any way for a developer to check provisioning status? Right now an "Approved" badge and a completely non-functional production build look identical from our side. Is there a supported way to exercise the production privacy proxy path before shipping to the App Store? Development builds bypass it by design, so the first real test of the production path was made by paying customers. This seems likely to affect other adopters. Is provisioning keyed on the extension bundle id, the app bundle id, or the team id? Other system consumers on the same device receive proxy configurations normally. We accidentally submitted two identical configurations three minutes apart on 2026-08-08 and both show "Approved". Can a duplicate block or confuse provisioning, and should one be revoked? Environment: iPhone 17 Pro Max, iOS 26.6.1, Xcode 26.5 (17F42). App Store build version 14 (36). Feedback Assistant report with full device logs: FB24501570 GitHub issue with the same detail: https://github.com/apple/pir-service-example/issues/162 Configuration ID: 693155e2-b8ba-4b47-9b6c-64fd97677386 and a1959377-8069-4225-87b9-c1ff81016611
Replies
2
Boosts
0
Views
493
Activity
Sep ’26
provider(_:didActivate:) callback intermittently not triggered, causing widespread audio loss for users
Hi everyone, I am facing a critical issue where the CallKit provider delegate method provider(_:didActivate:) is intermittently not triggered. This occasionally results in a total loss of audio during some VoIP calls, while other calls work perfectly fine. Here is the sequence of steps I am currently implementing: Report Incoming Call: The app receives a VoIP push notification and reports the call using reportNewIncomingCall(with:update:completion:). Answer Action: The user taps the answer button, and the app processes the CXAnswerCallAction. Configure Audio Session: Inside the provider delegate, I configure the AVAudioSession category and mode (e.g., setting category to .playAndRecord and mode to .voiceChat). Note: As per Apple's guidelines, I do not call setActive(true) manually, expecting CallKit to activate it automatically. Despite following this standard flow, there are times when provider(_:didActivate:) is skipped entirely, meaning the audio engine fails to initialize for that specific call session. We are currently receiving a large volume of user complaints regarding this issue, as it heavily impacts the core calling experience in production. Could an Apple engineer or anyone from the community look into this? Any insights into what might be causing CallKit to occasionally fail to activate the audio session or how to work around this would be highly appreciated. Thank you!
Replies
5
Boosts
0
Views
1.3k
Activity
Aug ’26
Callkit returns error code=106 which is not on official document
Callkit returns error code=106. I can't find what this error code means in official documents. What is this error code? Has anybody seen this error code so far? My app is VoIP app using APNs VoIP push. Below is log example of my app. 2026/04/26 09:01:45.752 [436:103]CallManager incomingCall(::::_:completion:):228: error Error Domain=com.apple.CallKit.error.incomingcall Code=106 "(null)"
Replies
1
Boosts
0
Views
543
Activity
Aug ’26
Live Caller ID Lookup Extension - No option in phone settings
iOS 18 (22A3354) will not offer a option in settings (> Apps > Phone) after calling openSettings to enable live caller id lookup extension. iPhone and MacBook are in the same network. The PIRService runs on MacBook and is reachable via iPhone Safari (via http://MacBookPro:8080/). Hummingbird print log: hb_method=GET hb_uri=/ [Hummingbird] Request. After deploying the application via Xcode to the iPhone no requests are printed in the terminal. The extension was added like documented and bundle id is also checked multiple times. issuerRequestUri in service-config.json is http://MacBookPro:8080/issue. As far as I can tell, everything has been set up in accordance with the Testing Live Caller ID instructions. Is there something missing?
Replies
10
Boosts
0
Views
2.5k
Activity
Aug ’26
Live Caller ID Lookup request stuck in review - how to verify our endpoints pass validation?
We submitted Live Caller ID Lookup requests for two apps and both remain “In Review.” Our PIR server, Privacy Pass issuer and OHTTP gateway are deployed, DNS TXT records are published for both bundle identifiers, and the test number returns correctly. Is there a way to verify from our side that Apple’s automated endpoint validation passes? Any common misconfiguration that causes a request to sit without feedback?
Replies
0
Boosts
0
Views
704
Activity
Aug ’26
How to collect statistics for Message Filter Extension and Call Blocking Extension?
Hi everyone, I'm developing an iOS app that includes both a Message Filter Extension and a Call Blocking Extension. I'd like to understand whether there is any supported way to collect or access statistics for these extensions, such as: Number of messages filtered (allowed vs. junk) Number of blocked calls Number of identified calls Extension invocation count Filtering success/failure metrics Any analytics or usage data exposed by the system I understand that these extensions are privacy-sensitive, so I'm not expecting access to user content. I'm specifically looking for aggregate statistics or system-provided metrics that an app can legally and technically access. My questions are: Does iOS expose any APIs or callbacks for collecting usage statistics from a Message Filter Extension? Is there any way to determine how many calls were blocked or identified by a Call Blocking Extension? Are there any recommended Apple-supported approaches for tracking extension usage without violating user privacy? If no APIs are available, is this limitation intentional due to the privacy model of these extensions? I'd appreciate any guidance or best practices from Apple engineers or developers who have worked with these extensions. Thank you!
Replies
4
Boosts
0
Views
1.3k
Activity
Jul ’26
How should apps handle deprecated INStartAudioCallIntentIdentifier and INStartVideoCallIntentIdentifier from Recents?
Hello, I am currently developing call-related features for our app, and I have a question regarding one of the APIs. When the app is launched from the Recents list by selecting a recent call, the activityType of the userActivity is provided as either INStartAudioCallIntentIdentifier or INStartVideoCallIntentIdentifier. However, I understand that these identifiers have been deprecated since iOS 13, and the documentation recommends using INStartCallIntentIdentifier instead. The issue is that when the app is launched from the Recents list, INStartCallIntentIdentifier is never provided. Instead, the deprecated identifiers (INStartAudioCallIntentIdentifier and INStartVideoCallIntentIdentifier) continue to be delivered. I have reviewed the available documentation, but it is not clear how developers are expected to handle this situation. Could you please advise on the recommended approach for supporting this flow? Is it expected that applications continue to handle the deprecated identifiers in this case, or is there another recommended implementation? I would greatly appreciate any guidance you can provide. Thank you.
Replies
0
Boosts
0
Views
631
Activity
Jul ’26
iOS 26.5: Xfinity Mobile 'Spam' call filtering not silencing calls / not sending to voicemail (CallKit bug)
On my iPhone (iOS 26.5), the Call Filtering "Spam" setting under Settings → Apps → Phone isn't working as described. The setting's own text says calls identified as spam or fraud by Xfinity Mobile will be silenced, sent to voicemail, and moved to the Spam list, but in practice these calls ring through normally with no silencing at all, even though the toggle is enabled and Unknown Callers is off. This looks related to a broader CallKit regression that's been reported on Apple's Developer Forums since the iOS 26 beta cycle (threads 794740, 799694, and 800415), where call-blocking and filtering entries are being ignored or applied inconsistently; other developers have already filed Feedback IDs FB19140680, FB19140594, and FB20276986 for related symptoms. It would be great to get this fixed, since it defeats the purpose of carrier-level spam filtering on the phone.
Replies
0
Boosts
0
Views
548
Activity
Jul ’26
AVRoutePickerView's presented route list is automatically dismissed when CXProvider reports the call as connected (`reportOutgoingCall(with:connectedAt:)`)
Hi, I want my user to be able to change their audio output device while calling someone, even before the call is established. But I've run into an issue combining CallKit and AVRoutePickerView. On iOS 26 (Xcode 26.5, tested on iPhone 13 Mini): when my AVRoutePickerView's route list is presented while my call is still connecting (not yet established), the presented list is automatically dismissed the moment the app reports the call as connected via CXProvider.reportOutgoingCall(with:connectedAt:). That doesn't seem natural to me — it should stay open, since the user is actively interacting with unrelated system UI at that moment. This happens even when nothing about the AVAudioSession itself changes: no category change, no route change, no didActivate/didDeactivate callback fires around that time. The dismissal is caused by the CallKit "connected" state transition itself, but I don't know why, or whether it's intentional. Steps to reproduce (sample project below): Tap "Start Fake Call". Within 5 seconds, tap the small route picker button and leave its list open. Wait — at t+5s the app reports the call connected via reportOutgoingCall(connectedAt:), and the list closes itself right after. Is this normal? And is there an official, CallKit-sanctioned way to report a call's connected state without this side effect — some way to keep the route picker stable across that transition? The two workarounds I've found both have real downsides: Calling reportOutgoingCall(connectedAt:) at the very start of the call, before the callee has actually answered , but then CallKit no longer reflects the real call state (Recents/duration would include ringing time, and calls that are never answered would still show as "connected"). Disabling audio device selection entirely until the call is established... works, but hurts the user experience, since users may want to switch output before the call connects. Here's the minimal reproduction: import CallKit import SwiftUI struct ContentView: View { @StateObject private var demo = CallKitDemo() var body: some View { VStack(spacing: 24) { Text(demo.statusText) .font(.system(.body, design: .monospaced)) .multilineTextAlignment(.center) Button("Start Fake Call") { demo.startFakeCall() } .disabled(demo.isCallInProgress) RoutePickerView() .frame(width: 60, height: 60) } .padding() } } private struct RoutePickerView: UIViewRepresentable { func makeUIView(context: Context) -> AVRoutePickerView { let picker = AVRoutePickerView() picker.delegate = context.coordinator return picker } func updateUIView(_ uiView: AVRoutePickerView, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator() } final class Coordinator: NSObject, AVRoutePickerViewDelegate { func routePickerViewWillBeginPresentingRoutes(_ routePickerView: AVRoutePickerView) { print("[REPRO]", Date(), "picker WILL present routes") } func routePickerViewDidEndPresentingRoutes(_ routePickerView: AVRoutePickerView) { print("[REPRO]", Date(), "picker DID end presenting routes <- dismissed here") } } } @MainActor final class CallKitDemo: NSObject, ObservableObject { @Published var statusText = "Idle" @Published var isCallInProgress = false private let provider: CXProvider = { let configuration = CXProviderConfiguration() configuration.supportsVideo = false configuration.maximumCallGroups = 1 configuration.maximumCallsPerCallGroup = 1 configuration.supportedHandleTypes = [.generic] return CXProvider(configuration: configuration) }() private let callController = CXCallController() private var currentCallUUID: UUID? override init() { super.init() provider.setDelegate(self, queue: nil) } func startFakeCall() { let uuid = UUID() currentCallUUID = uuid isCallInProgress = true statusText = "Requesting CXStartCallAction..." let handle = CXHandle(type: .generic, value: "repro-call") let startAction = CXStartCallAction(call: uuid, handle: handle) callController.request(CXTransaction(action: startAction)) { error in if let error { print("[REPRO]", Date(), "CXStartCallAction request failed:", error) } } DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in guard let self, currentCallUUID == uuid else { return } print("[REPRO]", Date(), "reportOutgoingCall(connectedAt:)") statusText = "Reported connected at t+5s" provider.reportOutgoingCall(with: uuid, connectedAt: Date()) } DispatchQueue.main.asyncAfter(deadline: .now() + 8) { [weak self] in guard let self, currentCallUUID == uuid else { return } callController.request(CXTransaction(action: CXEndCallAction(call: uuid))) { _ in } currentCallUUID = nil isCallInProgress = false statusText = "Call ended" } } } extension CallKitDemo: CXProviderDelegate { func providerDidReset(_ provider: CXProvider) {} func provider(_ provider: CXProvider, perform action: CXStartCallAction) { action.fulfill() } func provider(_ provider: CXProvider, perform action: CXEndCallAction) { action.fulfill() } func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { print("[REPRO]", Date(), "didActivate") } func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { print("[REPRO]", Date(), "didDeactivate") } } Thanks for your help !
Replies
1
Boosts
0
Views
1.3k
Activity
Jul ’26
CallKit CXCallObserver.calls reports stale `hasEnded` status
Hello Apple Developer Support Team, I am experiencing an issue with the CallKit framework on iOS where the CXCallObserver.calls property does not accurately reflect the active call status in real-time. The Issue: Even when there are no active CallKit calls (all calls have been hung up), accessing callObserver.calls still returns CXCall objects where hasEnded is false. This leads to false positives when we check: callObserver.calls.contains { !$0.hasEnded } Our Setup: We maintain a strong reference to CXCallObserver inside a singleton manager. We have assigned the CXCallObserverDelegate and it is receiving events. We noticed a significant delay (sometimes permanently on simulator/certain devices) between the call actually ending on the system level and the calls array being cleared. Questions: Is the CXCallObserver.calls array designed to be an asynchronous snapshot, or should it guarantee real-time consistency with the actual system call state? Is this a known caching issue with callservicesd where ended calls are not immediately deallocated from the observer’s calls array? What is the recommended best practice for determining if there is an active call on the device without relying on the potentially stale callObserver.calls property? Thank you for your support, and I look forward to your guidance. Best regards,
Replies
1
Boosts
0
Views
803
Activity
Jul ’26