ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

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-PatternConsequenceFix
Force cross-platform for everythingPoor UX for platform-specific featuresPlatform channels for native APIs
Ignore platform conventionsApp feels foreign on both platformsAdapt to platform design guidelines
Share too much UI codeLowest common denominator UXPlatform-adaptive components
No performance profilingAssume framework handles performanceProfile on real devices, optimize hot paths
Skip native developers entirelyCannot debug platform-specific issuesAt 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).

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →