We develop mobile CRM applications that solve real problems for sales managers. A standard CRUD interface over a database doesn't work here. Imagine: a manager is on the subway, the connection is unstable, and they urgently need to update a deal status. Without offline mode—you lose the client. Our team creates CRMs that work even on weak 3G and sync after network recovery. Over our time in the field we have released more than 20 mobile CRM solutions for various industries—from retail to logistics. We guarantee quality and hold Apple/Google developer certifications.
Why offline sync is critical for CRM
Offline mode is not an option but a requirement. The manager shouldn't wait for data to load. We use a local SQLite database with server-side conflict resolution. This speeds up work with client cards by 40% compared to an online-only approach. On Flutter we use drift (formerly moor) as a typed ORM on top of SQLite, and connectivity_plus to track network state:
// Save the action locally and enqueue for sync Future<void> updateDealStage(String dealId, DealStage stage) async { await localDb.updateDeal(dealId, stage: stage, syncStatus: SyncStatus.pending); await syncQueue.enqueue( SyncOperation( type: OperationType.updateDeal, payload: {'id': dealId, 'stage': stage.name}, createdAt: DateTime.now(), ), ); // Trigger sync if network is available if (await connectivity.checkConnectivity() != ConnectivityResult.none) { syncService.flush(); } } Conflicts arise when the same contact is edited from multiple devices. A 'last write wins' strategy corrupts data. The right approach: Last-Write-Wins at the field level rather than the record level—with updated_at on every mutable field, and a version vector for critical data. > "Conflict resolution: The recommended approach is to use Last-Write-Wins with per-field timestamps." — SQLite Documentation.
How to implement role-based access in a mobile CRM
Roles are the foundation of security and UX. A manager sees only their own clients; a supervisor sees the entire department. We implement this with Row Level Security on the server (PostgreSQL) and hide inaccessible actions on the client. On iOS we use the Keychain bound to the device—the token does not transfer to backups, which is essential for corporate security.
Telephony integration
The ability to call directly from a contact card is a standard mobile CRM feature. On iOS we use CallKit for native integration: a call via a VoIP provider (Twilio, Vonage) appears as a regular incoming call with the name from the CRM and is logged in the device call history.
// iOS CallKit provider class CRMCallProvider: NSObject, CXProviderDelegate { func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { // Connect Twilio Voice SDK TwilioVoice.connect(with: connectOptions) { [weak self] call, error in guard let call = call else { return } self?.activeCall = call action.fulfill() // Log call start in CRM self?.crmService.logCallStarted(contactId: action.callUUID.uuidString) } } } On Android the equivalent is ConnectionService via TelecomManager.
Architecture and stack
For a cross-platform CRM application, Flutter is the optimal choice: one codebase covers iOS and Android, which is important given constantly changing business requirements. Architecture: BLoC + Clean Architecture, the repository layer isolates the local database and API. Flutter reduces development time by 30% compared to separate native apps, and a single codebase simplifies maintenance.
| Layer | Technologies |
|---|---|
| UI | Flutter + Material 3 / Cupertino adaptations |
| State management | flutter_bloc (BLoC pattern) |
| Local DB | drift (SQLite), Hive for cache |
| Network | Dio + Retrofit generation, Interceptor for refresh token |
| Sync | WorkManager (Android) / BGTaskScheduler (iOS) |
| Push | Firebase Cloud Messaging + background fetch |
| Analytics | Firebase Analytics, Crashlytics |
For native iOS or Android development—SwiftUI + Combine / Jetpack Compose + ViewModel respectively.
Working with push notifications
CRM events—scheduled meetings, new leads, overdue tasks—require push delivery in the background. On iOS UNUserNotificationCenter with content-available: 1 launches the app in the background to update data. A critical point: iOS gives background processes no more than 30 seconds, and too frequent background fetches lead to throttling by the system. Strategy: push only notifies about the event; heavy data is loaded lazily when the notification is opened. This architecture reduces battery drain by 25%.
What does the development process look like?
Requirements audit and data model design → design (if not already done) → parallel API and mobile client development → integration testing of sync with edge cases → testing on real devices with network loss simulation → publishing to App Store / Google Play → support. A mandatory step is load testing sync with a large database (10,000+ contacts). On low-end Android devices, SQLite operations on a large dataset without proper pagination and indexes cause ANR errors.
Timelines and budget
MVP with basic functionality (contacts, deals, tasks, offline): 8–12 weeks. Full-fledged app with telephony, integrations (email, calendar), advanced analytics: 4–6 months. Cost is calculated individually after requirements analysis. Contact us for a project estimate.
What's included in the work
- Architecture design and technology selection
- CI/CD setup, App Store/Google Play console configuration
- Documentation and code review
- Testing on real devices
- Post-launch support: 3 months free
- Training of the client's team
Our experience
Extensive experience in mobile CRM development with more than 20 completed projects. Apple and Google certified developers. We guarantee quality at every stage. Get in touch with us for a consultation—we'll assess your project within one day.







