Cross-Platform Development Patterns
Build applications that run on iOS and Android from a single codebase. Covers React Native, Flutter, KMP architecture decisions, platform-specific code handling, shared business logic, and the patterns that maximize code reuse without sacrificing native quality.
Building separate iOS and Android apps doubles development cost, testing effort, and feature parity challenges. Cross-platform frameworks promise “write once, run everywhere” — but the reality is more nuanced. The right approach depends on your team, timeline, and quality requirements.
Framework Comparison
React Native:
Language: JavaScript/TypeScript
Rendering: Native components (uses platform UIKit/Views)
Hot Reload: Yes (fast iteration)
Ecosystem: NPM (massive), Expo (managed workflow)
Performance: Near-native (bridge communication overhead)
When: Web team expanding to mobile, rapid prototyping
Who uses: Instagram, Shopify, Discord
Flutter:
Language: Dart
Rendering: Custom engine (Skia/Impeller, pixel-perfect control)
Hot Reload: Yes (extremely fast)
Ecosystem: pub.dev (growing), strong widget library
Performance: Near-native (compiled to ARM, no bridge)
When: Pixel-perfect custom UI, small team shipping fast
Who uses: Google Pay, BMW, Alibaba
Kotlin Multiplatform (KMP):
Language: Kotlin
Rendering: Native (platform UI, only shares business logic)
Hot Reload: No
Ecosystem: Kotlin/JVM (mature), growing KMP-specific
Performance: Native (compiled per platform)
When: Sharing business logic, keeping native UI quality
Who uses: Netflix, Cash App, VMware
Code Sharing Strategy
Full Cross-Platform (Flutter/React Native):
Shared: UI + Business Logic + Navigation
Platform-specific: Push notifications, deep links, sensors
Code reuse: 85-95%
┌───────────────────────────────┐
│ Shared Code │
│ ┌─────────────────────────┐ │
│ │ UI Components │ │
│ ├─────────────────────────┤ │
│ │ Business Logic │ │
│ ├─────────────────────────┤ │
│ │ Navigation │ │
│ ├─────────────────────────┤ │
│ │ State Management │ │
│ └─────────────────────────┘ │
├───────────────────────────────┤
│ Platform Channels/Bridges │
│ (camera, biometrics, NFC) │
└───────────┬───────────────────┘
┌─────┴─────┐
iOS Android
Shared Business Logic Only (KMP):
Shared: Networking, data models, business rules, validation
Platform-specific: UI, navigation, platform APIs
Code reuse: 50-70%
┌──────────┐ ┌──────────────┐ ┌──────────┐
│ SwiftUI │ │ Shared KMP │ │ Jetpack │
│ (iOS) │◄────►│ │◄───►│ Compose │
│ UI │ │ - API Client│ │ (Android)│
└──────────┘ │ - Models │ │ UI │
│ - Validation│ └──────────┘
│ - Business │
│ Rules │
└──────────────┘
Platform-Specific Code
// React Native: Platform-specific modules
import { Platform } from 'react-native';
// Simple platform branching
const styles = {
shadow: Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
},
android: {
elevation: 4,
},
}),
};
// Platform-specific files: Component.ios.tsx / Component.android.tsx
// React Native automatically resolves the correct file
// Native module bridge (for platform APIs not available in RN)
import { NativeModules } from 'react-native';
const { BiometricAuth } = NativeModules;
async function authenticateWithBiometrics() {
try {
const result = await BiometricAuth.authenticate({
reason: 'Verify your identity',
fallbackLabel: 'Use passcode',
});
return result.success;
} catch (error) {
console.error('Biometric auth failed:', error);
return false;
}
}
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| Force cross-platform for everything | Poor UX for platform-specific features | Platform channels for native APIs |
| Ignore platform conventions | App feels foreign on both platforms | Adapt to platform design guidelines |
| Share too much UI code | Lowest common denominator UX | Platform-adaptive components |
| No performance profiling | Assume framework handles performance | Profile on real devices, optimize hot paths |
| Skip native developers entirely | Cannot debug platform-specific issues | At least one native expert per platform |
Cross-platform is a spectrum, not a binary. The best approach shares what makes sense (business logic, networking, data models) and respects what is different (UI conventions, platform APIs, user expectations).