A real-time messaging app for international communicators, built with Swift, SwiftUI, and Firebase
Pingrrr is an iOS messaging application designed for seamless cross-language communication. It combines real-time chat functionality with AI-powered features like translation, cultural context, and tone adjustment to break down language barriers.
- Features
- Architecture
- Prerequisites
- Setup Instructions
- Environment Configuration
- Running Locally
- Project Structure
- Development Guidelines
- Testing
- Deployment
- Documentation
- Real-time Chat: Sub-200ms message delivery with Firebase Firestore
- One-on-One & Group Chat: Support for 2+ users in conversations
- Offline Support: SwiftData persistence with queued message sends
- Optimistic UI: Instant message appearance before server confirmation
- Read Receipts: Track message delivery and read status
- Typing Indicators: Real-time typing status updates
- Presence System: Online/offline status with last seen timestamps
- Push Notifications: Firebase Cloud Messaging for foreground notifications
- Media Sharing: Image and voice message support
-
Auto-Translation: Toggle globe button to translate all messages
- Single press: Enable/disable auto-translation
- Long press: Configure native and target languages
- Translates incoming messages to your native language
- Translates outgoing messages to target language
- Recipients only see the translated version
-
Manual Translation: Long-press any message to translate on-demand
-
Language Detection: Automatically detects message language
-
Cultural Context: Provides cultural hints and context for better understanding
-
Tone Adjustment: Modify message formality (casual, neutral, formal, professional)
-
Slang Explanation: Get explanations for slang and idioms
-
Smart Replies: AI-generated context-aware reply suggestions
- Analyzes conversation history
- Generates replies in your native language
- Provides translated version for sending
- One-tap send button
-
Message Summarization: Summarize long conversations or message threads
- Dark Mode Design: Signal/X-inspired interface with black backgrounds
- 60 FPS Performance: Smooth scrolling and animations
- Minimalist Interface: Clean, intuitive design with blue accents
- <2s Cold Launch: Optimized startup time with SwiftData preloading
graph TB
subgraph "iOS Client"
A[SwiftUI Views] -->|MVVM| B[ViewModels]
B --> C[SwiftData]
B --> D[Firebase SDK]
B --> E[AIService]
C -->|Local Cache| F[(SwiftData Store)]
D -->|Network| G[Firebase Services]
E -->|HTTP| H[Cloud Functions]
end
subgraph "Firebase Backend"
G --> I[Firebase Auth]
G --> J[Firestore]
G --> K[FCM]
G --> L[Storage]
H --> M[AI Functions]
M --> N[OpenAI GPT-4]
M -->|RAG| J
end
style A fill:#1f77b4,stroke:#333,stroke-width:2px
style B fill:#ff7f0e,stroke:#333,stroke-width:2px
style C fill:#2ca02c,stroke:#333,stroke-width:2px
style J fill:#e377c2,stroke:#333,stroke-width:2px
style M fill:#bcbd22,stroke:#333,stroke-width:2px
sequenceDiagram
participant User
participant SwiftUI
participant ViewModel
participant SwiftData
participant Firebase
participant CloudFunctions
participant OpenAI
User->>SwiftUI: Send Message
SwiftUI->>ViewModel: Trigger send()
ViewModel->>SwiftData: Save optimistic message
ViewModel->>SwiftUI: Update UI (instant)
alt Auto-Translate Enabled
ViewModel->>CloudFunctions: Request translation
CloudFunctions->>OpenAI: Translate text
OpenAI-->>CloudFunctions: Translated text
CloudFunctions-->>ViewModel: Translation result
ViewModel->>Firebase: Send translated message
else No Translation
ViewModel->>Firebase: Send original message
end
Firebase-->>ViewModel: Confirmation
ViewModel->>SwiftData: Update message status
Firebase->>ViewModel: Realtime listener update
ViewModel->>SwiftUI: Update delivery status
flowchart LR
A[User Types Message] --> B{Globe Active?}
B -->|No| C[Send Original]
B -->|Yes| D[Translate to Target Language]
D --> E[Store Original Locally]
E --> F[Send Only Translation]
F --> G[Recipients Receive Translation]
H[Receive Message] --> I{Globe Active?}
I -->|No| J[Display Original]
I -->|Yes| K{From Me?}
K -->|Yes| L[Display My Original]
K -->|No| M[Translate to Native Language]
M --> N[Display Translation Below]
style D fill:#4CAF50,stroke:#333,stroke-width:2px
style F fill:#FF9800,stroke:#333,stroke-width:2px
style M fill:#2196F3,stroke:#333,stroke-width:2px
graph TD
subgraph "ViewModels"
A[AuthViewModel]
B[ConversationsViewModel]
C[ChatViewModel]
end
subgraph "Services"
D[AuthService]
E[ConversationService]
F[MessageSyncService]
G[PresenceService]
H[AIService]
I[NetworkMonitor]
J[NotificationService]
K[ProfileService]
L[MediaService]
M[OutgoingMessageQueue]
N[TypingIndicatorService]
end
subgraph "Data Layer"
O[SwiftData Models]
P[Firebase SDK]
end
A --> D --> P
B --> E --> O
B --> E --> P
C --> F --> O
C --> F --> P
C --> H --> P
C --> M --> O
C --> N --> P
D --> G
E --> G
I --> B
I --> C
J --> P
K --> P
L --> P
style C fill:#FF6B6B,stroke:#333,stroke-width:3px
style F fill:#4ECDC4,stroke:#333,stroke-width:2px
style H fill:#FFE66D,stroke:#333,stroke-width:2px
Before setting up Pingrrr, ensure you have the following installed:
- Xcode 15+ (Download)
- iOS 17+ SDK
- CocoaPods or Swift Package Manager (SPM recommended)
- Node.js 18+ and npm (Download)
- Firebase CLI (
npm install -g firebase-tools) - Git
- Apple Developer Account (for running on physical devices)
- Firebase Project (Create one)
- OpenAI API Key (Get one)
- Firebase Authentication (Email/Password enabled)
- Cloud Firestore
- Cloud Functions
- Cloud Messaging
- Cloud Storage
git clone https://github.com/yourusername/pingrrr.git
cd pingrrr-
Create a Firebase Project
firebase login firebase projects:create pingrrr-your-id firebase use pingrrr-your-id
-
Enable Required Services
- Go to Firebase Console
- Select your project
- Enable Authentication β Email/Password
- Enable Firestore Database β Start in production mode
- Enable Cloud Functions
- Enable Cloud Messaging
- Enable Cloud Storage
-
Download iOS Configuration
- In Firebase Console β Project Settings
- Add iOS app with bundle ID:
com.yourcompany.pingrrr - Download
GoogleService-Info.plist - Place in
pingrrr/pingrrr/directory
-
Configure Firestore Security Rules
firebase deploy --only firestore:rules
-
Configure Storage Security Rules
firebase deploy --only storage:rules
-
Navigate to Functions Directory
cd functions -
Install Dependencies
npm install
-
Configure Environment Variables (see Environment Configuration)
-
Build Functions
npm run build
-
Deploy Functions
npm run deploy # OR deploy specific function firebase deploy --only functions:aiTranslate
-
Open Xcode Project
cd .. open pingrrr.xcodeproj # OR if using workspace open pingrrr.xcworkspace
-
Configure Signing & Capabilities
- Select
pingrrrtarget - Go to "Signing & Capabilities"
- Select your Team
- Update Bundle Identifier if needed
- Select
-
Add Required Capabilities
- Push Notifications
- Background Modes β Remote notifications
- Background Modes β Background fetch
-
Verify Firebase SDK Integration
- Firebase packages should be auto-resolved via SPM
- If not, add packages manually:
https://github.com/firebase/firebase-ios-sdk- Select: FirebaseAuth, FirebaseFirestore, FirebaseFunctions, FirebaseMessaging, FirebaseStorage
-
Add GoogleService-Info.plist
- Drag the downloaded
GoogleService-Info.plistintopingrrr/pingrrr/folder in Xcode - Ensure "Copy items if needed" is checked
- Add to
pingrrrtarget
- Drag the downloaded
-
Generate APNs Key
- Go to Apple Developer Portal
- Certificates, Identifiers & Profiles β Keys
- Create new key with APNs enabled
- Download
.p8key file
-
Upload to Firebase
- Firebase Console β Project Settings β Cloud Messaging
- Upload APNs key with Team ID and Key ID
-
Update Info.plist
- Add required notification keys (already configured in project)
Create required Firestore indexes:
firebase deploy --only firestore:indexesOr manually create in Firebase Console based on firestore.indexes.json.
Create a .env file in the functions/ directory:
# functions/.env
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxxSet Firebase environment variables:
cd functions
firebase functions:config:set openai.api_key="sk-proj-xxxxxxxxxxxxxxxxxxxxx"Verify configuration:
firebase functions:config:getNo additional environment variables needed. Configuration is handled through:
GoogleService-Info.plist(Firebase config)- Xcode Build Configuration
Info.plist(app metadata)
# Firebase Project
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_API_KEY=your-api-key
FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
FIREBASE_STORAGE_BUCKET=your-project.appspot.com
# OpenAI
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxx
# iOS Bundle
BUNDLE_IDENTIFIER=com.yourcompany.pingrrr
TEAM_ID=YOUR_TEAM_ID
# APNs (for push notifications)
APNS_KEY_ID=YOUR_KEY_ID
APNS_TEAM_ID=YOUR_TEAM_ID-
Select Target Device
- In Xcode, select "iPhone 15 Pro" (or your preferred simulator)
-
Build and Run
# Command line xcodebuild -scheme pingrrr -destination 'platform=iOS Simulator,name=iPhone 15 Pro' build # OR press Cmd+R in Xcode
-
Create Test Users
- Run the app on 2+ simulators simultaneously
- Sign up with different email addresses
- Start chatting!
-
Connect iPhone via USB or WiFi
-
Trust Developer Certificate
- iPhone Settings β General β VPN & Device Management
- Trust your developer certificate
-
Select Device in Xcode
- Choose your iPhone from device list
-
Build and Run (Cmd+R)
Push notifications work differently on simulator vs device:
- Simulator: Only foreground notifications (iOS 16+)
- Physical Device: Full notification support
To test:
- Send message while app is in foreground (toast notification)
- Send message while app is in background (push notification)
For local development without consuming Firebase quota:
# Start Firebase Emulators
firebase emulators:start
# Update iOS app to use emulators (uncomment in AppDelegate.swift)
# Auth.auth().useEmulator(withHost: "localhost", port: 9099)
# Firestore.firestore().useEmulator(withHost: "localhost", port: 8080)pingrrr/
βββ pingrrr/ # iOS App
β βββ App/ # App lifecycle & entry point
β β βββ AppDelegate.swift # Firebase initialization
β β βββ AppMain.swift # App entry point
β β βββ RootContainerView.swift # Root container with auth routing
β β
β βββ Models/ # Data models
β β βββ ChatModels.swift # SwiftData models (Message, Conversation, etc.)
β β βββ TranslationLanguage.swift # Translation language definitions
β β
β βββ Views/ # SwiftUI Views
β β βββ AuthenticationFlowView.swift # Login/signup
β β βββ ConversationsView.swift # Chat list
β β βββ ChatView.swift # Chat interface with AI features
β β βββ SettingsSheet.swift # User settings
β β βββ Components/ # Reusable UI components
β β
β βββ ViewModels/ # Business logic
β β βββ AuthViewModel.swift # Authentication state
β β βββ ConversationsViewModel.swift # Conversations list logic
β β βββ ChatViewModel.swift # Chat logic & AI features
β β βββ TypingIndicatorService.swift # Typing indicators
β β
β βββ Services/ # Backend integration
β β βββ AuthService.swift # Firebase Auth wrapper
β β βββ ConversationService.swift # Conversation CRUD
β β βββ ConversationsSyncService.swift # Firestore sync
β β βββ MessageSyncService.swift # Message sync & persistence
β β βββ AIService.swift # AI features coordinator
β β βββ PresenceService.swift # Online/offline status
β β βββ NotificationService.swift # FCM integration
β β βββ MediaService.swift # Image/voice upload
β β βββ OutgoingMessageQueue.swift # Offline message queue
β β βββ NetworkMonitor.swift # Network connectivity
β β βββ ProfileService.swift # User profile management
β β
β βββ Extensions/ # Swift extensions
β βββ Assets.xcassets/ # Images & icons
β βββ Info.plist # App configuration
β
βββ functions/ # Firebase Cloud Functions
β βββ src/
β β βββ index.ts # Functions entry point
β β βββ ai/ # AI feature implementations
β β βββ translate.ts # Translation
β β βββ detectLang.ts # Language detection
β β βββ culturalHint.ts # Cultural context
β β βββ adjustTone.ts # Tone adjustment
β β βββ explainSlang.ts # Slang explanation
β β βββ smartReplies.ts # Smart reply generation
β β βββ summarize.ts # Conversation summarization
β β βββ assistant.ts # AI assistant
β β βββ common.ts # Shared utilities
β β
β βββ package.json # Node dependencies
β βββ tsconfig.json # TypeScript config
β
βββ memory-bank/ # Project documentation
β βββ projectbrief.md # Project overview
β βββ PRD.md # Product requirements
β βββ techContext.md # Technical architecture
β βββ systemPatterns.md # Design patterns
β βββ MVPcriticalrequirements.md # MVP checklist
β βββ progress.md # Development progress
β
βββ scripts/ # Utility scripts
β βββ set_user_tier.sh # User tier management
β βββ lookup_uid.sh # User ID lookup
β
βββ firebase.json # Firebase configuration
βββ firestore.rules # Firestore security rules
βββ firestore.indexes.json # Firestore indexes
βββ storage.rules # Storage security rules
βββ README.md # This file
- Swift: Follow Swift API Design Guidelines
- SwiftUI: Use declarative patterns, avoid imperative code
- Naming: Clear, descriptive names; avoid abbreviations
- Comments: Document complex logic, algorithms, and public APIs
-
MVVM (Model-View-ViewModel)
- Views: Pure SwiftUI, no business logic
- ViewModels: Business logic, state management, async operations
- Models: Data structures (SwiftData models)
-
Service Layer
- Services handle external dependencies (Firebase, AI)
- Single responsibility principle
- Injected into ViewModels
-
Async/Await
- Use Swift Concurrency for all async operations
- Avoid completion handlers
- Proper error handling with
do-catch
- @Published: For ViewModel properties that drive UI
- @State: For local view state
- @StateObject: For ViewModel initialization
- @ObservedObject: For passed ViewModels
- @EnvironmentObject: For app-wide state (AuthViewModel)
-
SwiftUI Optimization
- Use
LazyVStackfor long lists - Avoid expensive computations in view body
- Use
@ViewBuilderfor conditional views - Extract subviews to reduce recomputation
- Use
-
SwiftData
- Use
@Querywith predicates for filtering - Batch operations when possible
- Optimize fetch descriptors
- Use
-
Firebase
- Limit Firestore listener scope
- Use indexed queries
- Batch writes for multiple operations
- Implement pagination for large datasets
-
AI Features
- Cache translations for repeated content
- Debounce user interactions
- Show loading states
- Implement timeout handling
// Example: Proper error handling pattern
func sendMessage() async {
do {
try await viewModel.send(request: .text(message))
// Success handling
} catch NetworkError.offline {
errorMessage = "No internet connection. Message queued."
} catch AIError.rateLimitExceeded {
errorMessage = "AI usage limit reached. Try again later."
} catch {
errorMessage = "Failed to send message: \(error.localizedDescription)"
}
}- Unit Tests: Test ViewModels and Services
- Integration Tests: Test Firebase interactions
- UI Tests: Test critical user flows
- Manual Testing: Test on multiple devices and iOS versions
Run unit tests via Xcode:
# Command line
xcodebuild test -scheme pingrrrTests -destination 'platform=iOS Simulator,name=iPhone 15 Pro'
# OR press Cmd+U in XcodeTest scenarios to verify:
-
Real-time Messaging
- Send message between 2 devices
- Verify <200ms delivery on good network
-
Offline Support
- Enable Airplane Mode
- Send messages (queued)
- Disable Airplane Mode
- Verify messages sync
-
App Lifecycle
- Force quit app
- Reopen
- Verify messages persisted
-
Group Chat
- Create 3+ user conversation
- Send messages from each user
- Verify all receive messages
-
AI Features
- Test each AI feature (translate, tone, smart reply, etc.)
- Verify <2s response time
- Test with various languages
-
Performance
- Send 20+ rapid messages
- Verify smooth scrolling
- Check memory usage
- Sign up new user
- Sign in existing user
- Create conversation
- Send text message
- Send image
- Send voice message
- Receive message (foreground notification)
- Receive message (background push)
- Toggle online status
- See typing indicator
- View read receipts
- Enable auto-translate
- Configure translation languages
- Use manual translation
- Generate smart reply
- Adjust message tone
- Explain slang
- Get cultural context
- Summarize conversation
- Test offline β online transition
- Test poor network conditions
-
Archive Build
- Xcode β Product β Archive
- Wait for archive to complete
-
Distribute to App Store Connect
- Select archive β Distribute App
- Choose "App Store Connect"
- Upload build
-
Configure TestFlight
- Go to App Store Connect
- Select your app β TestFlight
- Add internal testers
- Create external test group (optional)
-
Submit for Review (external testing)
- Add test information
- Submit for review
- Wait for approval (~24 hours)
Deploy all Firebase resources:
# Deploy everything
firebase deploy
# Deploy specific services
firebase deploy --only functions
firebase deploy --only firestore:rules
firebase deploy --only storage:rules
firebase deploy --only firestore:indexes- Update version number in Xcode
- Update build number
- Test on multiple iOS versions (17, 18)
- Test on multiple device sizes (iPhone SE, Pro, Pro Max)
- Verify all Firebase functions deployed
- Verify Firestore indexes created
- Test push notifications on physical device
- Review and update security rules
- Enable Firebase App Check (recommended)
- Set up Crashlytics (optional)
- Configure analytics (optional)
- Update App Store metadata
- Prepare screenshots
- Write release notes
- Project Brief: High-level overview and objectives
- PRD: Detailed product requirements and specifications
- Tech Context: Technical architecture and patterns
- System Patterns: Design patterns and conventions
- MVP Requirements: Critical MVP checklist
- MVP Tasks: Development tasks and progress
- Progress: Development timeline and achievements
// Send message
await viewModel.send(request: .text("Hello"))
// Toggle auto-translate
viewModel.toggleAutoTranslate()
// Update translation languages
viewModel.updateAutoTranslateLanguages(native: .en, target: .es)
// AI Features
await viewModel.translateMessage(for: messageID, to: "es")
await viewModel.generateSmartReply(for: messageID)
await viewModel.adjustTone(for: messageID, to: .formal)
await viewModel.explainSlang(for: messageID)
await viewModel.provideCulturalContext(for: messageID)
await viewModel.summarizeConversation()// Translation
let result = try await AIService.shared.translate(text: text, to: targetLang)
// Language detection
let lang = try await AIService.shared.detectLanguage(text: text)
// Smart replies
let replies = try await AIService.shared.smartReplies(history: history, native: lang)
// Cultural context
let context = try await AIService.shared.culturalContext(text: text, lang: lang)firestore/
βββ users/
β βββ {userId}/
β βββ uid: string
β βββ email: string
β βββ displayName: string
β βββ photoURL: string?
β βββ bio: string?
β βββ onlineStatus: "online" | "offline"
β βββ lastSeen: timestamp
β βββ fcmToken: string?
β βββ aiUsageToday: number
β
βββ conversations/
β βββ {conversationId}/
β βββ participantIDs: [string]
β βββ participantNames: {userId: string}
β βββ participantPhotos: {userId: string}
β βββ lastMessageText: string
β βββ lastMessageAt: timestamp
β βββ lastMessageBy: string
β βββ unreadCounts: {userId: number}
β βββ typingUsers: {userId: timestamp}
β βββ translationPreferences: {userId: {enabled, native, target, activatedAt}}
β βββ createdAt: timestamp
β
βββ messages/
βββ {messageId}/
βββ conversationID: string
βββ senderID: string
βββ senderName: string
βββ content: string (translated if auto-translate on)
βββ originalContent: string? (sender's original text)
βββ timestamp: timestamp
βββ mediaType: "image" | "voice"?
βββ mediaURL: string?
βββ voiceDuration: number?
βββ status: "sending" | "sent" | "delivered" | "read"
βββ readBy: {userId: timestamp}
βββ translatedContent: string? (manual translation)
βββ autoTranslations: {
β "broadcast": {text, targetLanguageCode},
β userId: {text, targetLanguageCode}
β }
βββ aiInsights: {
slangExplanation: string?,
culturalContext: string?,
summary: string?
}
Build Failed: GoogleService-Info.plist not found
- Download from Firebase Console
- Drag into Xcode project
- Ensure added to target
Push Notifications Not Working
- Verify APNs key uploaded to Firebase
- Check capabilities enabled in Xcode
- Test on physical device (not simulator)
- Verify FCM token registered
Messages Not Syncing
- Check Firebase rules allow read/write
- Verify internet connection
- Check Firestore indexes created
- Review Firebase Console logs
AI Features Failing
- Verify OPENAI_API_KEY set in Cloud Functions
- Check Cloud Functions deployed
- Review function logs:
firebase functions:log - Verify usage limits not exceeded
App Crashes on Launch
- Check Firebase initialized properly
- Verify GoogleService-Info.plist configuration
- Review Xcode console for errors
- Check SwiftData migration issues
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Firebase for backend infrastructure
- OpenAI for AI capabilities
- Vercel AI SDK for streamlined AI integration
- Apple for Swift and SwiftUI
- Signal and X for UI/UX inspiration
For issues, questions, or feedback:
- Open an issue on GitHub
- Email: support@pingrrr.app
- Twitter: @pingrrr
Built with β€οΈ for international communicators worldwide π