How psiphon-inc/psiphon-ios-vpn works
1. Overall shape
Psiphon iOS VPN is a mixed Swift/Objective-C iOS codebase built around three runtime pieces: the main app, a NetworkExtension packet-tunnel target, and shared libraries/data models used by both. The app is a VPN client UI plus subscription/PsiCash commerce flows; the extension is the actual tunnel process.
2. Main application target
The Psiphon/ directory contains the app shell, screens, reducers, custom views, and launch/onboarding flows. AppDelegate, SwiftDelegate, MainViewController, RootContainerController, and the ViewControllers/ tree drive the user-facing navigation; View/ and ViewControllers/Views/ hold the custom animated UI used on the home, region, onboarding, subscription, and PsiCash screens.
3. State and logic layer
The app’s behavior is organized around reducer/store-style state management rather than ad hoc view logic. You can see this in MainViewReducer, VPNStateReducer, PsiCashReducer, AppReceiptReducer, FeedbackReducer, and the PsiApi/Sources/PsiApi/Store/ package (Store, Effect, SerialEffect, MainState). UI reacts to state changes through ReactiveObjC and custom reactive helpers.
4. Shared domain package
PsiApi/ is a Swift package that packages reusable domain logic: HTTP request modeling, localization, logging, IPC notifications, platform abstractions, and a shared store architecture. It also contains the reusable App Store IAP and PsiCash client models, so the app target is not the only consumer of purchase and status logic.
5. VPN tunnel boundary
PsiphonVPN/ is the packet tunnel extension target. PacketTunnelProvider and BasePacketTunnelProvider are the entrypoint for the OS-managed VPN process, and they bridge to the prebuilt PsiphonTunnel.xcframework, which contains the platform-specific tunnel engine and reachability/default-route helpers.
6. Host app ↔ extension communication
The host app and tunnel extension share state through app-group-backed storage and notification plumbing rather than direct in-process calls. Shared Core Data (Shared/CoreData/SharedModel.xcdatamodeld and PsiApi/Sources/.../SharedCoreData) plus keyed stores in PsiphonVPN/LocalDatastore and shared NSUserDefaults wrappers carry authorization, configuration, and status between processes; Shared/Notifier and IPC notification helpers provide signaling.
7. Configuration and embedded runtime inputs
The tunnel process depends on static configuration artifacts checked into the repo: Shared/psiphon_config.stub and Shared/embedded_server_entries.stub are runtime inputs for network parameters and server discovery. The app also bundles Psiphon/InAppSettings.bundle for user-configurable settings and reads localized strings from Shared/Strings/ and StoreAssets/.
8. Commerce and monetization flows
The repo has two distinct payment-related subsystems. PsiApi/Sources/AppStoreIAP/ manages App Store receipt/payment queue/subscription validation, while Psiphon/PsiCash/ plus PsiApi/Sources/PsiCashClient/ wrap the PsiCashLib.xcframework binary for PsiCash account/balance/speed-boost purchasing. UI for these flows lives in dedicated view controllers and PsiCash-specific views.
9. Shared services and utilities
Shared/ contains cross-cutting infrastructure used by both app and extension: logging, profiling, jetsam tracking, file utilities, rotating log files, localization helpers, and app metadata wrappers. The asn1c/ sources are included for receipt parsing, and Shared/External/c-timestamp plus Utilities/ provide common low-level primitives.
10. Dependencies, packaging, and build tooling
The project mixes source dependencies and prebuilt binaries: CocoaPods brings in ReactiveObjC, InAppSettingsKit, MBProgressHUD, and EFCountingLabel, while PsiphonTunnel.xcframework and Frameworks/PsiCashLib.xcframework are vendored binaries. Build/release automation is handled by build.sh, Fastlane, GitHub Actions, localization scripts, and several SwiftPM utility/dev tools under Utilities/, Testing/, and dev/.
Main entry points: MainViewReducer.swift App state · MainViewController.m Main flow · PacketTunnelProvider.m VPN extension · ExtensionDataStore.m Extension store
Components 19
Main app
Host iOS client UI, onboarding, settings, and commerce flows.
- App entry · Application startup and root app wiring.
Psiphon/AppDelegate.m - Swift delegate · Swift-side app coordination and lifecycle helpers.
Psiphon/SwiftDelegate.swift - Root UI · Primary container and navigation controllers for the app.
Psiphon/ViewControllers/RootContainerController.m - Main flow · Main screen, onboarding, launch, and deep-link routing.
Psiphon/ViewControllers/MainViewController.m - Custom views · Animated buttons, status widgets, and reusable presentation views.
Psiphon/View/AnimatedUIButton.swift - Subscriptions · App Store subscription and receipt-facing user flows.
Psiphon/SubscriptionViewController.m - PsiCash UI · PsiCash account, balance, and purchase screens.
Psiphon/PsiCash/PsiCashReducer.swift - App state · Central reducers and state for VPN, main screen, feedback, and commerce.
Psiphon/MainViewReducer.swift
Shared code
Cross-target models, storage, logging, localization, and utilities.
- Shared core · Cross-process data models, shared defaults, logging, profiling, and notifications.
Shared/Notifier.swift - Shared Core Data · Shared authorization and persisted state used by host app and extension.
Shared/CoreData/SharedModel.xcdatamodeld/SharedModel.xcdatamodel/contents - Strings · Localized strings and app/store asset text bundles.
Shared/Strings/en.lproj/Localizable.strings - Runtime config · Static Psiphon config and embedded server entries consumed at runtime.
Shared/psiphon_config.stub
VPN target
Packet-tunnel extension and host-extension data bridge.
- VPN extension · NetworkExtension packet-tunnel entrypoint and extension-side logic.
PsiphonVPN/PacketTunnelProvider.m - Extension store · Keyed storage, authorization, and notification bridge inside the extension.
PsiphonVPN/LocalDatastore/ExtensionDataStore.m
Swift packages
Standalone reusable packages and development tools.
- Store core · Reducer/effect store architecture shared by app logic and domain code.
PsiApi/Sources/PsiApi/Store/Store.swift - App Store IAP · Receipt parsing, subscription verification, and payment queue logic.
PsiApi/Sources/AppStoreIAP/IAPReducer.swift - PsiCash client · Shared PsiCash models, state, and effects for commerce flows.
PsiApi/Sources/PsiCashClient/PsiCashState.swift
Vendored deps
Prebuilt binaries and third-party libraries integrated into the repo.
- Tunnel engine · Prebuilt tunnel runtime and reachability/default-route helpers.
PsiphonTunnel.xcframework/ios-arm64/PsiphonTunnel.framework/PsiphonTunnel - PsiCash lib · Binary PsiCash client library used for balance and purchase operations.
Frameworks/PsiCashLib.xcframework/ios-arm64_armv7/PsiCashLib.framework/PsiCashLib
Connections 22
- App entry → Swift delegate: hands off to · Startup delegates split between Obj-C and Swift.
- App entry → Root UI: roots · Creates the main container and navigation shell.
- Root UI → Main flow: hosts · Primary container presents launch, onboarding, and main screens.
- Main flow → Custom views: renders with · Uses custom animated and reusable UI components.
- Main flow → App state: drives · UI behavior is driven by reducer/state updates.
- App state → Store core: built on · App state follows the shared store/effect architecture.
- App state → App Store IAP: consumes · Subscription and receipt logic flow through shared IAP domain code.
- App state → PsiCash client: consumes · PsiCash state and effect models back the commerce UI.
- Subscriptions → App Store IAP: uses · Subscription screens rely on receipt and purchase verification logic.
- PsiCash UI → PsiCash client: uses · PsiCash screens bind to shared client models and actions.
- PsiCash UI → PsiCash lib: bridges to · Binary PsiCash library handles account and purchase operations.
- Shared core → Shared Core Data: persists · Shared authorization and status data are stored in app-group Core Data.
- VPN extension → Extension store: reads/writes · Extension process stores shared status and authorization values.
- VPN extension → Tunnel engine: drives · Packet tunnel provider delegates core VPN work to the vendored engine.
- Extension store → Shared Core Data: shares · Extension storage interoperates with the shared Core Data model.
- Extension store → Shared core: signals via · Uses shared notifier/IPC helpers to communicate with the host app.
- Main flow → Shared core: consumes · App screens and reducers rely on shared logging, defaults, and IPC helpers.
- App state → Runtime config: reads · State and tunnel setup use embedded config and server entries.
- VPN extension → Runtime config: reads · Tunnel startup depends on static runtime configuration inputs.
- App Store IAP → Shared Core Data: shares · Subscription authorization uses the same shared persistent backing.
- PsiCash client → PsiCash lib: wraps · Shared PsiCash domain code wraps the vendored binary library.
- Strings → Main flow: localizes · Localized resources feed the app UI and store assets.