Native Mobile Exploration: Swift/Kotlin vs Capacitor/Ionic
Status: Exploratory | Date: 2026-03-27 | Author: Josh Lockhart
This document evaluates whether Objectuve should move from the current Capacitor/Ionic hybrid approach to native mobile apps built with Swift (iOS) and Kotlin (Android).
Current State
Objectuve ships as a single Vue 3 + Ionic codebase wrapped by Capacitor 8.1.x. The web app runs inside a native WebView on iOS and Android.
| Metric | Value |
|---|---|
| Vue components | 108 |
| Page-level views | 17 |
| Composables | ~15 |
| Pinia stores | 1 (theme) |
| Active Capacitor plugins | 1 (Camera) |
| Installed Capacitor plugins | 5 (Camera, Haptics, Keyboard, StatusBar, App) |
| Custom native code (Swift/Kotlin) | 0 lines |
| GraphQL queries/mutations | All in graphql.js via Apollo Client |
| State management | Apollo cache (primary), Pinia (minimal) |
| Lazy-loaded routes | 2 of 17 |
Key takeaway: The native shells are completely stock. AppDelegate.swift and MainActivity.kt contain zero custom code. All logic lives in the web layer.
Option A: Stay with Capacitor/Ionic (Current)
Pros
- Single codebase. One set of 108 components serves iOS, Android, and web. Bug fixes and features ship to all platforms simultaneously.
- Web-first development speed. Vite HMR, browser DevTools, and instant reload. No Xcode/Gradle build cycles during day-to-day development.
- Ionic's native-feeling components.
IonPage,IonModal,IonActionSheet, etc. adapt to platform conventions (iOS swipe-back, Material bottom sheets) without platform-specific code. - Tiny native surface area. Only Camera is actively used. Fewer native dependencies means fewer platform-specific bugs, fewer App Store review surprises, and simpler upgrades.
- Team size fit. A solo developer maintaining one codebase is far more sustainable than maintaining three (web + iOS + Android).
- Web deployment as first-class citizen. The same build serves
app.objectuve.comvia Firebase Hosting. Native apps just wrap it. - Existing investment. 108 components, 17 views, full test suite (Vitest + Cypress), Storybook with 31+ stories, Apollo client with caching and subscriptions. This represents months of work.
- Capacitor 8 is mature. Plugin ecosystem covers most native APIs. Community and Ionic team actively maintain it.
Cons
- WebView performance ceiling. Complex animations, heavy lists, and transitions run in a WebView, not on the GPU-accelerated native UI layer. Perceptible on older/mid-range Android devices.
- No access to cutting-edge platform APIs. New iOS/Android features (Live Activities, Dynamic Island, Widgets, Material You theming) require Capacitor plugins that may not exist or lag behind.
- App Store perception. Some reviewers have historically been stricter with WebView-based apps, though this has improved significantly.
- Debugging native issues is harder. When something breaks at the Capacitor bridge layer, debugging requires jumping between web and native toolchains.
- Bundle size. The full web runtime (Vue, Ionic, Apollo, Tailwind) ships inside the app. Current Capacitor apps tend to be larger than lean native equivalents.
- Limited offline capability. While possible with service workers and Apollo cache persistence, offline-first patterns are more natural in native.
Improvements available without going native
- Add route-level code splitting (only 2 of 17 routes are lazy-loaded today)
- Enable Apollo cache persistence for offline reads
- ✅ Haptics integration complete (shipped via PR #488, OBJ-165) — tactile feedback on badge unlock, streak ticks, goal completion
- StatusBar integration for polish (plugin already installed)
- Use Capacitor's
@aparajita/capacitor-secure-storagefor encrypted local storage - Add push notifications via
@capacitor/push-notifications - Explore
@capacitor/local-notificationsfor streak reminders - Consider Capacitor background tasks for sync
Option B: Fully Native (Swift + Kotlin)
Two separate native apps, each with their own UI layer, consuming the same GraphQL API.
Pros
- Peak performance. Native UI rendering, 60fps animations, smooth gesture handling. SwiftUI and Jetpack Compose are GPU-accelerated and optimized for their respective platforms.
- Full platform API access. Live Activities, Widgets, Dynamic Island, App Intents (Siri), Material You, WearOS/watchOS — all available immediately on release.
- Smaller app binary. No WebView runtime, no bundled JavaScript. Native apps can be significantly smaller.
- Platform-idiomatic UX. Navigation patterns, haptics, transitions, and gestures feel exactly right for each platform without any abstraction layer translating them.
- Better App Store reception. Native apps tend to get smoother review processes and can leverage platform-specific features that App Store editors highlight.
- Superior offline support. CoreData (iOS) and Room (Android) provide robust, well-documented offline-first data layers.
- Deeper OS integration. Background refresh, Shortcuts/App Intents, Focus filters, notification categories with actions — all trivial in native, complex or impossible in Capacitor.
- Gamification animations. Badge unlocks, XP animations, streak celebrations, and rank-up sequences would look and feel significantly better with native animation frameworks (Lottie native, SwiftUI animations, Compose animations).
Cons
- Triple the codebase. Web (Vue), iOS (Swift), Android (Kotlin) — three implementations of every screen, every interaction, every edge case. The 108 components and 17 views would need to be rebuilt twice.
- Massive upfront investment. Conservatively 3-6 months of focused work per platform to reach feature parity with the current app, assuming one developer.
- Ongoing maintenance multiplier. Every feature, bug fix, and design change must be implemented and tested on all three platforms. A change that takes 1 hour in the hybrid app takes 3+ hours across native apps.
- GraphQL client differences. Apollo iOS (Swift) and Apollo Kotlin have different APIs, caching semantics, and code generation approaches than Apollo Client (JS). The current
graphql.jsquery definitions don't transfer. - Two new tech stacks to master. SwiftUI and Jetpack Compose are both relatively new and evolving. Learning curves are real, even for experienced developers.
- Web app still needed.
app.objectuve.commust continue to work. Going native doesn't eliminate the web codebase — it adds to it. - Testing infrastructure rebuild. RSpec stays, but Vitest/Cypress tests don't transfer. Need XCTest (iOS) and Espresso/Compose Testing (Android) suites from scratch.
- CI/CD complexity. Currently: one build pipeline. With native: three pipelines (web, iOS, Android), each with their own signing, provisioning, and deployment.
- Capacitor already does what you need. With only Camera actively used and zero custom native code, the current app isn't hitting Capacitor's limitations.
What the migration path looks like
- Set up Swift/Kotlin projects with GraphQL clients (Apollo iOS, Apollo Kotlin)
- Implement authentication (Clerk SDKs exist for iOS and Android)
- Rebuild all 17 views and 108 components in SwiftUI and Jetpack Compose
- Implement Apollo cache, offline storage, and state management natively
- Rebuild gamification UI (badges, XP, streaks, ranks) with native animations
- Set up CI/CD for both platforms (Fastlane partially exists for iOS)
- Set up native test suites (XCTest, Espresso)
- Maintain web app in parallel for
app.objectuve.com
Option C: Kotlin Multiplatform (KMP) + Native UI
A middle ground: shared business logic in Kotlin, with SwiftUI and Compose for UI.
Pros
- Shared business logic. GraphQL client, data models, caching, auth token management, and gamification calculations written once in Kotlin, shared across iOS and Android.
- Native UI on both platforms. SwiftUI for iOS, Jetpack Compose for Android. Full platform fidelity without a WebView.
- Incremental adoption. Can start by sharing just the data layer while keeping native UI separate. Doesn't require all-or-nothing.
- Growing ecosystem. JetBrains actively invests in KMP. Apollo Kotlin already supports KMP. Ktor (networking) is KMP-native.
Cons
- Still need two UI layers. SwiftUI and Compose are different frameworks. Every screen is still built twice.
- Still doesn't solve the web problem.
app.objectuve.comneeds its own frontend. You'd maintain Kotlin shared + SwiftUI + Compose + Vue web = four codebases. - KMP learning curve. Kotlin Multiplatform's tooling (especially for iOS interop via Kotlin/Native) has rough edges. Gradle configuration is complex.
- iOS developers may resist. The Swift community generally prefers pure Swift solutions over Kotlin-generated frameworks.
- Debugging shared code on iOS is painful. Stepping through Kotlin/Native code in Xcode is not a great experience.
Decision Framework
Answer these questions to clarify the right path:
1. What pain are you actually feeling today?
If the answer is "none, but I'm thinking ahead" — that favors staying with Capacitor and investing in optimizations (code splitting, cache persistence, haptics).
If the answer is "animations feel janky" or "I need Widgets/Live Activities" — those are concrete signals that native could deliver value.
2. How important is the web app?
If app.objectuve.com must remain a full-featured experience, going native means maintaining the Vue app plus native apps. The web app doesn't go away.
If the web app could become a lightweight landing/redirect to app stores, native becomes more viable since you're not maintaining three full frontends.
3. What's the team trajectory?
Solo developer: Capacitor/Ionic is the pragmatic choice. One codebase, one test suite, one deployment pipeline.
Growing team (1 iOS + 1 Android dev): Native becomes feasible. Each developer owns their platform.
Hiring native contractors: Possible but coordination overhead is high, especially for maintaining design consistency and feature parity.
4. Where does Objectuve's competitive advantage come from?
If it's the gamification system (streaks, XP, badges, ranks) — native animations would meaningfully enhance the dopamine loop that drives retention. Badge unlocks, rank-up celebrations, and streak visuals would feel more rewarding.
If it's the anti-social philosophy (quick sessions, action over consumption) — the current WebView performance is likely sufficient for 10-minute sessions with tap-heavy interactions.
5. What's the cost of waiting?
Capacitor isn't going anywhere. The migration path from Capacitor to native is always available. Features built in Vue today inform what native screens need to look like. There's no "window closing" pressure.
Objectuve-Specific Considerations
| Factor | Implication |
|---|---|
| Anti-social app (~10 min/day) | Short sessions reduce exposure to WebView performance gaps. Users aren't scrolling infinite feeds. |
| Gamification is the product | Badge unlock animations, XP popups, streak celebrations, and rank-up sequences are the highest-value moments. Native would make these feel premium. |
| Solo developer | Maintaining 3 codebases is a 3x multiplier on every feature. This is the single biggest factor against going native today. |
| Pre-launch phase | Iterating fast matters more than platform polish right now. Ship, learn, then optimize. |
| Camera-only native usage | The app isn't pushing Capacitor's boundaries. There's no native pain being felt. |
| GraphQL + Apollo | Apollo iOS and Apollo Kotlin exist but have different APIs. Migration is doable but non-trivial. |
Summary Matrix
| Dimension | Capacitor/Ionic (Current) | Fully Native (Swift + Kotlin) | KMP + Native UI |
|---|---|---|---|
| Development speed | Fast (1 codebase) | Slow (3 codebases) | Medium (2 UI + 1 shared) |
| Performance | Good (WebView) | Excellent (native) | Excellent (native) |
| Platform API access | Limited (plugin-dependent) | Full | Full |
| Maintenance burden | Low | High (3x) | Medium-High (2-3x) |
| Gamification polish | Adequate | Premium | Premium |
| Team size requirement | 1 developer | 2-3 developers | 2 developers |
| Web app compatibility | Built-in | Separate codebase | Separate codebase |
| Migration effort | N/A (current) | 6-12 months | 4-8 months |
| Risk | Low (proven) | High (full rewrite) | Medium (newer ecosystem) |
Suggested Next Steps
If exploring further:
- Prototype one screen natively. Pick the Dashboard or Goal Detail view. Build it in SwiftUI and Jetpack Compose to get a real feel for the effort and the quality difference.
- Benchmark WebView vs native animations. Record badge unlock and streak animations on a mid-range Android device in both Capacitor and a native prototype. See if the difference is meaningful.
- Audit platform API wishlist. List specific iOS/Android features you want (Widgets, Live Activities, Watch complications, etc.) and check if Capacitor plugins exist.
- Optimize the current app first. Add lazy loading to remaining 15 routes, enable Apollo cache persistence, integrate Haptics and StatusBar plugins. See if the polished hybrid app meets your bar.
Last updated: 2026-05-11