Univa Learning

Module 1 of 9 Client

Mobile App Development

Level Intermediate Estimated study time 75-90 minutes Prerequisites comfortable with Next.js/React and basic web deployment (this module assumes you already ship web apps and just need the mobile picture)
Video lesson: coming soon

Why this matters for Univa

Univa builds AI apps and websites for Malaysian SMEs on Next.js, TypeScript, Tailwind, Supabase, and Vercel/Cloudflare Pages. Sooner or later a client asks "can this be an app," and the honest answer has at least five different technical shapes: a Progressive Web App (PWA), a Capacitor-wrapped web app, a React Native app, a Flutter app, or a fully native Swift/Kotlin app.

Each shape has a different price tag, a different hiring story, and a different app store story. Getting this wrong in one direction overcharges a client for something that should have been simple; getting it wrong in the other direction under-delivers on something that genuinely needed native code. This module is written to close that gap once, so the decision becomes routine rather than a fresh research project every time a client asks.

Univa is already shipping one of these paths in production: the IGG esports superapp is a PWA, not a store app. That decision was not arbitrary. It is the cheapest and fastest path for a small team already fluent in web tooling, and it avoids Apple's and Google's review process and revenue cuts entirely. But "PWA by default" is a strategic choice, not the only option, and Ahnaf needs the full landscape for three reasons:

  • Client credibility. When a client asks "why isn't this in the App Store," a vague answer sounds like a cost-cutting excuse. A precise answer (store fees, review delay, no new native APIs actually needed) sounds like an engineering decision, because it is one.
  • Knowing when the answer should change. Some client needs genuinely require native code: Bluetooth hardware integration, offline maps, background location tracking, or a client who simply will not accept anything that is not on the App Store. Recognizing these early avoids a rebuild later.
  • Quoting correctly. Native mobile hiring and timelines look nothing like web hiring and timelines. A Flutter or fully native build is not "the web app but for phones," it is closer to a second product with its own specialists, its own release cycle, and its own store fees eating into the client's revenue.

This module gives you the vocabulary and the decision rules to make that call quickly, and explains what is actually happening under the hood of each option, so you are repeating engineering reasoning to clients, not marketing claims.


Core concepts

Before comparing named frameworks, understand the four fundamentally different strategies for building a "mobile app." Every framework below is a variation on one of these four.

1. Fully native.

  • You write platform-specific code (Swift for Apple platforms, Kotlin for Android) that compiles directly into the operating system's own toolkit.
  • There is zero abstraction layer between your code and the OS.
  • This means two separate codebases and, in practice, two separate specialist skill sets, since very few developers are genuinely senior in both.

2. Compile-to-native with a custom rendering engine.

  • The framework compiles your code to real native machine code, but at render time it draws its own UI (buttons, text fields, scroll views) onto a blank canvas instead of using the OS's built-in widgets.
  • Flutter works this way: it paints every pixel itself using its own rendering engine (Skia, with a newer engine called Impeller taking over in recent releases).
  • This is why a Flutter app looks and behaves identically on iOS and Android: it never asks either OS to draw anything, so there is nothing for the two platforms to disagree about.

3. Native bridge or direct native calls.

  • The app's business logic is written in one shared language (JavaScript for React Native, C# for .NET MAUI), but at render time that logic maps onto the real, actual native OS widgets (a genuine iOS UIView, a genuine Android View).
  • Historically this mapping went through an asynchronous "bridge" that serialized every message as JSON text, which was a real performance bottleneck for anything involving frequent updates: scrolling, animation, gestures.
  • As of 2026, React Native's New Architecture (the only architecture shipped since React Native 0.82, released October 2025) replaced that bridge with JSI (JavaScript Interface), which lets JavaScript call native code directly and synchronously.
  • Fabric is the new renderer built on JSI, and TurboModules replaced the old native module system.
  • Net effect: React Native today renders real native widgets with none of the old bridge latency, and third-party production benchmarks report meaningfully faster cold starts and rendering versus the pre-2026 bridge architecture.

