research: mobile app strategy #36

Open
opened 2026-03-23 03:55:32 +00:00 by barrettruth · 0 comments
barrettruth commented 2026-03-23 03:55:32 +00:00

Recommendation

PWA first, Expo second. Start with a Progressive Web App. It requires zero new tooling, zero App Store fees, and ships immediately on the existing Next.js stack. If PWA limitations become blocking (primarily push notification UX on iOS and lack of background sync), graduate to React Native with Expo — the only native option that preserves the TypeScript/React skill set and maximizes code reuse with the existing src/core/ business logic.

Do not pursue Capacitor, KMP, Flutter, or native Swift unless the calculus changes significantly.


Comparison Table

Criteria PWA Expo (React Native) Capacitor SwiftUI KMP Flutter
New language None None (TS/React) None Swift Kotlin Dart
Code reuse with delta Very high (same app) High (src/core/, types, validation) High (static export of web app) None None None
Effort to MVP Days Weeks Weeks Months Months Months
Offline support Service workers (no background sync on iOS) SQLite + expo-sqlite + Drizzle Service workers (same as PWA) Core Data / SQLite SQLDelight drift / sqflite
Push notifications (iOS) Supported since iOS 16.4; limited UX Full native push via Expo Push Service or APNs Full native push via Capacitor plugin Full native push Full native push Full native push
App Store required No Yes ($99/yr Apple Developer Program) Yes ($99/yr) Yes ($99/yr) Yes ($99/yr) Yes ($99/yr)
Cross-platform Yes (any browser) iOS + Android iOS + Android iOS only iOS + Android iOS + Android
Native feel Good (no native gestures/haptics) Excellent Decent (web in native shell) Best Excellent Very good

1. Progressive Web App (PWA)

What it is

A PWA turns the existing Next.js web app into an installable, offline-capable app via service workers, a web manifest, and the "Add to Home Screen" flow. No App Store involved.

Effort

Very low. The delta web app already runs in a browser. Adding PWA support means:

  • A manifest.json with icons and theme colors.
  • A service worker for caching (Workbox or next-pwa / @serwist/next).
  • Cache strategies: cache-first for static assets, network-first for API responses, stale-while-revalidate for task lists.

This is days of work, not weeks.

Code sharing

100%. It is the same app — no separate codebase.

Offline support

Service workers can cache the app shell and API responses. For a todo app, a network-first strategy for /api/tasks with a cache fallback provides a solid offline read experience. Offline writes can be queued in IndexedDB and replayed via the Background Sync API — but iOS does not support Background Sync. On iOS, queued writes would need to be replayed when the app regains focus, which is less reliable.

Push notifications

Supported on iOS since 16.4, but with significant caveats:

  • The PWA must be installed to the home screen first (no in-browser push).
  • Users must manually enable notifications in Safari settings.
  • No silent notifications, no rich media, no Time Sensitive notifications, no Live Activities.
  • No background sync means notification-triggered data fetches are limited.
  • The multi-step install-then-permit flow means real-world opt-in rates are much lower than native.

App Store

Not applicable. No $99/yr fee, no review process, no TestFlight. Updates deploy instantly via the existing VPS.

Limitations

  • No access to native APIs beyond what the Web API surface provides (no haptics, no Siri integration, no widget support).
  • iOS still forces WebKit for all browsers, so PWA capabilities are entirely at Apple's discretion.
  • No background fetch or periodic sync on iOS — the app can only do work while it is in the foreground.
  • The "Add to Home Screen" flow is not discoverable; there is no install prompt on iOS Safari like there is on Android.

Verdict

The right starting point. Covers 80-90% of mobile needs for a self-hosted todo app with near-zero incremental effort. The main gaps are push notification UX and background sync on iOS.


2. React Native / Expo

What it is

A native mobile app built with React and TypeScript, compiled to actual native code. Expo is the managed framework around React Native that handles build tooling, OTA updates, and native module configuration.

Effort

Medium. Expo has gotten significantly easier — npx create-expo-app scaffolds a working project, Expo Router provides file-based routing similar to Next.js, and EAS Build handles native compilation in the cloud (no local Xcode/Android Studio setup required for builds, though Xcode is needed for simulator testing).

The UI layer must be rewritten since React Native uses <View>, <Text>, <Pressable> instead of HTML elements. No <div>, no CSS-in-the-traditional-sense (though StyleSheet is similar, and NativeWind brings Tailwind-like classes to React Native). The existing Tailwind + base-ui component library would not transfer directly.

Estimated timeline: 3-5 weeks to a functional MVP with task CRUD, list views, and auth.

Code sharing with delta

This is the strongest argument for Expo. The existing src/core/ layer — task.ts, recurrence.ts, dag.ts, urgency.ts, settings.ts, automation.ts — is pure TypeScript business logic that takes a db: Db parameter. These modules have no React or DOM dependencies and could be extracted into a shared package in a monorepo (pnpm workspaces). The Expo app would import the same core logic, types from types.ts, and validation schemas.

Additionally, the Expo app can call the same REST API routes (/api/tasks, /api/auth, /api/automations) that the web app uses. The API contract is already defined and tested.

A realistic monorepo structure:

packages/
  core/        -- extracted from src/core/ (task.ts, recurrence.ts, etc.)
  web/         -- existing Next.js app
  mobile/      -- Expo app

Offline support

Excellent. expo-sqlite provides a local SQLite database on-device. Combined with Drizzle ORM (which supports SQLite), the same schema definitions could theoretically be shared. Options:

  • expo-sqlite + Drizzle: type-safe local DB with manual sync to the server API.
  • PowerSync: real-time sync layer on top of SQLite (third-party service).
  • Turso: per-user embedded SQLite with cloud sync.

For a self-hosted app, manual sync via the existing REST API is the most appropriate approach — no third-party sync services needed.

Push notifications

Full native push notification support. Two paths:

  • Expo Push Service: simplest option. The delta server sends a POST to Expo's push API with the device token. Expo handles routing to APNs/FCM. Free for reasonable volumes.
  • Direct APNs/FCM: more control, no Expo dependency. The expo-notifications library still handles the client side; the server talks directly to Apple/Google.

Both approaches work well with a self-hosted backend. The server stores device tokens (linked to user accounts in the existing SQLite DB) and sends notifications when relevant events occur.

App Store deployment

  • Apple Developer Program: $99/yr, required.
  • TestFlight: included, supports up to 10,000 external testers. Internal testing (up to 100 testers) does not require App Review.
  • Review process: most apps reviewed within 24-48 hours. Common rejection reasons for small apps: missing privacy policy, iPad rendering issues, login flow problems for reviewers. Expo's documentation has good guidance on avoiding these.
  • OTA updates: EAS Update allows pushing JS bundle updates without a full App Store review, as long as no native code changes. This is a significant advantage for rapid iteration.
  • Android: Google Play developer account is a one-time $25 fee. Review is typically faster and less strict.

Verdict

The best native option if PWA limitations become blocking. Maximizes code reuse, stays in TypeScript/React, and has the most mature ecosystem. The cost is maintaining a second UI layer and the App Store overhead.


3. Capacitor (+ Ionic)

What it is

Capacitor wraps a static export of the web app in a native WebView shell. The web code runs as-is inside a native container, with access to native device APIs via Capacitor plugins.

Effort

Low to medium. The main work is converting the Next.js app to a static export (output: 'export' in next.config.ts), which is a significant architectural constraint.

The problem for delta

Delta uses server-side features extensively: server actions (src/app/actions/), API routes (src/app/api/), server components, and Drizzle ORM queries that run on the server. A static export eliminates all of these. The Capacitor app would need to:

  • Remove all use server directives and server actions.
  • Replace server-side data fetching with client-side fetch calls to the delta API.
  • Disable Next.js image optimization (images: { unoptimized: true }).
  • Handle routing differently (no dynamic server routes, client-side navigation has known issues with Capacitor).

This effectively means maintaining two build configurations of the same app, or rewriting significant portions for client-side-only operation. The "wrap your existing app" pitch breaks down when the app relies on server-side rendering.

The latest guidance (early 2026) also recommends against using Ionic's UI components with Next.js, calling the integration "very hacky," and suggests Konsta UI instead.

Offline support

Same as PWA — service workers and cache strategies. No advantage over a PWA here.

Push notifications

Full native push via Capacitor's push notification plugin. Works well.

App Store deployment

Same requirements as Expo: $99/yr Apple Developer Program, App Review, etc. Apple generally accepts WebView-wrapped apps, but they can be rejected if the app is deemed to provide no value beyond what a website offers. For a personal/small-user app, this is unlikely to be an issue.

Verdict

Not recommended for delta. The static export requirement conflicts with the existing server-side architecture. The effort to maintain a static export build alongside the server-rendered web app negates the supposed simplicity. A PWA provides the same web-based experience without the native shell overhead, and Expo provides a genuinely native experience if that is needed.


4. Swift / SwiftUI (iOS only)

What it is

A fully native iOS app written in Swift using Apple's SwiftUI framework.

Effort

High. SwiftUI is a new language and framework to learn. While the declarative syntax has conceptual similarities to React, the ecosystem is entirely different: URLSession for networking, Codable for JSON parsing, Core Data or SwiftData for persistence, Combine for reactivity. Every component must be built from scratch — no code sharing with the web app.

Estimated timeline: 6-10 weeks for a solo developer new to Swift, assuming prior programming experience.

Code sharing with delta

None. Different language, different runtime, different everything. The only shared artifact is the REST API contract.

Offline support

Excellent. Core Data, SwiftData, or direct SQLite access provide robust local storage. Apple's ecosystem has mature tooling for offline-first patterns.

Push notifications

Best-in-class. Full access to all iOS notification features: Time Sensitive, Live Activities, provisional notifications, notification grouping, rich media.

App Store deployment

Same $99/yr. SwiftUI apps tend to have smoother reviews since they use Apple's own framework.

Cross-platform

iOS only. An Android version would require a completely separate app in Kotlin/Java.

Verdict

Not recommended. The effort is too high for a solo developer who already has a working TypeScript/React web app. The iOS-only limitation is also a drawback. The only scenario where SwiftUI makes sense is if native iOS polish (widgets, Siri integration, Live Activities) becomes a hard requirement.


5. Kotlin Multiplatform (KMP)

What it is

JetBrains' framework for sharing Kotlin code across iOS and Android. Business logic is written once in Kotlin; UI can be native (SwiftUI/Jetpack Compose) or shared via Compose Multiplatform.

Effort

High. Requires learning Kotlin, Gradle build system, and either Compose Multiplatform or platform-specific UI frameworks. KMP adoption has grown (7% to 23% in 18 months), and Google announced official support at I/O 2024, but the ecosystem is still maturing compared to React Native.

Code sharing with delta

None with the existing TypeScript codebase. The delta REST API would be consumed as any HTTP client, but no business logic, types, or validation could be reused.

Offline support

Good. SQLDelight provides type-safe SQLite access across platforms.

Push notifications

Full native push on both platforms.

Verdict

Not recommended. KMP is a strong choice for teams with Kotlin/Android experience, but for a solo developer coming from TypeScript/React with an existing Next.js app, the learning curve and zero code reuse make it impractical.


6. Flutter

What it is

Google's cross-platform framework using the Dart language. Renders its own UI via the Skia/Impeller engine rather than using native components.

Effort

Medium-high. Dart is a new language but relatively easy to learn for TypeScript developers (both are typed, garbage-collected, and support async/await). Flutter's developer tooling (hot reload, DevTools) is excellent. However, no code can be shared with the existing Next.js app.

Estimated timeline: 4-6 weeks for an MVP.

Code sharing with delta

None. Dart and TypeScript are different languages. The REST API is the only integration point.

Offline support

Good. drift (formerly moor) and sqflite provide SQLite access. Flutter has mature offline-first patterns.

Push notifications

Full native push via firebase_messaging or platform-specific plugins.

Performance

