Mobile-First: A Necessity in Africa
In Africa, 85% of internet access happens via mobile. For Keneya, ignoring mobile would have meant ignoring 85% of our users.
But mobile in Africa has specific constraints:
Our Offline-First Approach
The Keneya Mobile app works even without connection. Data synchronizes as soon as the network returns.
Data Architecture
typescript
// db/schema.ts
import { create } from 'react-native-sqlite-storage'
const db = await create({
name: 'keneya.db',
location: 'default'
})
// Main tables
await db.executeSql(`
CREATE TABLE IF NOT EXISTS consultations (
id TEXT PRIMARY KEY,
patient_id TEXT,
doctor_id TEXT,
date TEXT,
status TEXT,
synced INTEGER DEFAULT 0
)
`)
await db.executeSql(`
CREATE TABLE IF NOT EXISTS medical_records (
id TEXT PRIMARY KEY,
patient_id TEXT,
content TEXT,
updated_at TEXT,
synced INTEGER DEFAULT 0
)
`)Smart Synchronization
typescript
// sync/sync-manager.ts
class SyncManager {
async syncAll() {
if (!await this.hasConnection()) {
console.log('No connection, sync postponed')
return
}
// 1. Upload unsynced local data
await this.uploadPendingData()
// 2. Download new server data
await this.downloadServerData()
// 3. Resolve conflicts
await this.resolveConflicts()
}
async uploadPendingData() {
const pending = await db.executeSql(
'SELECT * FROM consultations WHERE synced = 0'
)
for (const record of pending) {
try {
await api.post('/consultations', record)
await db.executeSql(
'UPDATE consultations SET synced = 1 WHERE id = ?',
[record.id]
)
} catch (error) {
console.error('Sync failed:', error)
}
}
}
}App Size Optimization
For entry-level phones, every MB counts:
1. Hermes Instead of JSC
json
// android/app/build.gradle
project.ext.react = [
enableHermes: true
]Result: APK reduced from 35MB to 22MB
2. Optimized Images
3. Code Splitting
typescript
// Lazy loading screens
const ConsultationScreen = lazy(() => import('./screens/Consultation'))
const MedicalRecordScreen = lazy(() => import('./screens/MedicalRecord'))Data Consumption Management
typescript
// Sync only on WiFi (user option)
const settings = await AsyncStorage.getItem('sync_settings')
if (settings.wifiOnly && !isWifiConnected()) {
showNotification('Sync waiting for WiFi')
return
}
// Compress images before upload
const compressed = await ImageCompressor.compress(photo, {
quality: 0.7,
maxWidth: 1024
})Concrete Results
Conclusion
In Africa, mobile-first is not a choice, it's an obligation. And offline-first is the only viable approach to guarantee a smooth user experience.
*Developing a mobile app for Africa? [Let's talk strategy](/contact).*