12 KiB
Mobile Development Best Practices
Cross-platform best practices for modern mobile development (2024-2025).
Mobile-First Design Principles
Core Principles
- Content First: Remove chrome, focus on content
- Progressive Disclosure: Hide complexity behind layers
- Thumb-Friendly: Primary actions within reach
- Performance Budget: <2s launch, <1s screen load
- Offline-First: Design for unreliable networks
Touch Targets
- iOS: 44x44px minimum (HIG guideline)
- Android: 48x48px minimum (Material Design)
- Optimal: 44-57px for important actions
- Spacing: 8px minimum between targets
Typography
- iOS: San Francisco (system font)
- Android: Roboto (Material)
- Minimum: 16px body text (accessibility)
- Line height: 1.5x for readability
Performance Optimization
Launch Time Optimization
Targets:
- Cold start: <2s
- Warm start: <1s
- Hot start: <0.5s
Techniques:
- Defer non-critical initialization
- Lazy load dependencies
- Preload critical data only
- Show UI before data ready
Memory Management
Targets:
- Typical screen: <100MB
- Peak usage: <200MB
Techniques:
- Image pagination/virtualization
- Release resources in background
- Profile with Instruments/Profiler
- Avoid retain cycles/memory leaks
React Native Example:
// Use FlatList instead of ScrollView for long lists
<FlatList
data={items}
renderItem={({ item }) => <ItemCard item={item} />}
keyExtractor={(item) => item.id}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
/>
Network Optimization
Techniques:
- Batch API requests
- Cache aggressively
- Compress images (WebP, AVIF)
- Use CDN for static assets
- Implement request deduplication
Example Strategy:
User opens screen
├─ Show cached data immediately (stale-while-revalidate)
├─ Fetch fresh data in background
└─ Update UI when fresh data arrives
Battery Optimization
Techniques:
- Batch network requests
- Reduce GPS accuracy when possible
- Use push instead of polling
- Respect Doze Mode (Android)
- Background App Refresh (iOS)
Targets:
- Active use: <5% per hour
- Background: <1% per hour
Offline-First Architecture
Local Storage Options
React Native:
- AsyncStorage (small data, <6MB)
- Realm (complex objects, relationships)
- SQLite (relational data)
- MMKV (fastest key-value)
Flutter:
- SharedPreferences (small data)
- Hive (NoSQL, fast)
- Drift (SQLite wrapper)
- ObjectBox (object database)
iOS:
- UserDefaults (small data)
- Core Data (complex objects)
- SwiftData (modern replacement)
- Realm
Android:
- SharedPreferences (small data)
- Room (SQLite ORM)
- Realm
- DataStore (Preferences + Proto)
Data Synchronization Strategies
1. Write-Through Cache
User makes change
├─ Update local database immediately
├─ Update UI optimistically
├─ Queue sync operation
└─ Sync to server in background
2. Hybrid Sync (Push + Pull)
Push Sync (Real-time)
├─ WebSocket connection for critical updates
└─ Immediate notification of changes
Pull Sync (Periodic)
├─ Periodic polling for non-critical data
├─ Pull on app foreground
└─ Incremental sync (only changes since last sync)
3. Conflict Resolution
- Last-write-wins: Use timestamps
- Operational transformation: Merge changes
- CRDT: Conflict-free replicated data
- Manual resolution: User chooses
Example: Offline-First Comments
// React Native + TypeScript
class CommentService {
async postComment(text: string, postId: string) {
const tempId = generateTempId();
const comment = {
id: tempId,
text,
postId,
synced: false,
timestamp: Date.now()
};
// 1. Save locally immediately
await db.comments.insert(comment);
// 2. Update UI (optimistic)
eventBus.emit('comment:added', comment);
// 3. Sync to server in background
try {
const serverComment = await api.postComment(text, postId);
// Replace temp ID with server ID
await db.comments.update(tempId, {
id: serverComment.id,
synced: true
});
} catch (error) {
// Mark as pending sync, retry later
await db.comments.update(tempId, {
syncError: error.message
});
syncQueue.add({ type: 'comment', id: tempId });
}
}
}
Mobile Analytics & Monitoring
Analytics Platforms (2024-2025)
Firebase Analytics (Recommended)
- Free tier generous
- Mobile-specific events
- Integrated with Crashlytics
- AI-powered insights
- Supports all platforms
Sentry
- Error tracking + performance
- Cross-platform support
- Source map upload
- Release tracking
- Custom breadcrumbs
Amplitude
- Product analytics
- User behavior tracking
- Cohort analysis
- A/B testing integration
Essential Events to Track
User Journey:
- App opened
- Screen viewed
- Feature used
- Conversion events
- User retention
Performance:
- App launch time
- Screen load time
- API latency
- Crash-free rate
- ANR rate (Android)
Business:
- Purchases
- Subscriptions
- Ad impressions
- Feature adoption
- Referrals
Crashlytics Integration
React Native:
import crashlytics from '@react-native-firebase/crashlytics';
// Log events
crashlytics().log('User tapped purchase button');
// Set user attributes
crashlytics().setUserId(user.id);
// Log non-fatal errors
try {
await riskyOperation();
} catch (error) {
crashlytics().recordError(error);
}
Flutter:
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
// Log events
FirebaseCrashlytics.instance.log('User tapped purchase');
// Set user ID
FirebaseCrashlytics.instance.setUserIdentifier(userId);
// Record errors
await FirebaseCrashlytics.instance.recordError(
error,
stackTrace,
reason: 'API call failed',
);
Push Notifications Best Practices
Platforms
- iOS: APNs (Apple Push Notification service)
- Android: FCM (Firebase Cloud Messaging)
- Cross-platform: OneSignal, Firebase, AWS SNS
Best Practices
1. Permission Request Strategy
❌ Bad: Request permission on app launch
✅ Good: Request after user sees value
Flow:
1. User interacts with feature
2. Show custom modal explaining benefits
3. Request system permission
4. Handle denial gracefully
2. Personalization
- Segment users by behavior
- Send at optimal times (time zones)
- Personalize content
- A/B test messaging
3. Frequency
- Avoid notification spam
- Respect user preferences
- Implement quiet hours
- Group related notifications
4. Deep Linking
// React Native
import messaging from '@react-native-firebase/messaging';
messaging().onNotificationOpenedApp(remoteMessage => {
const { screen, params } = remoteMessage.data;
navigation.navigate(screen, params);
});
Impact:
- 25% revenue increase with proper personalization
- 88% opt-in rate with pre-permission modal (vs 40% without)
Authentication & Authorization
Modern Auth Stack (2024-2025)
Standard Pattern:
OAuth 2.0 (Authorization)
├─ JWT (Stateless auth tokens)
├─ Refresh tokens (Long-term access)
└─ Biometric (Convenient re-auth)
Implementation
Biometric Authentication (iOS)
import LocalAuthentication
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "Unlock your account") { success, error in
if success {
// Authenticated
}
}
}
Biometric Authentication (Android)
import androidx.biometric.BiometricPrompt
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Biometric login")
.setSubtitle("Log in using your biometric credential")
.setNegativeButtonText("Use account password")
.build()
val biometricPrompt = BiometricPrompt(this, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
// Authenticated
}
})
biometricPrompt.authenticate(promptInfo)
Secure Token Storage
iOS: Keychain
import Security
func saveToken(_ token: String, for key: String) {
let data = token.data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemAdd(query as CFDictionary, nil)
}
Android: EncryptedSharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val sharedPreferences = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
sharedPreferences.edit().putString("auth_token", token).apply()
React Native: react-native-keychain
import * as Keychain from 'react-native-keychain';
// Save credentials
await Keychain.setGenericPassword('username', token, {
accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SET,
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
// Retrieve credentials
const credentials = await Keychain.getGenericPassword();
const token = credentials.password;
App Store Deployment
App Store (iOS)
Requirements (2024-2025):
- Xcode 15+ with iOS 17 SDK (minimum)
- Xcode 16+ with iOS 18 SDK (recommended for 2025)
- Privacy manifest required
- Account deletion in-app mandatory
Release Process:
- Archive in Xcode
- Upload to App Store Connect
- Submit for review
- Phased release (7-day rollout)
Review Time:
- Average: 1-2 days
- Expedited: 1-2 hours (emergencies only)
Rejection Reasons:
- Crashes (50%)
- Privacy violations (25%)
- Incomplete information (15%)
- Guideline violations (10%)
Google Play (Android)
Requirements (2024-2025):
- Target Android 14 (API 34) now
- Target Android 15 (API 35) by Aug 31, 2025
- Privacy policy required
- Data safety form required
Release Process:
- Build signed AAB (Android App Bundle)
- Upload to Play Console
- Submit to production track
- Staged rollout (10% → 50% → 100%)
Review Time:
- Average: 1-3 days
- Updates: 1-2 days
Staged Rollout Strategy
Week 1:
- 10% of users
- Monitor crash-free rate
- Watch for critical bugs
Week 2:
- 50% of users
- Validate performance metrics
- Check user feedback
Week 3:
- 100% of users
- Full release if metrics healthy
Rollback Triggers:
- Crash-free rate drops >5%
- Critical bug discovered
- Major user complaints
Cross-Platform Comparison
Flutter vs React Native (2024-2025)
| Metric | React Native | Flutter |
|---|---|---|
| Adoption | 35% | 46% |
| Performance | 80-90% | 85-95% |
| App Size | 40-50MB | 15-20MB |
| Dev Speed | Fast | Very Fast |
| Commercial | 12.57% | 5.24% |
| Developers | 20:1 ratio | 1 ratio |
| Best For | JS teams | Performance |
Architecture Comparison
MVVM (Small Apps):
View
↓
ViewModel (business logic)
↓
Model (data)
Clean Architecture (Large Apps):
Presentation (UI)
↓
Domain (business logic, use cases)
↓
Data (repositories, APIs, DB)
Resources
Performance:
- iOS: https://developer.apple.com/documentation/xcode/improving-your-app-s-performance
- Android: https://developer.android.com/topic/performance
- React Native: https://reactnative.dev/docs/performance
Analytics:
- Firebase: https://firebase.google.com/docs/analytics
- Sentry: https://docs.sentry.io/platforms/react-native/
- Amplitude: https://amplitude.com/docs
Security:
- OWASP Mobile: https://owasp.org/www-project-mobile-top-10/
- iOS Security: https://support.apple.com/guide/security/
- Android Security: https://source.android.com/docs/security
Testing: