Understanding Doric's Architecture

Key Components

  • Doric Core: The runtime engine that handles UI rendering and manages JS-native communication.
  • Bridge: Handles bi-directional messaging between JavaScript and native Android/iOS components.
  • Doric Modules: Native-side features exposed to JavaScript through the bridge (e.g., animations, navigation).

Common Integration Stack

Doric apps typically use a JavaScript-based logic layer (often bundled via Webpack) and embed native views via Android or iOS containers. Any desync or misconfiguration in this stack can cause erratic behavior and performance bottlenecks.

Common Troubleshooting Scenarios

1. Inconsistent Layout Rendering

Developers often report layout shifts or blank views, especially when using custom components or nested stacks.

Symptoms: Views render off-screen, overlapping, or disappear entirely

Root Causes:

  • Incorrect layout params (e.g., missing width or height)
  • Improper use of Flexbox-style layout in nested stacks
  • Delayed view inflation due to bridge latency

Fix:

  • Ensure all views have explicit layout values
  • Use Stack and FlexLayout modules appropriately
  • Validate layout behavior in both Android and iOS native shells

2. Memory Leaks During Page Navigation

Doric uses retained JavaScript contexts and native references. If not disposed correctly, memory leaks can accumulate across page transitions.

Issue: Increasing memory usage on every page open/close

Diagnosis: Use Xcode Instruments or Android Profiler to trace retained views and JavaScript references.

Solution:

  • Explicitly call dispose() on modules during onDestroy()
  • Avoid global state in JS unless managed by a singleton or store
  • Use weak references in native extensions where applicable

3. JS-Native Communication Failures

Bridge failures can result in methods not being invoked or responses being dropped.

Console Warning: "CallNative failed: method not found: toast"

Common Causes:

  • Mismatched method name or case sensitivity
  • Missing @DoricMethod annotation on the native side
  • Failure to register modules with registerModule()

Fix: Always confirm native methods are correctly annotated and registered before page launch. Use bridge logging to trace request and response pairs.

Diagnostics and Debugging

Enable Bridge Logs

Set DoricNative.setDebug(true) in the Android/iOS host app to log all bridge messages, errors, and module dispatches.

Use Chrome DevTools with WebSocket Proxy

Doric supports remote debugging of JS logic. Use a WebSocket bridge to link the JS runtime to Chrome DevTools for breakpoints and variable inspection.

Native Profiler Usage

Use Android Profiler or Instruments to monitor FPS, memory usage, and thread contention to isolate rendering and memory issues.

Long-Term Architectural Recommendations

1. Separate Navigation State from UI Logic

Use a centralized store (like Redux or Vuex) to manage navigation and avoid coupling UI transitions with component logic.

2. Module Versioning

Define versioned module interfaces and validate compatibility during initialization. This prevents runtime crashes when updating the native app with stale JS code.

3. Hybrid Rendering Fallbacks

Use native fallback rendering for critical screens where performance is paramount or Doric modules are still maturing.

Best Practices

  • Minimize use of global variables in JS context
  • Dispose unused views explicitly on page transition
  • Profile memory frequently during development
  • Isolate third-party native libraries in their own modules
  • Use defensive coding in native bridge handlers

Conclusion

Doric offers an elegant and lightweight bridge between native and JavaScript worlds, but at scale, it requires disciplined architecture, memory management, and observability. Developers must proactively handle lifecycle events, enforce layout consistency, and monitor bridge communication to maintain reliability. By adopting these strategies, enterprise teams can use Doric to deliver performant mobile experiences while retaining the flexibility of web technologies.

FAQs

1. Can Doric handle large-scale navigation stacks?

Yes, but only with disciplined memory and state management. Always dispose of pages explicitly and avoid keeping deep stacks in memory without navigation guards.

2. How do I debug Doric view rendering issues?

Use the Doric debugger in combination with layout logs on the native side. Log the full view hierarchy and ensure all dimensions are resolved before render.

3. Why is my JS module not invoking native methods?

Check for missing annotations, incorrect method names, or registration errors. Enable bridge logging to trace call paths and ensure correct parameter serialization.

4. Does Doric support code splitting?

Yes. Use bundlers like Webpack to split JS modules by route or feature. This reduces load time and memory footprint in large apps.

5. What are good alternatives for native-heavy screens?

For performance-critical screens, consider writing native views and embedding Doric pages selectively. This hybrid model gives more control without abandoning JS flexibility.