init
This commit is contained in:
@@ -0,0 +1,545 @@
|
||||
# Mobile Development Best Practices
|
||||
|
||||
Cross-platform best practices for modern mobile development (2024-2025).
|
||||
|
||||
## Mobile-First Design Principles
|
||||
|
||||
### Core Principles
|
||||
1. **Content First**: Remove chrome, focus on content
|
||||
2. **Progressive Disclosure**: Hide complexity behind layers
|
||||
3. **Thumb-Friendly**: Primary actions within reach
|
||||
4. **Performance Budget**: <2s launch, <1s screen load
|
||||
5. **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:**
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
// 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:**
|
||||
```javascript
|
||||
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:**
|
||||
```dart
|
||||
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**
|
||||
```javascript
|
||||
// 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)**
|
||||
```swift
|
||||
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)**
|
||||
```kotlin
|
||||
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**
|
||||
```swift
|
||||
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**
|
||||
```kotlin
|
||||
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**
|
||||
```javascript
|
||||
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:**
|
||||
1. Archive in Xcode
|
||||
2. Upload to App Store Connect
|
||||
3. Submit for review
|
||||
4. 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:**
|
||||
1. Build signed AAB (Android App Bundle)
|
||||
2. Upload to Play Console
|
||||
3. Submit to production track
|
||||
4. 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:**
|
||||
- Detox: https://wix.github.io/Detox/
|
||||
- Appium: https://appium.io/docs/en/latest/
|
||||
- XCTest: https://developer.apple.com/documentation/xctest
|
||||
- Espresso: https://developer.android.com/training/testing/espresso
|
||||
Reference in New Issue
Block a user