A practical demonstration of incrementally migrating an Ionic + Capacitor hybrid iOS app to native Swift/SwiftUI.
This repository demonstrates a senior-level architecture for safely migrating a hybrid mobile app (Ionic/React + Capacitor) to native iOS (Swift/SwiftUI) using the Strangler Pattern. Rather than a risky "big bang" rewrite, this approach allows teams to:
- Migrate one feature/screen at a time
- Maintain a working app throughout the migration
- Use feature flags to control rollout
- Share core business logic between hybrid and native layers
- Minimize risk and maintain velocity
This is a simplified "tax app" that demonstrates:
- Hybrid Foundation: An Ionic/React app running in Capacitor on iOS
- Native Router: A Swift coordinator that routes between hybrid and native screens
- Incremental Migration: Two screens migrated from Ionic to SwiftUI (Profile, Tax Year Selection)
- Shared Core: Business logic (TaxYearService) used by both native and hybrid layers
- Feature Flags: Safe rollout of native screens with fallback to hybrid
- Testing Strategy: 59 passing tests using Swift Testing framework covering all migration components
/
├── README.md # This file
├── CLAUDE.md # Implementation guide
├── hybrid-app/ # Ionic + Capacitor app
│ ├── src/ # React/TypeScript source
│ ├── ios/ # iOS project with native code
│ │ └── App/
│ │ └── NativeNavigation/ # Capacitor plugin
│ └── capacitor.config.ts
└── docs/
├── ARCHITECTURE.md # Technical architecture details
└── MIGRATION_NOTES.md # Migration strategy and lessons
- macOS with Xcode 14+
- Node.js 18+ and npm
- Ionic CLI:
npm install -g @ionic/cli - CocoaPods:
sudo gem install cocoapods
-
Clone and install dependencies:
cd hybrid-app npm install -
Build and sync to iOS:
npm run build npx cap sync ios
-
Open in Xcode:
npx cap open ios
-
Run the app:
- Select a simulator or device in Xcode
- Press Run (⌘R)
The migration is broken into phases (see CLAUDE.md for details):
- Phase 0: Documentation & cleanup ✓
- Phase 1: Baseline Ionic navigation ✓
- Phase 2: Native navigation Capacitor plugin ✓
- Phase 3: Swift router/coordinator ✓
- Phase 4: First native screen (Profile) ✓
- Phase 5: Feature flags and fallback ✓
- Phase 6: Second native screen + shared services ✓
- Phase 7: Testing (59 passing tests) ✓
- Phase 8: Documentation polish ✓ (complete)
New native functionality is built alongside the existing hybrid app, gradually replacing it.
A Swift AppRouter decides whether each route should be handled by:
- The Ionic WebView (legacy), or
- A native SwiftUI screen (migrated)
Business logic lives in shared Swift services that can be used by both:
- Native SwiftUI screens
- Capacitor plugins called from Ionic
Each route can be flagged as "native ready" with fallback to hybrid if needed.
Compared to a full rewrite:
- ✅ Lower risk - always have a working app
- ✅ Incremental value - ship native features progressively
- ✅ Team velocity - don't block feature development
- ✅ Learning curve - team learns native iOS gradually
- ✅ Rollback safety - can disable native features if issues arise
Compared to staying hybrid:
- ✅ Better performance - native rendering and animations
- ✅ Platform features - easier access to iOS APIs and SDKs
- ✅ User experience - native look and feel
- ✅ Long-term maintainability - simpler stack
This demo is ideal for:
- Interview prep: Demonstrating senior mobile architecture knowledge
- Migration planning: Proof of concept before starting a real migration
- Team education: Teaching hybrid → native migration patterns
- Architecture reference: Template for production migrations
- ARCHITECTURE.md - Detailed technical architecture
- MIGRATION_NOTES.md - Migration strategy and decision log
- CLAUDE.md - Implementation instructions for AI assistance
This is a learning demo, not production code. It intentionally omits:
- Real authentication or backend integration
- Comprehensive error handling
- Production-grade state management
- Accessibility features
- Analytics and monitoring
- CI/CD pipelines
The focus is on demonstrating the migration architecture clearly.
MIT - This is a demo project for educational purposes.