# iOS Telegram Client Roadmap ## Summary Build a full native iOS app on top of the new `tele-core` crate, keeping Telegram/TDLib behavior shared in Rust and implementing UI/platform concerns in SwiftUI. Target feature parity with the current TUI client: auth, chat list, folders, messages, send/edit/delete/reply/forward/copy, reactions, search, pinned/profile flows, photos/albums, voice playback, notifications, multi-account support, unread/muted/mentions, drafts, network/typing state. Architecture default: Rust `tele-core` remains the source of Telegram state and operations; add a UniFFI-based bridge crate plus a SwiftUI app. Do not port `ratatui`, terminal image rendering, `ffplay`, desktop notifications, or TUI keybindings to iOS. ## Phases ### Phase 0: Stabilize Current Workspace - Finish and commit the current split: `tele-core` + `tele-tui`. - Confirm `tele-core` has no TUI/desktop deps: no `ratatui`, `crossterm`, `ratatui-image`, `notify-rust`, `arboard`, `open`, `ffplay`. - Save this roadmap at `docs/superpowers/plans/2026-05-20-ios-app-roadmap.md`. - Acceptance: - `cargo fmt -- --check` - `cargo check -p tele-core` - `cargo test -p tele-core` - `cargo check -p tele-tui --all-targets --all-features` - `cargo test --workspace --all-features` ### Phase 1: iOS-Facing Core API - Add a platform-neutral session layer in `tele-core` above `TdClientTrait`. - Expose stable models for iOS: account, auth state, chat summary, folder, message, media, reactions, profile, draft, network state, typing state. - Add event stream types for Swift: auth changed, chat list changed, message added/updated/deleted, reaction changed, media download progress, incoming notification candidate, network changed. - Keep filesystem, notifications, clipboard, URL open, and audio playback out of `tele-core`. - Acceptance: - Core unit tests cover auth transitions, chat/message mapping, incoming event queue, folders, reactions, drafts, search results, pinned/profile data. - Existing `tele-tui` behavior remains unchanged. ### Phase 2: UniFFI Bridge - Add `crates/tele-ios-ffi` using UniFFI. - Export a small async-safe facade: - `create_session(config) -> SessionHandle` - `poll_events() -> Vec` - auth methods: phone/code/password - chat methods: load chats/folders/history/search/open profile - message methods: send/edit/delete/reply/forward/react/copy payload - media methods: download photo/download voice - Generate Swift bindings and wire Rust build outputs into an iOS-consumable XCFramework. - Acceptance: - Swift sample test can create a fake session, receive events, load chats, open a chat, send a message, react, and search. - Real TDLib linking is validated for iOS simulator and device or documented as blocked with exact linker error. #### Phase 2A: Local TDLib for iOS `tdlib-rs` 1.2.0 with `download-tdlib` downloads prebuilt TDLib archives from `FedericoBruzzone/tdlib-rs` releases using the pattern `tdlib-{tdlib_version}-{target_os}-{target_arch}.zip`. The terminal/macOS build works because the macOS archive exists. The iOS archives, for example `tdlib-1.8.29-ios-aarch64.zip`, are not published, so iOS real linking must not depend on `download-tdlib`. - Keep `download-tdlib` for the terminal/macOS build path while it remains useful there. - Switch the iOS FFI build path to `tdlib-rs` `local-tdlib` support. - Pin the local TDLib build to the TDLib version expected by `tdlib-rs` 1.2.0: `1.8.29`. - Add `scripts/build-tdlib-ios.sh` to build TDLib locally for: - `arm64-apple-ios` - `arm64-apple-ios-sim` - optionally `x86_64-apple-ios-sim` if Intel Mac simulator support is needed. - Store generated TDLib artifacts outside git, for example: - `.build/tdlib-ios/iphoneos/include` - `.build/tdlib-ios/iphoneos/lib` - `.build/tdlib-ios/iphonesimulator/include` - `.build/tdlib-ios/iphonesimulator/lib` - Add `scripts/build-ios-ffi-with-local-tdlib.sh` to set `LOCAL_TDLIB_PATH` and build `tele-ios-ffi` for simulator/device targets. - Patch `tdlib-rs` through `crates/vendor/tdlib-rs` until upstream handles `target_os = "ios"` in its `local-tdlib` build helper. - Run TDLib's host-side `prepare_cross_compiling` target before the iOS cross-build, because TDLib 1.8.29 expects generated TL/MIME sources to exist during cross-compilation. - After simulator and device Rust builds link, package the Rust FFI output and TDLib dependency into an iOS-consumable XCFramework or documented adjacent native dependency. - Acceptance: - `scripts/check-ios-tdlib-linking.sh` no longer fails because of a missing GitHub release archive. Completed on 2026-05-21. - `cargo build -p tele-ios-ffi --target aarch64-apple-ios-sim --release` links with local TDLib. Completed on 2026-05-21 through `scripts/check-ios-tdlib-linking.sh`. - `cargo build -p tele-ios-ffi --target aarch64-apple-ios --release` links with local TDLib. Completed on 2026-05-21 through `IOS_RUST_TARGET=aarch64-apple-ios scripts/build-ios-ffi-with-local-tdlib.sh`. - The Xcode app can be built against the real FFI path, not only the fake bridge. Completed on 2026-05-21 through `scripts/build-ios-real-ffi-xcframework.sh` and `TELE_IOS_USE_LOCAL_FFI=1 scripts/build-ios-simulator-app.sh`. - Any remaining CMake/OpenSSL/zlib/linker blockers are documented with the exact command and error text. ### Phase 3: iOS App Shell - Add native app under `apps/ios/TeleTuiIOS`. - Use SwiftUI + MVVM: - `SessionStore` - `AuthViewModel` - `ChatListViewModel` - `ChatViewModel` - `ProfileViewModel` - `MediaViewModel` - Implement auth screens, loading/error state, tab-free chat list, folder selector, chat screen, compose bar, profile sheet, settings/account switcher shell. - Store credentials and account metadata with Keychain + Application Support paths, not TUI config files. - Acceptance: - App boots on simulator using fake bridge. - Auth flow screens match all `AuthState` variants. - Chat list and chat detail render deterministic fake data. ### Phase 4: Messaging Feature Parity - Implement chat list features: folders, search, unread count, mentions, muted state, pinned indicator, drafts, network footer/status equivalent. - Implement message features: markdown rendering, incoming/outgoing styling, date separators, sender grouping, albums, reply/forward context, edited/read state. - Implement actions: send, edit, delete confirmation, reply, forward, reactions picker, pinned messages, profile open/leave flow, copy message text via `UIPasteboard`. - Acceptance: - XCTest/UI tests cover the same user journeys as TUI: launch, open chat, send, receive incoming, switch chats, edit, reply, search, reactions, delete confirmation. - Snapshot tests cover chat list, message list, modals/sheets, auth screens, profile, and media placeholders. ### Phase 5: iOS Media, Notifications, and Accounts - Replace desktop-only behavior with iOS-native services: - notifications: `UNUserNotificationCenter` - URL open: `UIApplication.open` - clipboard: `UIPasteboard` - voice playback: `AVAudioPlayer` or `AVPlayer` - photo viewing: SwiftUI image viewer with zoom/pan - Implement photo download/cache, albums, fullscreen viewer, voice download/cache/play/seek. - Implement multi-account switcher with per-account TDLib db paths and app-level account lock policy suitable for iOS. - Acceptance: - Local notifications respect muted chats and mention-only settings. - Voice messages play, pause, seek, and cache. - Photos/albums open fullscreen and reuse cache. - Account switching does not mix old TDLib events into the active account. ### Phase 6: Real Device Hardening - Run against real Telegram credentials on simulator and device. - Add background/foreground lifecycle handling: reconnect, suspend polling, resume events, close sessions safely. - Add privacy and permission flows: notifications, local network/file storage if needed. - Add crash logging and structured Rust/Swift logs. - Optimize large chats: pagination, image memory pressure, voice cache limits, event batching. - Acceptance: - 30-minute manual smoke on real device: auth, load chats, open multiple chats, send/edit/delete/reply/forward/react, receive notifications, view photos, play voice, switch accounts. - No mixed-account updates after rapid account switch. - No UI freeze during chat history loads or media downloads. ### Phase 7: Release Readiness - Add CI jobs for Rust core, FFI generation, Swift build, simulator tests, and packaging. - Add TestFlight configuration, app icons, launch screen, entitlements, privacy manifest, notification capability, and TDLib packaging docs. - Document support matrix: minimum iOS version, device architectures, TDLib version, known limitations. - Acceptance: - Clean CI from empty checkout. - TestFlight build installs and launches. - Release checklist in docs includes credentials setup, TDLib build, signing, and rollback steps. ## Key Interface Decisions - iOS UI is native SwiftUI, not a terminal/TUI port. - Rust/Swift boundary is UniFFI unless TDLib iOS linking forces a lower-level C ABI. - `tele-core` owns Telegram semantics and emits events; Swift owns presentation, storage permissions, notifications, clipboard, URL opening, and media playback. - `tele-tui` remains a first-class crate and must keep passing its existing tests after every core change. ## Test Plan - Rust: - `cargo fmt -- --check` - `cargo clippy --workspace --all-targets --all-features -- -D warnings` - `cargo test --workspace --all-features` - `cargo tree -p tele-core | rg "ratatui|crossterm|ratatui-image|notify-rust|arboard|open|ffplay"` must return no matches. - FFI: - Generated Swift bindings compile. - Fake-session Swift tests cover all exported methods and event variants. - iOS: - XCTest unit tests for view models. - SwiftUI snapshot tests for auth, chat list, chat, message actions, profile, media. - UI tests for auth mock flow, open chat, send, edit, reply, forward, react, search, account switch. - Manual: - Simulator fake-data smoke each phase. - Real-device TDLib smoke before Phase 6 completion. ## Assumptions - "Полноценное iOS-приложение" means practical feature parity with current `tele-tui`, not a minimal chat reader. - iOS implementation should prioritize native UX over preserving Vim/TUI keybindings. - iOS credentials/account storage should use iOS-native secure storage and app sandbox paths. - The first saved plan file should be `docs/superpowers/plans/2026-05-20-ios-app-roadmap.md`.