VoIP calling with CallKit
At a glance
| Item | Summary |
|---|---|
| Purpose | Use the CallKit framework to integrate native VoIP calling. |
| App architecture | A C++, C/Objective-C header, Objective-C++, Swift sample bundle with entry-bearing project variants Speakerbox-Watch, Speakerbox, each leading to CallKit APIs. |
| Main patterns | View-controller organization, Protocol-oriented abstraction, Delegate or data-source callbacks, SwiftUI environment injection |
| Project style | 28 scanned source file(s) across C++, C/Objective-C header, Objective-C++, Swift, organized around ranked entry, type, and file boundaries. |
Project structure
Source bundle/
├── Speakerbox-Watch/
│ ├── Speakerbox_WatchApp.swift
│ ├── ContentView.swift
│ └── PushRegistryDelegate.swift
├── Speakerbox/
│ ├── AppDelegate.swift
│ ├── AudioController.mm
│ ├── NewCallView.swift
│ ├── AudioController.h
│ ├── CallView.swift
│ ├── CallsListView.swift
│ ├── EmptyCallsView.swift
│ └── MainView.swift
└── IntentsExtension/
└── IntentHandler.swift
Structure observations
- Architecturally prominent files are ranked from entry points and role-named declarations; resource-only paths are omitted.
- Primary languages: C++, C/Objective-C header, Objective-C++, Swift.
- The verified tree contains 8 project/configuration file(s) and 22 source declaration(s).
Overall architecture
flowchart LR
Bundle["Sample bundle"]
V1["Speakerbox-Watch"]
V2["Speakerbox"]
Boundary["CallKit APIs"]
Bundle --> V1
V1 --> Boundary
Bundle --> V2
V2 --> Boundary
Reference code
Speakerbox-Watch/Speakerbox_WatchApp.swift:11 — architecture anchor
@main
struct SpeakerboxWatchApp: App {
// ...
}Interpretation
The branches represent separate entry-bearing project variants in the downloaded bundle, not runtime calls between those variants. Each branch is intentionally collapsed at the documented framework boundary; the detailed target-local flow remains in the cited files. Ownership is claimed only where the next section cites a stored property or assignment.
Ownership and state
classDiagram
SpeakerboxWatchApp *-- ProviderDelegate : callProvider
ContentView o-- Callback : incomingCallback
AppDelegate *-- PKPushRegistry : pushRegistry
AppDelegate *-- SpeakerboxCallManager : callManager
Ownership evidence
Speakerbox-Watch/Speakerbox_WatchApp.swift:14 — stored dependency or nearest verified ownership anchor
@main
struct SpeakerboxWatchApp: App {
// ...
var callProvider = ProviderDelegate(callManager: SpeakerboxCallManager())
// ...
}Speakerbox/AppDelegate.swift:18 — iOS target dependencies
let pushRegistry = PKPushRegistry(queue: .main)
let callManager = SpeakerboxCallManager()
var providerDelegate: ProviderDelegate?| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
SpeakerboxWatchApp |
ProviderDelegate (callProvider) |
creates and retains | App/module collaborators |
ContentView |
Callback (incomingCallback) |
stores a callback | App/module collaborators |
AppDelegate |
PKPushRegistry (pushRegistry) |
creates and retains | AppDelegate initializes the immutable binding and configures its delegate at launch. |
AppDelegate |
SpeakerboxCallManager (callManager) |
creates and retains | AppDelegate initializes the immutable binding and forwards call actions to it. |
Composition arrows indicate a source-visible construction expression or locally owned value state; aggregation means the owner stores or receives a dependency without proving exclusive lifetime ownership.
Class and protocol design
Speakerbox/StartCallConvertible.swift:8 — representative type boundary
protocol StartCallConvertible {
var startCallHandle: String? { get }
var video: Bool? { get }
}| Type | Responsibility | Depends on or conforms to |
|---|---|---|
SpeakerboxWatchApp |
Application entry and top-level composition | App |
AppDelegate |
Receives callback-driven events | UIResponder, UIApplicationDelegate |
ContentView |
User-interface presentation and input forwarding | View |
NewCallView |
User-interface presentation and input forwarding | View |
IntentHandler |
Handles callbacks or feature events | INExtension, INStartCallIntentHandling |
PushRegistryDelegate |
Receives callback-driven events | NSObject |
AudioController |
View lifecycle, callbacks, and feature coordination | NSObject |
CallView |
User-interface presentation and input forwarding | View |
CallsListView |
User-interface presentation and input forwarding | View |
EmptyCallsView |
User-interface presentation and input forwarding | View |
The source explicitly defines local protocol relationships: NSUserActivity → StartCallConvertible, URL → StartCallConvertible.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
pushRegistry (Speakerbox-Watch/PushRegistryDelegate.swift:13) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
phase (Speakerbox-Watch/Speakerbox_WatchApp.swift:13) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
application (Speakerbox/AppDelegate.swift:44) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: hide an implementation step that is not part of the collaboration surface. |
muteAudio (Speakerbox/AudioController.h:12) |
header-visible |
The declaration is exposed to translation units that import the header. | Inference: declare a contract needed by other Objective-C/C translation units. |
Reference code
Speakerbox-Watch/PushRegistryDelegate.swift:13 — representative boundary
private let pushRegistry = PKPushRegistry(queue: DispatchQueue.main)Swift declarations without a modifier are internal; explicit private, fileprivate, private(set), public, or open entries above are interpreted by language semantics. Objective-C/C samples instead rely on header and implementation boundaries, which are not equivalent to Swift lexical privacy.
Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application entry and top-level composition | SpeakerboxWatchApp |
The source’s App suffix makes this role explicit. |
| View lifecycle, callbacks, and feature coordination | AudioController |
The source’s Controller suffix makes this role explicit. |
| Receives callback-driven events | AppDelegate, ProviderDelegate, PushRegistryDelegate, SceneDelegate |
The source’s Delegate suffix makes this role explicit. |
| Handles callbacks or feature events | IntentHandler |
The source’s Handler suffix makes this role explicit. |
| Long-lived feature or framework coordination | SpeakerboxCallManager |
The source’s Manager suffix makes this role explicit. |
| User-interface presentation and input forwarding | CallView, CallsListView, ContentView, EmptyCallsView |
The source’s View suffix makes this role explicit. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| View-controller organization | Speakerbox/AudioController.h:10 |
A controller is the verified coordination boundary; this is MVC-style only where a separate model is present. |
| Protocol-oriented abstraction | Speakerbox/NSUserActivity+StartCallConvertible.swift:11 |
A local protocol and concrete conformance create an explicit capability boundary. |
| Delegate or data-source callbacks | Speakerbox/AppDelegate.swift:12 |
Callback protocols invert event delivery back into the sample’s owner. |
| SwiftUI environment injection | Speakerbox/CallView.swift:24 |
The environment supplies state or a capability without threading it through every initializer. |
Naming conventions
- Types: App: SpeakerboxWatchApp; Controller: AudioController; Delegate: AppDelegate, ProviderDelegate, PushRegistryDelegate, SceneDelegate; Handler: IntentHandler; Manager: SpeakerboxCallManager; View: CallView, CallsListView, ContentView, EmptyCallsView, MainView.
- Protocols:
StartCallConvertible. - Methods:
application,pushRegistry,displayIncomingCall,setupAudioSession,setupIOUnit,setupAudioChain,init,handleInterruption. - Files:
Speakerbox-Watch/ContentView.swift,Speakerbox/AppDelegate.swift,Speakerbox/AudioController.mm,Speakerbox/NewCallView.swift,IntentsExtension/IntentHandler.swift,Speakerbox-Watch/PushRegistryDelegate.swift.
Architecture takeaways
Speakerbox_WatchAppis the main source-visible entry or composition anchor for this sample.- Framework work reaches SwiftUI, UIKit, AVFoundation, CallKit through a deliberately small high-level chain; the detailed API graph remains inside the cited implementation files.
- Stored-property evidence identifies lifecycle collaboration; it does not by itself prove exclusive object ownership.
- Access-control conclusions separate verified language visibility from the likely design rationale.
- Local protocol relationships provide an explicit substitution boundary.
Source map
| Source file | Relevant symbols |
|---|---|
Speakerbox-Watch/Speakerbox_WatchApp.swift |
SpeakerboxWatchApp |
Speakerbox-Watch/ContentView.swift |
ContentView, ContentView_Previews |
Speakerbox/AppDelegate.swift |
AppDelegate |
Speakerbox/AudioController.mm |
AudioController, AudioController |
Speakerbox/NewCallView.swift |
NewCallView, NewCallDetails |
IntentsExtension/IntentHandler.swift |
IntentHandler |
Speakerbox-Watch/PushRegistryDelegate.swift |
PushRegistryDelegate |
Speakerbox/AudioController.h |
AudioController |
Speakerbox/CallView.swift |
CallView |
Speakerbox/CallsListView.swift |
CallsListView |