← Back

Building Sarmiza

Tax residency and immigration compliance is a high-stakes process fraught with risks. Double taxation, visa overstays, and errors require precise tracking. Most tools are inadequate, offering limited accuracy, usability, and privacy.

Sarmiza simplifies this process. It allows users to set day limits, create timelines, and track progress effortlessly. As the sole designer and front-end engineer at Nomikos, I led its development end-to-end, from user research to front-end implementation in React Native and TypeScript. A reusable design system and iOS-native patterns ensured a polished, consistent user experience.

Where it hurts the most

Our goal was to uncover the core frustrations in tax residency and immigration compliance. Growing up in Hong Kong, I experienced the complexities of navigating multiple jurisdictions firsthand. Interviews with frequent travelers revealed that conflicting rules and tight deadlines added immense pressure. Their biggest frustrations were the manual effort of tracking travel days and the fear of costly errors.

High-Fidelity first

I find it best to start with a high-fidelity mockup or demo. Feedback on a cohesive concept prompts sharper, more actionable feedback than a loose proposal or scattered set of ideas—an insight I learned from Ken Kocienda's Creative Selection.

Early High Fidelity Mockups

That's not to say wireframes, flows, and documentation aren't important. They are vital for surfacing issues, handoff, and alignment. But product development, especially early on, works best as a deductive process: you start with something near-finished, then refine backward.

Final product

Sarmiza combines privacy, security, and elegant design to solve users’ core pain points. The onboarding experience is efficient, guiding users step-by-step to activate their tracker and create a timeline.

To drive subscriptions, we introduced a mid-onboarding paywall featuring a limited-time discounted plan and the app’s value proposition. Respecting user expectations, we ensured it appeared only after the tracker’s value had been demonstrated.

After onboarding, users are introduced to their Dashboard. Inspired by the iOS Widget Stack, I designed and implemented our own to save space and deliver timely, relevant information. Currently, the stack shows a promotional card with a countdown timer for the same limited-time offer. A quick swipe up dismisses it.

Custom day limits and preset trackers are also added and displayed on the Dashboard.

Finally, a summary component on the Dashboard provides a day count breakdown by location for any time period.

Design system foundations

To maintain consistency across iOS and future Android builds, I developed a design system using react-native-unistyles. Centralizing color tokens, typography, and spacing in a single configuration file allowed developers to make global updates with minimal effort.

These breakpoints ensure the layout gracefully adjusts to various iPhone screen sizes. Smaller screens inherit settings from larger ones for consistent scaling.

const breakpoints = {
 xs: 0,
 sm: 375,
 md: 393,
 lg: 430,
};

Color tokens are broken down by brand, surface, and text colors. This helps maintain a unified look while making it easy to adapt to dark mode or future brand updates.

// This code is simplified for the sake of this article
const lightTheme = {
  colors: {
    blue: "#1471AF",
    green: "#1E7069",
    darkBlue: "#1D1D37",
    surface: "#FFFFFF",
    backgroundPrimary: "#FAFAFA",
    textPrimary: "#1E1E1E",
    textMuted: "#D9D9D9",
  },
};

Different text styles—display, heading, label, and body—are defined with responsive font sizes and line heights. This setup ensures readability across device sizes.

// This code is simplified for the sake of this article
const typography = {
  display: {
    fontSize: { sm: 40, md: 48, lg: 56 },
    fontFamily: "SourceSerif_700Bold",
  },
  heading: {
    fontSize: { sm: 24, md: 30, lg: 36 },
    fontFamily: "SourceSerif_600SemiBold",
  },
  body: {
    fontSize: { sm: 14, md: 16, lg: 20 },
    fontFamily: "Poppins_400Regular",
  },
};

I aimed to provide a polished interface for iPhone users while keeping the design adaptable for Android. Modular React Native components, such as bottom sheets and interactive carousels, ensured a consistent experience across the product, reinforcing usability and brand identity.

Timeline creation bottleneck

Timeline creation emerged as a critical bottleneck, with 68% of users dropping out during onboarding. Without a complete travel timeline, they couldn’t leverage core features like custom day limits or preset trackers.

A breakthrough came when an engineer, inspired by a Chinese app, proposed using photo metadata for on-device timeline generation. This Photos-to-Timeline feature cut onboarding time from 15 minutes to under 2, boosting trial conversions from 9% to 15%. Users loved the feature’s speed and privacy-first approach, calling it “magical.”

When users tap “Begin,” the app requests photo permissions with clear privacy messaging. It estimates the processing time based on the selected date range—scanning about 50 photos per second on iOS—and provides a real-time progress bar. Using CameraRoll, the app fetches photos and flags each for further processing, as outlined below:

// This code is simplified for the sake of this article
async function getMedia(fromTime, toTime) {
 setShowLoadingScreen(true);
 const photos = await CameraRoll.getPhotos({
 first: 50000,
 assetType: "All",
 fromTime,
 toTime,
 include: ["location"],
 });
 // Update progress indicator and prepare for next stage
}

Once photos are retrieved, the app filters out high-altitude images and those taken less than one minute apart. It then converts valid coordinates into custom location codes, saving everything locally:

// This code is simplified for the sake of this article
for (const photo of photos.edges) {
  if (photo.node.location.altitude < 5000) {
    const locCode = await convert_coords(photo.node.location);
    // Group entries for timeline display
    rawTimeline.push(locCode);
  }
}

Finally, a cohesive timeline is generated, with an optional cloud backup, all processed entirely on-device to ensure user privacy. This approach provides a seamless and efficient travel log while reinforcing Sarmiza’s commitment to trust and transparency.

Since then, we’ve added timeline imports from Google Maps, Flighty, and App in the Air.

Each import presented unique challenges, requiring a clear and adaptable way to guide users. I designed and implemented Walkthrough Card components to streamline this process.

Privacy as a competitive advantage

Sarmiza's location-based features required balancing functionality with privacy. We adopted an offline-first architecture, required no login, and synced data only via iCloud Backup. Despite the technical challenges, we prioritized transparency and data minimization.

We implemented a multi-layered local storage system. MMKV handles fast, encrypted key-value access, while WatermelonDB supports complex queries. Encryption keys are stored securely in the Device Keychain, eliminating the need for external logins or servers and maintaining full control over sensitive data.

// This code is simplified for the sake of this article
const mmkvStorage = new MMKV();
mmkvStorage.setItem = (key, value) => {
  mmkvStorage.delete(key);
  mmkvStorage.set(key, value);
};

Battery life and data privacy guided our background task strategy. Using distance filters and persist modes, we capture essential travel data with minimal updates. This ensures accurate timelines, power efficiency, and transparency, even with inconsistent connectivity.

// This code is simplified for the sake of this article
BackgroundGeolocation.ready({
 distanceFilter: 100,
 stopOnTerminate: false,
 startOnBoot: true,
 preventSuspend: false,
 maxDaysToPersist: 7,
 persistMode: BackgroundGeolocation.PERSIST_MODE_ALL
});

By combining a robust local storage layer with efficient background tasks, Sarmiza delivers a fully offline experience that protects user trust at every turn.

Takeaways

Building Sarmiza underscored the value of prioritizing user trust in a domain filled with complex compliance requirements. Starting with near-finished, high-fidelity designs enabled actionable feedback and sped up critical features like the Photos-to-Timeline flow, which halved onboarding time and nearly doubled conversions.

By committing to an offline-first architecture, minimal data usage, and local encryption, we created a privacy-first solution that outperformed existing tools. A unified design system ensured consistency across iOS and future Android builds, resulting in a streamlined, transparent product that simplified a high-stakes process.