4. WebView wrapper.

  • The entire UI is a website (HTML, CSS, JavaScript) rendered inside an embedded browser engine, with a thin native shell around it exposing device APIs (camera, GPS, filesystem, push notifications) through a plugin bridge.
  • Ionic and Capacitor work this way.
  • A PWA is the extreme, shell-less version of the same idea: no native wrapper at all, just a website that declares itself installable via a Web App Manifest (a JSON file describing the app's name, icons, and display mode) and a service worker (a background script that caches assets so the app can load offline).
  • The OS then lets the user pin it to the home screen and run it full-screen, indistinguishable at a glance from a "real" app.

Quick reference: rendering strategy to framework

StrategyWhat actually draws the UIFrameworks that use it
Fully nativeThe OS's own native UI toolkit, directlySwiftUI, Jetpack Compose
Compile-to-native, own canvasThe framework's own rendering engine (not OS widgets)Flutter
Native bridge / direct callsReal OS widgets, driven by shared non-native codeReact Native, .NET MAUI
WebView wrapperA browser engine rendering HTML/CSS/JSIonic, Capacitor, PWA

Four more concepts worth holding onto, because they explain most client confusion:

  • App store distribution versus web distribution. Anything that produces a native binary (fully native, Flutter, React Native, MAUI, or a Capacitor-wrapped app) has to go through Apple's App Store or Google Play, with their review processes, fees, and update delays. A PWA is just a URL; there is no store, no review, no fee, and an update goes live the instant Univa deploys, exactly like pushing a change to a normal website.
  • Capability tiers differ by browser, not just by framework. A PWA's ceiling depends on what the phone's browser engine exposes. Android's Chrome has supported installable PWAs and web push notifications for years, while Apple's Safari only added web push for installed PWAs in iOS 16.4 (March 2023) and still gates some capabilities more tightly than Android does. This is the single most common surprise for clients who assume "PWA" means identical behavior on every phone.
  • Progressive enhancement, not an all-or-nothing bet. None of these choices is permanent. A PWA can be upgraded to a Capacitor-wrapped app later without a rewrite, a Capacitor app can adopt native modules screen by screen, and a React Native app can drop native Swift/Kotlin modules into just the screens that need them. Treat each path as a starting rung on a ladder, not a final architecture decision made once and never revisited.
  • Bundle size and data cost matter for SME end users. A PWA has effectively no "install," just a cached web page; a typical Flutter or React Native app runs 20-50MB or more once installed, and a fully native app can be smaller but still requires a real download. For an SME client's customers on limited mobile data plans, a zero-download PWA can meaningfully lower the barrier to first use compared to a multi-megabyte store download.

The landscape

Frameworks compared

FrameworkLanguageHow it rendersPerformanceEcosystem / maturityBest for
FlutterDartCompiles to native ARM/x64 code; paints its own UI on a canvas (Skia/Impeller engine), bypassing OS widgets entirelyNear-native; very smooth animations since it controls every pixel directlyMature, backed by Google, large widget catalog, single codebase for iOS/Android/web/desktopCustom, highly polished UI that must look pixel-identical across platforms
React NativeJavaScript / TypeScriptNew Architecture (JSI + Fabric + TurboModules) calls native code directly and renders real OS widgetsNear-native since the 2026 New Architecture removed the old async bridge; still slightly behind fully native for the heaviest animation/graphics workVery mature, huge npm ecosystem, Meta-backed, Expo simplifies tooling and buildsTeams already on React/JS/TS who want to share code and hiring pool with an existing web app
IonicHTML / CSS / JS (paired with Angular, React, or Vue)Renders as a website inside a WebView, styled with components designed to mimic native UIWeakest of the group for animation-heavy or graphics-heavy screens; fine for forms, lists, and content-driven appsMature, huge web-skill overlap, usually paired with Capacitor for the native shellContent-driven or forms-driven apps built by web teams with no dedicated mobile specialists
CapacitorAny web stack (React, Next.js, Vue, plain JS)Wraps an existing web app in a native WebView shell; exposes native device APIs (camera, push, filesystem) via a JS-to-native plugin bridgeSame ceiling as any WebView app: fine for typical business apps, weak for 60fps games or heavy animationMature, made by the Ionic team as the modern Cordova successor, works with any web framework (not just Ionic's own UI kit)Turning an existing Next.js/React web app into an installable, store-listable app with minimal new code
.NET MAUIC# / XAMLMaps UI code onto native OS widgets via platform-specific "handlers," a similar rendering approach to React NativeNative-widget performance; solid, though a smaller community means fewer battle-tested edge casesActively maintained by Microsoft, with a confirmed roadmap through .NET 11, but a narrow niche outside enterprise .NET shopsTeams that already run a C#/.NET backend and want one shared UI codebase for mobile plus desktop
SwiftUI / SwiftSwiftCompiles directly to native binary, uses Apple's own UI toolkit with zero abstraction layerBest possible performance and platform fidelity on Apple devicesExtremely mature, Apple's official current-generation framework, full day-one access to every new iOS APIApple-first apps needing brand-new platform features (widgets, Live Activities, App Clips) or performance-critical work (games, camera/AR)
Jetpack Compose / KotlinKotlinCompiles to JVM/native bytecode, uses Android's own UI toolkit with zero abstraction layerBest possible performance and platform fidelity on AndroidExtremely mature, Google's official current-generation toolkit, replaced the older XML-view systemAndroid-first apps needing deep platform integration or top-tier performance
PWAWhatever the underlying web app already uses (React, Next.js, plain JS)Runs entirely inside the phone's browser engine; "installed" means a home-screen icon plus a manifest and service worker for offline caching and full-screen displayIdentical to the underlying website; fine for content, forms, dashboards, and ordering flows; not suited to heavy animation or deep native hardware accessAs mature as the web platform itself; capability varies by OS, with Android's Chrome generally ahead of iOS SafariZero-install, zero-store apps for SMEs and events: loyalty cards, booking, menus, internal tools, one-off event apps

Capability matrix by path

A rough, planning-level view of what each path can reliably reach. Treat "Partial" as "possible but with real caveats worth testing before promising it to a client."

CapabilityPWACapacitorReact NativeFlutterFully native
Works offlineYes (service worker caching)YesYesYesYes
Push notificationsPartial (Android strong, iOS since 16.4 with caveats)YesYesYesYes
Camera / barcode scanningPartial (browser API, permission prompts vary)Yes (plugin)YesYesYes
Biometric auth (Face ID / fingerprint)Partial, limited browser supportYes (plugin)YesYesYes
Bluetooth / hardware integrationNo, not reliablyPartial (plugin-dependent)YesYesYes
Background location trackingNoPartial (OS-limited)YesYesYes
App Store / Play Store listingNo (TWA can list on Play only)YesYesYesYes
Home screen iconYesYesYesYesYes

Release cadence by path

PathHow an update shipsTypical delay before users see it
PWADeploy to Vercel/Cloudflare like any websiteSeconds to minutes
Capacitor-wrapped appWeb content can often hot-update in place; native shell changes need a store resubmissionInstant for web-layer changes, days for native-layer changes (store review)
React Native / FlutterNew build submitted to both storesHours to a few days per store review
Fully native (SwiftUI / Compose)New build submitted to the relevant storeHours to a few days per store review
.NET MAUINew build submitted to both storesHours to a few days per store review

Rough build-cost profile (planning heuristic, not a fixed quote)

PathInitial build cost vs. a plain web appOngoing maintenanceNew tooling or hires required
PWASame, roughly +5% for manifest/service workerSame as maintaining the websiteNone
Capacitor-wrapped app+10-20%, mostly native shell setup and store listing workSlightly higher, occasional native-shell updatesNone (same team, plus a native build environment: Xcode, Android Studio)
React Native2-3x a plain web app, since UI is rebuilt for a native shell even if logic is sharedTwo store pipelines, one shared codebaseUsually none if the team is already JS/TS-fluent
Flutter2-3x a plain web app, entirely new UI codebaseTwo store pipelines, one shared codebaseDart is new; existing JS/TS skills do not transfer directly
Fully native (both platforms)3-4x a plain web app, two full UI codebasesTwo fully separate codebases to maintain long-termTwo new specialist hires (iOS and Android)
.NET MAUI2-3x a plain web appTwo store pipelines, one shared codebaseNew language (C#) and new ecosystem entirely

These multipliers are directional, meant for early scoping conversations with a client, not for a line-item quote. Always validate against the specific feature list before pricing.

A few nuances the tables cannot capture

  • Ionic and Capacitor are not competitors, they are layers. Ionic supplies a native-looking UI component library; Capacitor supplies the native runtime shell and plugin bridge that turns any web app (Ionic-styled or not) into an installable app. A Next.js app can adopt Capacitor directly without ever touching Ionic's component library, which is exactly the path that fits Univa's stack.
  • Hybrid, mixed-architecture apps are normal in production. Many real-world React Native apps drop into a native Swift or Kotlin module for one performance-critical screen (a camera filter, a map, a video player) while keeping the rest of the app in JavaScript. Cross-platform does not mean "100 percent cross-platform code or nothing."
  • .NET MAUI's biggest constraint for Univa is not technical, it is organizational. The framework itself is solid and Microsoft-backed, but adopting it means introducing a second language (C#) and a second backend paradigm into a team built entirely around the JavaScript/TypeScript ecosystem, a much bigger cost than the framework comparison table alone suggests.
  • PWA capability keeps expanding, but slowly and unevenly. Features that were native-app-only a few years ago (background sync, web push, home-screen shortcuts, some offline storage APIs) are now available to PWAs on most Android devices. iOS support trails but has been closing the gap release by release; always verify current iOS Safari support before promising a PWA feature to a client.
  • Expo narrows part of the release-cadence gap for React Native, but not all of it. Expo (the most common React Native toolchain today) supports limited over-the-air updates for JavaScript-only changes, which can ship without a full store review. Any change touching native modules or native permissions still requires a normal store submission, so React Native never fully matches a PWA's instant-deploy cadence.

Malaysia hiring and app store reality

PathMalaysia hiring poolApp store cut / feeFits Univa's existing stack?
PWA (Next.js)No new hire needed, same team builds itNone, no store involvedYes, directly
Capacitor wrapping Next.js/ReactNo new hire needed, same team builds itStandard store fees apply once listed (see below)Yes, directly
React NativeLarge pool; most Malaysian React/JS developers can ramp up quicklyStandard store fees applyMostly yes, same language family (JS/TS)
FlutterGrowing pool (bootcamps and mid-size companies increasingly use it); Dart itself is not transferable outside FlutterStandard store fees applyPartially, requires learning a new language
.NET MAUISmall pool, concentrated in enterprise .NET shopsStandard store fees applyNo, different language and ecosystem entirely
SwiftUI + Jetpack Compose (fully native)Two separate specialist hires needed (iOS and Android); small senior pool, highest day rates of the group locallyStandard store fees applyNo, requires two new specialist hires

Store fee reality, regardless of framework:

  • Apple: a USD 99/year Developer Program fee, a review process that can reject an app for policy reasons unrelated to code quality, and a 30 percent revenue cut, reduced to 15 percent for smaller developers under the Small Business Program (roughly under USD 1M/year in App Store revenue).
  • Google: a USD 25 one-time registration fee, a generally faster and looser review, and a 15-30 percent cut depending on revenue tier.
  • The framework does not change any of this. A cross-platform framework only reduces build cost; it does nothing to reduce store fees, review friction, or the wait for a review to clear.
  • PWAs skip all of it, because they are never submitted to a store at all. A Trusted Web Activity (TWA) can wrap a PWA specifically for a Google Play listing, if store presence is wanted without a full native rebuild. There is no equivalent official path onto Apple's App Store.

Common mistakes to avoid

  • Assuming "PWA" and "app in the App Store" are the same pitch. They are not; a PWA cannot be listed on Apple's App Store at all, only pinned to a home screen. Set this expectation with the client before, not after, the build.
  • Assuming Capacitor gives native-app performance. It gives native-app distribution (store listing, home-screen icon, native APIs). The rendering is still a WebView, so it inherits the same animation ceiling as any web app.
  • Assuming a Flutter or React Native rebuild is a small add-on to an existing web app. It is closer to a second product: new UI code, two store pipelines, and (for Flutter) a new language.
  • Assuming Google Play's looser review means no review at all. Both stores can and do reject apps; Google's is generally faster and less strict, not absent.
  • Forgetting that store fees are recurring, not one-time. The Apple/Google revenue cut applies to every in-app transaction for the life of the app, which matters a great deal when quoting a client on a revenue-share or subscription model.
  • Confusing "no app store listing" with "no discoverability plan." A PWA loses App Store and Play Store search traffic entirely. That is fine for an SME whose customers arrive via a QR code, a WhatsApp link, or a website, but it means discoverability has to be planned into the client's marketing, not assumed away.

How to choose

Work through these in order and stop at the first one that applies.

  • If the client's core need is content, booking, ordering, loyalty, or an internal tool, and there is no hard requirement for App Store or Play Store presence -> build a PWA.
    • Why: no new hires, no store fees, no review delay, instant deploys.
  • If the client insists on "an app I can download from the store," but the functional needs are still basic (forms, content, simple native APIs like camera or push) -> wrap the existing Next.js/React app in Capacitor.
    • Why: no new language, no new hires, just a native shell plus a handful of plugins, and it satisfies the "it's in the App Store" requirement.
  • If the app needs genuinely native-grade performance (games, heavy animation, offline-first sync, AR, real-time media) and the team should stay on one language -> build in React Native.
    • Why: keeps the team on JavaScript/TypeScript and can share types and business logic with the existing Next.js codebase in a monorepo.
  • If the client specifically wants pixel-identical custom UI across iOS and Android, and budget or timeline allow learning a new language, or a Flutter contractor is available and cheaper for the job than a senior React Native developer -> consider Flutter.
    • Why: Flutter's own rendering engine guarantees visual consistency that widget-based frameworks cannot promise.
  • If the client already runs a C#/.NET backend and wants one shared codebase for mobile plus desktop -> consider .NET MAUI, otherwise skip it.
    • Why: outside that specific context it does not align with Univa's stack or hiring pool.
  • If the app needs day-one access to brand-new platform features (iOS widgets, Live Activities, App Clips), or is so performance-critical that even React Native's New Architecture is not enough -> go fully native (SwiftUI for iOS, Jetpack Compose for Android).
    • Why: only a direct native toolkit gets zero-day access to new platform APIs and the absolute performance ceiling; treat this as a subcontracted, separately-quoted specialist build.
  • If the client needs deep hardware integration that Capacitor or React Native cannot reach cleanly (custom Bluetooth POS devices, background geolocation under strict battery limits, offline maps) -> this is also a signal to escalate past WebView-based options, usually to React Native first and fully native only if React Native's plugin ecosystem genuinely cannot cover it.

Univa playbook

Grounded in the Next.js, Supabase, Vercel/Cloudflare reality Univa already runs, in the order Univa should actually reach for each option, and why:

  1. Default: ship as a PWA.
    • Every new SME build starts as a Next.js web app with a manifest and a service worker.
    • This costs nothing extra, needs no new tooling or hires, and matches the freemium-tier-first philosophy: no store fees, no 15-30 percent revenue cut, no review delay, instant updates.
    • Example: this is exactly the model that shipped the IGG esports superapp, and it should stay the default for loyalty apps, booking tools, menus, and internal dashboards for SME clients.
  2. Next rung: Capacitor-wrap the same codebase.
    • If a client pushes for "an app on the App Store," or needs one or two native capabilities a browser cannot reliably deliver (push notification reliability, camera or barcode scanning, a "real" home screen icon that satisfies a client's trust expectations), wrap the existing Next.js/React app in Capacitor rather than rebuilding anything.
    • Same code, same team, same language, just an added native shell and a handful of plugins.
    • Example: quote this as a moderate add-on to an existing PWA build, not a rebuild from scratch.
  3. Escalate to React Native only when performance genuinely demands it.
    • If a client's brief crosses into games, heavy animation, complex offline sync, or hardware integration Capacitor cannot cover cleanly, move to React Native before Flutter.
    • It keeps the team on JavaScript/TypeScript, and in a monorepo it can share types and business logic with the Next.js web app.
    • Example: a booking app that also needs a live, animated seat-map or a real-time multiplayer feature would justify this jump.
  4. Flutter, fully native, and MAUI are subcontracted specialist builds, not in-house defaults.
    • None of these align with Univa's current stack or hiring pool.
    • Quote them separately, price in the specialist day rate, and only take them on when the client's requirement is genuinely native-tier.
    • Example: pixel-identical cross-platform UI points to Flutter; brand-new platform features or top-tier performance point to SwiftUI/Compose; an existing C#/.NET shop points to MAUI.
  5. Concrete standing rule for superapp-style builds (IGG and future event or community apps):
    • Stay PWA-first.
    • Only revisit Capacitor wrapping if the client explicitly asks for app store presence, or if push notification reliability on iOS becomes a proven blocker to adoption, not a hypothetical one.
    • Review this rule per project, not once for all time: an SME client base that shifts from mostly-Android to mixed iOS/Android usage can change the push-notification calculus.

Hands-on exercise

This exercise exists to make the PWA path concrete before you ever pitch it to a client. Pick any small existing Next.js page (a demo page is fine; it does not need to be a live client project). In one evening:

  1. Add a manifest. Write a manifest.json (name, short_name, icons, display: "standalone", theme color) and link it from the page's <head>. Budget about 20 minutes.
  2. Add a service worker. Write a minimal service worker that caches the page shell so it loads offline once installed. Budget about 30 minutes, since debugging caching behavior takes longer than writing it.
  3. Deploy and install. Deploy it (Vercel or Cloudflare Pages both work) and test "Add to Home Screen" on both an Android phone (Chrome) and an iPhone (Safari). Budget about 15 minutes.
  4. Compare notes. Write two or three sentences noting what differs between the two: splash screen behavior, icon appearance, whether push notifications work, whether it launches full-screen without browser chrome on both. Budget about 10 minutes.

Stretch goal (optional, only if time allows): run npx cap init on the same project and get it building inside Android Studio's emulator, just to see how little changes going from "PWA" to "Capacitor-wrapped app." This is the exact upgrade path described in the Univa playbook above, so doing it once by hand makes the decision rule concrete rather than theoretical.


Self-check

  1. What replaced the old JavaScript-to-native "bridge" in React Native's 2026 architecture, and what does it let JavaScript do that it could not do before?
  2. Why does a Flutter app look and behave identically on iOS and Android, in a way a React Native app usually does not?
  3. Name two things a client gives up by shipping a PWA instead of a native or store app, and two things they save.
  4. If a Univa client already has a Next.js web app and wants "an app in the App Store" without a full rebuild, what is the correct next step, and why?
  5. Under what condition should Univa move away from React Native toward fully native SwiftUI/Jetpack Compose?

Answers

  1. JSI (JavaScript Interface), part of React Native's New Architecture (with Fabric as the renderer and TurboModules replacing legacy native modules). It lets JavaScript call native code directly and synchronously instead of serializing async JSON messages across a bridge, removing the old latency bottleneck for scrolling, animation, and gestures.
  2. Flutter does not use the OS's native UI widgets at all; it paints every pixel itself onto its own rendering canvas (Skia/Impeller), so the same drawing code produces an identical visual result on both platforms regardless of how each OS's native widgets look.
  3. Gives up: App Store/Play Store discoverability, and some deep native hardware access (fully reliable push on older iOS versions, certain background or Bluetooth capabilities). Saves: store fees and revenue cuts (up to 30 percent), and the review/approval delay on every update.
  4. Wrap the existing Next.js/React code in Capacitor. It reuses the same codebase and team, adds a native shell plus native API plugins, and can then be submitted to the stores, without a full native or React Native rebuild.
  5. When the requirement crosses into needing brand-new platform features on day one (iOS widgets, Live Activities, App Clips), or performance and graphics demands that even React Native's New Architecture cannot meet (heavy games, AR, camera-intensive processing), and the budget supports hiring separate iOS and Android specialists.

Further reading

Official documentation only; framework tooling changes fast enough that third-party blog posts age out of date within months.