Skip to content

Astradial iOS App (astradial-ios)

Fork of BelledonneCommunications/linphone-iphone (Linphone 6.x, full-SwiftUI rewrite). We keep the engine and the iOS-platform plumbing; we replace the UI with a native iOS Phone-app clone per the iOS UI Spec.

  • Local checkout: ~/AstradialDevelopment/Mobile App/astradial-ios
  • Cloned: June 2026 from upstream master
  • Upstream remote: keep origin pointed at our fork once created; add upstream as upstream to pull SDK/feature updates: git remote add upstream https://github.com/BelledonneCommunications/linphone-iphone

What the codebase gives us

Linphone 6 is a 2024+ ground-up rewrite: SwiftUI throughout, and the SIP engine consumed as a Swift Package Manager dependency (linphone-sdk-swift-ios from gitlab.linphone.org/BC/public/linphone-sdk-swift-ios.git). No CocoaPods, no submodule SDK builds — open LinphoneApp.xcodeproj in Xcode, SPM resolves the SDK, build and run.

Source map (what to keep vs. replace)

Path What it is Astradial action
Linphone/Core/ (CoreContext.swift, CorePreferences.swift) liblinphone Core lifecycle, config, iterate loop Keep — this is the engine binding
Linphone/TelecomManager/ (TelecomManager.swift, ProviderDelegate.swift, EarlyPushkitDelegate.swift) CallKit provider + PushKit VoIP push handling Keep — hardest iOS plumbing, already solved
Linphone/Contacts/ContactsManager.swift CNContactStore ↔ Linphone Friend bridge Keep, extend for stock-Contacts UI
Linphone/UI/Main/History/ Call history (Recents) views + view models Keep view models, replace views with stock-Recents UI
Linphone/UI/Main/Contacts/ Contacts views + view models Keep view models, replace views
Linphone/UI/Call/ (CallView.swift, fragments, view models) In-call UI Keep logic; restyle per spec (CallKit native screen does most of the work)
Linphone/UI/Main/ContentView.swift Root tab/navigation structure Replace with the 5-tab Phone-app TabView (Favourites, Recents, Contacts, Keypad, Voicemail)
Linphone/UI/Assistant/, UI/Welcome/ Linphone onboarding/assistant Replace with Astradial QR provisioning + manual SIP entry
Linphone/UI/Main/Conversations/, Meetings/, Recordings/, Help/, Settings/ Chat, meetings, etc. Hide/remove from UI for v1 (engine support stays)
Linphone/Assets.xcassets, Fonts/, branding, GoogleService-Info.plist Linphone branding, Firebase config Replace with Astradial branding; our own push/Firebase config
msgNotificationService/, linphoneExtension/, intentsExtension/ Notification service + share + Siri intents extensions Review per-extension; keep what calling needs
*.lproj/, Linphone/Localizable/ Translations English only for v1; prune later

Build prerequisites

  • Mac with Xcode (15+), Apple developer account for device builds (CallKit/PushKit do not work in the simulator for real calls).
  • Open LinphoneApp.xcodeproj, let SPM resolve linphone-sdk-swift-ios, select a team, build to a device.
  • A local SDK build is possible (see upstream README.md, "Using a local Linphone SDK") but not needed unless we patch the SDK.

First build — verified June 2026 (what it took)

Verified on Xcode 26.5 (17F42), scheme Linphone, iPhone 17 Pro simulator (iOS 26.5). App builds, installs, launches to the Linphone welcome screen. Three gotchas, all fixed on the astradial branch (commit 1f3e740):

  1. Upstream's SDK pin was broken. The project tracked the beta branch of linphone-sdk-swift-ios, whose binary XCFrameworks live under download.linphone.org/snapshots/… — Belledonne purges those, so all 17 zips 404'd and package resolution failed. Fix: point the package requirement at the stable branch (→ SDK 5.5.1 release artifacts, which persist). If resolution ever 404s again after an upstream merge, check which branch the pbxproj pins.
  2. One API mismatch. App code on master was written against the beta SDK's renamed Conference.isMe(uri:); stable 5.5.1 expects isMe(URI:). 10 call sites in CallViewModel.swift.
  3. Don't build with CODE_SIGNING_ALLOWED=NO. It strips the app-group entitlement (group.belledonne-communications.linphone), liblinphone then can't create its data directory and the app exits at startup ("Create data path directory error"). Default simulator signing (no team needed) works.

