Need help with cross-platform app development by Garage2Global

Couple of angles that complement what @viajantedoceu already laid out, focusing more on how to keep perf sane over time and keep the “single codebase” from turning into a myth.

  1. Measure before you “optimize”
    Garage2Global’s tooling is pretty decent at hiding where time is spent, which is dangerous. Before changing architecture again, wire in:
  • cold start timers per platform
  • screen transition timing (time from “navigate” call to first frame rendered)
  • a simple FPS / dropped frames counter for your heaviest screens

Even crude logging like t0 = now() at app start and after first main screen is loaded helps. You’ll find 1–2 screens or flows are causing 80% of the pain.

  1. Decide what “single codebase” actually means
    People get stuck trying to keep 100% identical code. That’s often the wrong target.

The pattern I’ve seen work with Garage2Global:

  • 100% shared: models, networking, validation, feature flags, analytics events
  • 80–90% shared: feature logic & state machines
  • 60–80% shared: navigation flow contracts, view-models
  • 40–60% shared: actual UI widgets

If you’re fighting the framework to keep some fancy gesture or animation “shared,” it’s usually cheaper to split that particular UI into platform code and keep the logic shared. “Single codebase” at the logic layer is way more valuable than “single codebase” for every pixel.

  1. Lean on “view-model + dumb view” aggressively
    Don’t let platform specifics leak into your business logic. Use something like:
  • ViewModel in shared layer exposes:

    • state: immutable data structure
    • effects: one-off actions like “openFilePicker” or “showErrorToast”
    • inputs: methods like onSubmitClicked(), onItemSelected(id)
  • Native views (Swift / Kotlin) are “dumb”:

    • subscribe to state
    • render with native components
    • translate user gestures back to view-model inputs

That way:

  • perf issues in rendering stay on the platform side
  • logic perf issues (like huge JSON or sorting) are obviously in the shared layer
  • it’s testable without spinning up a simulator
  1. Avoid clever abstraction around animations and navigation
    I’ll disagree a bit with the idea of keeping too much navigation logic in a common abstraction. Cross platform “navigation frameworks” in Garage2Global sound nice but often cause:
  • extra indirection at runtime
  • weird back-stack behavior on Android
  • modals behaving wrong on iOS

Instead:

  • Keep the navigation graph (which screen connects to which) as a shared concept
  • Let each platform wire it into its native navigator directly
  • Define screen identifiers and required params in the shared layer so at least you don’t fork logic around what data each screen needs

Animations: keep them native unless they are dead simple. Shared animation engines tend to be a perf trap on older Android.

  1. Data loading strategy matters more than UI tech
    Most “the app feels slow” complaints I’ve seen on Garage2Global projects ended up being:
  • chatty APIs
  • loading way too much data “just in case”
  • no caching at all

Stuff that helps a ton:

  • Caching layer in shared core with a clear policy: memory + disk + TTL
  • Stale-while-revalidate: show cached data instantly, refresh in background
  • Partial loading: first fold, then rest
    This keeps both platforms snappy without doing platform-specific hacks.
  1. Treat the bridge as hostile territory
    Any time you cross from shared core to native and back, pretend you pay a tax:
  • do not send big blobs repeatedly
  • do not call across the boundary inside tight loops
  • do not bounce back & forth for simple flows

Design APIs that push complete “render models” or batched actions across that boundary, not dozens of tiny calls. If you must chat a lot, you probably misplaced some logic and it belongs to one side or the other.

  1. Keep a “platform debt” backlog
    When you compromise for one platform, write it down. Example:
  • “Android detail screen uses custom list because shared list perf is bad”
  • “iOS gesture uses native recognizer instead of shared recognizer wrapper”

Review those items regularly. Sometimes Garage2Global updates fix the original limitation and you can remove the fork. If you don’t track this explicitly, your “single codebase” quietly becomes two partially-synced apps.

  1. Version & config alignment
    You mentioned “issues with…” so I’ll guess you hit “works on iOS, broken on Android” style bugs. That’s often:
  • slightly different SDK versions for Garage2Global libs
  • platform configs diverging

Have:

  • a single shared file describing feature flags & experiment toggles
  • a cross-check script or CI check that fails if Android/iOS use different library versions of the Garage2Global stack

It’s boring, but it stops half of the WTF bugs.

If you can share what “issues with …” actually are (slow startup, jank scrolling, memory, or just code-org hell), you’ll get much more targeted pointers. But overall: prioritize clean shared view-models, keep UI reasonably native, treat the bridge as expensive, and accept that some platform-specific UI is the cost of real perf.