Flutter consistently benchmarks well: 60-120 FPS rendering, smaller binary sizes than React Native, and the Impeller rendering engine (stable in 2025) provides smooth animations. However, apps don't use native UI components, so they can feel subtly "off" on both platforms.

Verdict

Not recommended. While Flutter is a capable framework with excellent tooling, the Dart language requirement means zero code reuse with the existing TypeScript/React codebase. For a solo developer who already knows React, Expo provides a faster path with better code sharing.


Phase 1: PWA (now)

Add PWA support to the existing Next.js app:

  • manifest.json with app name, icons, theme color.
  • Service worker via @serwist/next or similar (Workbox-based).
  • Cache strategies: cache-first for static assets, network-first for API data.
  • Offline task queue in IndexedDB for writes when offline (replay on reconnect).
  • Test "Add to Home Screen" on iPhone.

This provides an installable, offline-capable mobile experience with zero additional infrastructure, zero App Store fees, and immediate deployment via the existing VPS.

Phase 2: Expo (if/when PWA hits walls)

Triggers for moving to Phase 2:

  • Push notification opt-in rates are too low due to the PWA install-then-permit flow.
  • Background sync is needed (iOS PWAs cannot sync in the background).
  • Native features are desired (haptics, widgets, Siri shortcuts).
  • The "web app in Safari" feel is not acceptable.

If Phase 2 is triggered:

  • Restructure into a pnpm monorepo with packages/core, packages/web, packages/mobile.
  • Extract src/core/ into the shared package.
  • Build the Expo app consuming the shared core and calling the existing REST API.
  • Enroll in the Apple Developer Program ($99/yr).
  • Use EAS Build for native compilation and EAS Update for OTA JS updates.

What to skip

  • Capacitor: the static export requirement conflicts with delta's server-side architecture.
  • SwiftUI: too much effort, iOS only, no code reuse.
  • KMP: wrong language ecosystem for this project.
  • Flutter: wrong language ecosystem for this project.