Useful CLI (when xcode-select points at CommandLineTools, prefix with DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer):

xcodebuild -project LinphoneApp.xcodeproj -scheme Linphone \
  -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 17 Pro' build

Download note: the SDK + Firebase binaries are ~1.3 GB on first resolve; linphone.xcframework and mediastreamer2.xcframework are the big ones and can time out — just re-run, completed downloads are cached.

App flow (implemented June 2026)

Onboarding (replaces Linphone Welcome/Assistant): [Sign In | Skip login] → Firebase email/password (same auth as the web dashboard) → native "Connect Your Line" SIP step with QR scan (editor's Zoiper-style payload) or manual entry; signed-in owners land on the Analytics (Pulse) tab. Main UI: Analytics · Recents · Contacts · Keypad · Tickets (open-count badge on tab + app icon). In-call: native stock-Phone screen (NativeCallUI.swift) — mute / DTMF pad / speaker / hold / record, replacing Linphone's CallView. Display name is now Astradial (bundle id still org.linphone.phone until the Apple-portal rebrand, task #3).

Rebrand checklist (before any TestFlight build)

  1. New bundle identifier (com.astradial.phone — decide and register in the Apple Developer portal) + app name, icon, splash.
  2. Replace GoogleService-Info.plist with an Astradial Firebase project (or drop Firebase if only used for Linphone's push/crash).
  3. Entitlements: our App Group, our Push/VoIP certificates.
  4. Strip Linphone default account creation (their assistant points at sip.linphone.org and Flexisip account services).
  5. Default linphonerc/CorePreferences pointed at Astradial: transports, codecs (opus/PCMU/PCMA to match our Asterisk), STUN/TURN if needed.

Astradial integration

Same model as the previous softphone (the server side already exists):

  • SIP: register to devsip.astradial.com:5060 (prod) / stagesip.astradial.com:5080 (staging) over UDP/TCP/TLS with the user's asterisk_endpoint + secret. No WebRTC/WSS requirement — liblinphone is a classic SIP UA, so the Flutter-era webrtc=yes/admin-only restriction does not apply.
  • Provisioning: scan the editor's SIP QR (Users → SIP icon; format documented in Softphone App → QR provisioning). Parse the Zoiper-style payload, create the Linphone Account from it. liblinphone also supports remote provisioning via a config URL — consider serving a per-user linphonerc from the API later.

Open questions

# Question Notes
1 VoIP push for inbound calls when app is killed. iOS kills background SIP registrations. Linphone solves this with Flexisip proxy sending PushKit pushes. Our Asterisk does not push. Options: (a) run a push gateway that subscribes/forks INVITEs to APNs VoIP push, (b) Flexisip in front of Asterisk, (c) v1 ships foreground/recent-background calling only. Must be decided before v1 "always reachable" claims.
2 GPLv3 vs commercial licence. linphone-iphone app code is GPLv3; SDK is dual-licensed. Closed-source App Store distribution needs either GPL compliance (publish our source) or a paid Belledonne licence. Business decision — track before submission.
3 Visual voicemail backend. Stock-style Voicemail tab needs message list + audio from Asterisk app_voicemail. No API for this today. v1 fallback: native-looking empty state + "Call Voicemail" feature-code dial.
4 Per-user push credentials / Firebase. Upstream ships Linphone's GoogleService-Info.plist; we must not ship it. Decide our crash-reporting + push stack.

Suggested branch strategy

  • master tracks upstream (pull from upstream/master to take SDK bumps).
  • astradial is our main development branch; all UI replacement work lands there. Rebases onto upstream are deliberate, occasional, and tested — upstream moves fast and touches the same UI files we are rewriting, so prefer cherry-picking engine-relevant fixes over blind merges.

Milestones (v1)

  1. M0 — builds & registers: rebranded shell builds on device, registers against staging via QR provisioning, can place/receive a call with CallKit (current Linphone UI still visible underneath is acceptable here).
  2. M1 — Keypad + outbound: stock-Phone tab bar + Keypad screen per UI spec, outbound calls.
  3. M2 — Recents + Contacts: call history and address-book screens per spec, info/detail pages, tap-to-call.
  4. M3 — Favourites + in-call polish: favourites CRUD, in-call screen parity, multi-call/transfer/DTMF verified against Asterisk (on-call features).
  5. M4 — Voicemail tab + push decision implemented, TestFlight beta to internal users.

Acceptance bar for every UI milestone: the side-by-side test in iOS UI Spec → Acceptance test.