Skip to content

Building Shooting Diary: an offline-first React Native application

When I started building Shooting Diary, I wanted a practical record-keeping tool for sport shooters that would remain useful at a range with unreliable connectivity. From a development perspective, that meant treating local data and offline behavior as core requirements.

This article describes the architecture and the reasons for choosing its tools. Reliability comes from the implementation and testing of those decisions, not from a promise that a framework can prevent every data problem.

The application stack: Expo and React Native

The application uses Expo and React Native. Expo provides a development workflow and access to native capabilities, while React Native supplies the interface foundation. The project can use appropriate native modules where the requirements call for them.

File-based navigation

Expo Router connects the navigation structure with files in the app/ directory. This makes it easier to see how screens are organized.

Typed routes can help catch invalid route references when configured, and deep linking can connect an external link to an application screen. These capabilities still need to be set up and tested for the project's actual navigation.

Why SQLite instead of a simple key-value store?

The application records equipment, ammunition, training sessions, and photographs. Those records have relationships, so a relational database fits the data better than storing unrelated values without an explicit model.

Expo SQLite provides a local database. In this project, that supports several useful decisions:

  • Relationships: configured foreign keys can help maintain links between records.
  • Analysis: SQL queries can calculate totals and trends without loading every record into JavaScript.
  • Interaction: asynchronous database operations help structure work without unnecessarily blocking the interface.

Queries, migrations, and the handling of interrupted operations still require care. Local storage does not itself establish a backup or recovery strategy.

Type safety and input validation

The architecture uses TypeScript and Zod to make data expectations explicit. User input is checked against a schema before it is used, while React Hook Form supports form state and feedback.

The following example keeps the original application identifiers and illustrates the expected fields:

// Example schema for an equipment record
export const weaponSchema = z.object({
  id: z.string().uuid(),
  brand: z.string().min(1, "Brand is required"),
  model: z.string().min(1, "Model is required"),
  caliber: z.string().min(1, "Caliber is required"),
  roundsCount: z.number().int().nonnegative(),
});

A schema can reject missing or incorrectly shaped values. It does not automatically verify every relationship or business rule, so those checks also belong in the relevant operation and tests.

Keeping interface and business logic separate

The interface uses React Native Paper components. Business logic is organized outside presentation components, in custom hooks and services. That includes calculations and the handling of in-app purchases through react-native-iap.

The separation makes it easier to review an operation without also working through the screen layout. It also provides a clearer place to test behavior and investigate regressions.

Images and device feedback

Photographs are part of the record-keeping workflow. The application uses expo-image for image handling, including caching and loading presentation. Image size and memory use remain important on mobile devices.

expo-haptics provides tactile feedback for selected interactions, such as saving a record. Such feedback should support a clear interface rather than replace visible confirmation.

Localization

The application uses i18next for translated content and expo-localization to work with the user's locale. Language, number formatting, and date presentation are separate concerns that need consistent handling.

Planning for localization early makes it easier to add or maintain languages without scattering text throughout the application.

What the architecture supports

Expo, SQLite, validation, and a clear separation of responsibilities give this project a structure for continued development. Offline behavior remains something to verify through realistic scenarios, including interrupted work and changes to stored data.

See the Shooting Diary application for the product described here.

Feel free to reach out

We are here for you

Your message will be read personally by me or someone from the team and we'll get back to you to talk through the details. No sales reps, straight to a practical technical consultation that moves you forward.

Personal approach
Discuss your ideas directly with the person working on your website.
Quick reply
We get back to you with clear next steps.
Looking forward to your message, Karel Sikyr, founder
Discuss your project

Contact Us