## Recommendation **PWA first, Expo second.** Start with a Progressive Web App. It requires zero new tooling, zero App Store fees, and ships immediately on the existing Next.js stack. If PWA limitations become blocking (primarily push notification UX on iOS and lack of background sync), graduate to React Native with Expo — the only native option that preserves the TypeScript/React skill set and maximizes code reuse with the existing `src/core/` business logic. Do not pursue Capacitor, KMP, Flutter, or native Swift unless the calculus changes significantly. --- ## Comparison Table | Criteria | PWA | Expo (React Native) | Capacitor | SwiftUI | KMP | Flutter | |---|---|---|---|---|---|---| | **New language** | None | None (TS/React) | None | Swift | Kotlin | Dart | | **Code reuse with delta** | Very high (same app) | High (`src/core/`, types, validation) | High (static export of web app) | None | None | None | | **Effort to MVP** | Days | Weeks | Weeks | Months | Months | Months | | **Offline support** | Service workers (no background sync on iOS) | SQLite + expo-sqlite + Drizzle | Service workers (same as PWA) | Core Data / SQLite | SQLDelight | drift / sqflite | | **Push notifications (iOS)** | Supported since iOS 16.4; limited UX | Full native push via Expo Push Service or APNs | Full native push via Capacitor plugin | Full native push | Full native push | Full native push | | **App Store required** | No | Yes ($99/yr Apple Developer Program) | Yes ($99/yr) | Yes ($99/yr) | Yes ($99/yr) | Yes ($99/yr) | | **Cross-platform** | Yes (any browser) | iOS + Android | iOS + Android | iOS only | iOS + Android | iOS + Android | | **Native feel** | Good (no native gestures/haptics) | Excellent | Decent (web in native shell) | Best | Excellent | Very good | --- ## 1. Progressive Web App (PWA) ### What it is A PWA turns the existing Next.js web app into an installable, offline-capable app via service workers, a web manifest, and the "Add to Home Screen" flow. No App Store involved. ### Effort Very low. The delta web app already runs in a browser. Adding PWA support means: - A `manifest.json` with icons and theme colors. - A service worker for caching (Workbox or `next-pwa` / `@serwist/next`). - Cache strategies: cache-first for static assets, network-first for API responses, stale-while-revalidate for task lists. This is days of work, not weeks. ### Code sharing 100%. It is the same app — no separate codebase. ### Offline support Service workers can cache the app shell and API responses. For a todo app, a network-first strategy for `/api/tasks` with a cache fallback provides a solid offline read experience. Offline writes can be queued in IndexedDB and replayed via the Background Sync API — but **iOS does not support Background Sync**. On iOS, queued writes would need to be replayed when the app regains focus, which is less reliable. ### Push notifications Supported on iOS since 16.4, but with significant caveats: - The PWA **must** be installed to the home screen first (no in-browser push). - Users must manually enable notifications in Safari settings. - No silent notifications, no rich media, no Time Sensitive notifications, no Live Activities. - No background sync means notification-triggered data fetches are limited. - The multi-step install-then-permit flow means real-world opt-in rates are much lower than native. ### App Store Not applicable. No $99/yr fee, no review process, no TestFlight. Updates deploy instantly via the existing VPS. ### Limitations - No access to native APIs beyond what the Web API surface provides (no haptics, no Siri integration, no widget support). - iOS still forces WebKit for all browsers, so PWA capabilities are entirely at Apple's discretion. - No background fetch or periodic sync on iOS — the app can only do work while it is in the foreground. - The "Add to Home Screen" flow is not discoverable; there is no install prompt on iOS Safari like there is on Android. ### Verdict The right starting point. Covers 80-90% of mobile needs for a self-hosted todo app with near-zero incremental effort. The main gaps are push notification UX and background sync on iOS. --- ## 2. React Native / Expo ### What it is A native mobile app built with React and TypeScript, compiled to actual native code. Expo is the managed framework around React Native that handles build tooling, OTA updates, and native module configuration. ### Effort Medium. Expo has gotten significantly easier — `npx create-expo-app` scaffolds a working project, Expo Router provides file-based routing similar to Next.js, and EAS Build handles native compilation in the cloud (no local Xcode/Android Studio setup required for builds, though Xcode is needed for simulator testing). The UI layer must be rewritten since React Native uses `<View>`, `<Text>`, `<Pressable>` instead of HTML elements. No `<div>`, no CSS-in-the-traditional-sense (though `StyleSheet` is similar, and NativeWind brings Tailwind-like classes to React Native). The existing Tailwind + base-ui component library would not transfer directly. Estimated timeline: 3-5 weeks to a functional MVP with task CRUD, list views, and auth. ### Code sharing with delta This is the strongest argument for Expo. The existing `src/core/` layer — `task.ts`, `recurrence.ts`, `dag.ts`, `urgency.ts`, `settings.ts`, `automation.ts` — is pure TypeScript business logic that takes a `db: Db` parameter. These modules have no React or DOM dependencies and could be extracted into a shared package in a monorepo (pnpm workspaces). The Expo app would import the same core logic, types from `types.ts`, and validation schemas. Additionally, the Expo app can call the same REST API routes (`/api/tasks`, `/api/auth`, `/api/automations`) that the web app uses. The API contract is already defined and tested. A realistic monorepo structure: ``` packages/ core/ -- extracted from src/core/ (task.ts, recurrence.ts, etc.) web/ -- existing Next.js app mobile/ -- Expo app ``` ### Offline support Excellent. `expo-sqlite` provides a local SQLite database on-device. Combined with Drizzle ORM (which supports SQLite), the same schema definitions could theoretically be shared. Options: - **expo-sqlite + Drizzle**: type-safe local DB with manual sync to the server API. - **PowerSync**: real-time sync layer on top of SQLite (third-party service). - **Turso**: per-user embedded SQLite with cloud sync. For a self-hosted app, manual sync via the existing REST API is the most appropriate approach — no third-party sync services needed. ### Push notifications Full native push notification support. Two paths: - **Expo Push Service**: simplest option. The delta server sends a POST to Expo's push API with the device token. Expo handles routing to APNs/FCM. Free for reasonable volumes. - **Direct APNs/FCM**: more control, no Expo dependency. The `expo-notifications` library still handles the client side; the server talks directly to Apple/Google. Both approaches work well with a self-hosted backend. The server stores device tokens (linked to user accounts in the existing SQLite DB) and sends notifications when relevant events occur. ### App Store deployment - **Apple Developer Program**: $99/yr, required. - **TestFlight**: included, supports up to 10,000 external testers. Internal testing (up to 100 testers) does not require App Review. - **Review process**: most apps reviewed within 24-48 hours. Common rejection reasons for small apps: missing privacy policy, iPad rendering issues, login flow problems for reviewers. Expo's documentation has good guidance on avoiding these. - **OTA updates**: EAS Update allows pushing JS bundle updates without a full App Store review, as long as no native code changes. This is a significant advantage for rapid iteration. - **Android**: Google Play developer account is a one-time $25 fee. Review is typically faster and less strict. ### Verdict The best native option if PWA limitations become blocking. Maximizes code reuse, stays in TypeScript/React, and has the most mature ecosystem. The cost is maintaining a second UI layer and the App Store overhead. --- ## 3. Capacitor (+ Ionic) ### What it is Capacitor wraps a static export of the web app in a native WebView shell. The web code runs as-is inside a native container, with access to native device APIs via Capacitor plugins. ### Effort Low to medium. The main work is converting the Next.js app to a static export (`output: 'export'` in `next.config.ts`), which is a significant architectural constraint. ### The problem for delta Delta uses server-side features extensively: server actions (`src/app/actions/`), API routes (`src/app/api/`), server components, and Drizzle ORM queries that run on the server. A static export eliminates all of these. The Capacitor app would need to: - Remove all `use server` directives and server actions. - Replace server-side data fetching with client-side `fetch` calls to the delta API. - Disable Next.js image optimization (`images: { unoptimized: true }`). - Handle routing differently (no dynamic server routes, client-side navigation has known issues with Capacitor). This effectively means maintaining two build configurations of the same app, or rewriting significant portions for client-side-only operation. The "wrap your existing app" pitch breaks down when the app relies on server-side rendering. The latest guidance (early 2026) also recommends against using Ionic's UI components with Next.js, calling the integration "very hacky," and suggests Konsta UI instead. ### Offline support Same as PWA — service workers and cache strategies. No advantage over a PWA here. ### Push notifications Full native push via Capacitor's push notification plugin. Works well. ### App Store deployment Same requirements as Expo: $99/yr Apple Developer Program, App Review, etc. Apple generally accepts WebView-wrapped apps, but they can be rejected if the app is deemed to provide no value beyond what a website offers. For a personal/small-user app, this is unlikely to be an issue. ### Verdict Not recommended for delta. The static export requirement conflicts with the existing server-side architecture. The effort to maintain a static export build alongside the server-rendered web app negates the supposed simplicity. A PWA provides the same web-based experience without the native shell overhead, and Expo provides a genuinely native experience if that is needed. --- ## 4. Swift / SwiftUI (iOS only) ### What it is A fully native iOS app written in Swift using Apple's SwiftUI framework. ### Effort High. SwiftUI is a new language and framework to learn. While the declarative syntax has conceptual similarities to React, the ecosystem is entirely different: URLSession for networking, Codable for JSON parsing, Core Data or SwiftData for persistence, Combine for reactivity. Every component must be built from scratch — no code sharing with the web app. Estimated timeline: 6-10 weeks for a solo developer new to Swift, assuming prior programming experience. ### Code sharing with delta None. Different language, different runtime, different everything. The only shared artifact is the REST API contract. ### Offline support Excellent. Core Data, SwiftData, or direct SQLite access provide robust local storage. Apple's ecosystem has mature tooling for offline-first patterns. ### Push notifications Best-in-class. Full access to all iOS notification features: Time Sensitive, Live Activities, provisional notifications, notification grouping, rich media. ### App Store deployment Same $99/yr. SwiftUI apps tend to have smoother reviews since they use Apple's own framework. ### Cross-platform iOS only. An Android version would require a completely separate app in Kotlin/Java. ### Verdict Not recommended. The effort is too high for a solo developer who already has a working TypeScript/React web app. The iOS-only limitation is also a drawback. The only scenario where SwiftUI makes sense is if native iOS polish (widgets, Siri integration, Live Activities) becomes a hard requirement. --- ## 5. Kotlin Multiplatform (KMP) ### What it is JetBrains' framework for sharing Kotlin code across iOS and Android. Business logic is written once in Kotlin; UI can be native (SwiftUI/Jetpack Compose) or shared via Compose Multiplatform. ### Effort High. Requires learning Kotlin, Gradle build system, and either Compose Multiplatform or platform-specific UI frameworks. KMP adoption has grown (7% to 23% in 18 months), and Google announced official support at I/O 2024, but the ecosystem is still maturing compared to React Native. ### Code sharing with delta None with the existing TypeScript codebase. The delta REST API would be consumed as any HTTP client, but no business logic, types, or validation could be reused. ### Offline support Good. SQLDelight provides type-safe SQLite access across platforms. ### Push notifications Full native push on both platforms. ### Verdict Not recommended. KMP is a strong choice for teams with Kotlin/Android experience, but for a solo developer coming from TypeScript/React with an existing Next.js app, the learning curve and zero code reuse make it impractical. --- ## 6. Flutter ### What it is Google's cross-platform framework using the Dart language. Renders its own UI via the Skia/Impeller engine rather than using native components. ### Effort Medium-high. Dart is a new language but relatively easy to learn for TypeScript developers (both are typed, garbage-collected, and support async/await). Flutter's developer tooling (hot reload, DevTools) is excellent. However, no code can be shared with the existing Next.js app. Estimated timeline: 4-6 weeks for an MVP. ### Code sharing with delta None. Dart and TypeScript are different languages. The REST API is the only integration point. ### Offline support Good. `drift` (formerly `moor`) and `sqflite` provide SQLite access. Flutter has mature offline-first patterns. ### Push notifications Full native push via `firebase_messaging` or platform-specific plugins. ### Performance Flutter consistently benchmarks well: 60-120 FPS rendering, smaller binary sizes than React Native, and the Impeller rendering engine (stable in 2025) provides smooth animations. However, apps don't use native UI components, so they can feel subtly "off" on both platforms. ### Verdict Not recommended. While Flutter is a capable framework with excellent tooling, the Dart language requirement means zero code reuse with the existing TypeScript/React codebase. For a solo developer who already knows React, Expo provides a faster path with better code sharing. --- ## Recommended Strategy ### Phase 1: PWA (now) Add PWA support to the existing Next.js app: - `manifest.json` with app name, icons, theme color. - Service worker via `@serwist/next` or similar (Workbox-based). - Cache strategies: cache-first for static assets, network-first for API data. - Offline task queue in IndexedDB for writes when offline (replay on reconnect). - Test "Add to Home Screen" on iPhone. This provides an installable, offline-capable mobile experience with zero additional infrastructure, zero App Store fees, and immediate deployment via the existing VPS. ### Phase 2: Expo (if/when PWA hits walls) Triggers for moving to Phase 2: - Push notification opt-in rates are too low due to the PWA install-then-permit flow. - Background sync is needed (iOS PWAs cannot sync in the background). - Native features are desired (haptics, widgets, Siri shortcuts). - The "web app in Safari" feel is not acceptable. If Phase 2 is triggered: - Restructure into a pnpm monorepo with `packages/core`, `packages/web`, `packages/mobile`. - Extract `src/core/` into the shared package. - Build the Expo app consuming the shared core and calling the existing REST API. - Enroll in the Apple Developer Program ($99/yr). - Use EAS Build for native compilation and EAS Update for OTA JS updates. ### What to skip - **Capacitor**: the static export requirement conflicts with delta's server-side architecture. - **SwiftUI**: too much effort, iOS only, no code reuse. - **KMP**: wrong language ecosystem for this project. - **Flutter**: wrong language ecosystem for this project.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
barrettruth/delta#36
No description provided.