Skip to content
Work

iPhone app · dominoes scorekeeper

App Store v1.2.1

Anota

An iPhone scorekeeper for Dominican-style two-team dominoes, so nobody keeps score on paper.

Anota scoreboard in the middle of a match: one team at 102, the other at 80.
Role
Solo: design, code, release
Timeline
May 2026 to now
Stack
TypeScript, Expo, React Native, StoreKit, AdMob, Jest

On the App Store, version 1.2.1. Public code on GitHub. Status checked Oct 15, 2026.

By the numbers

5

timed steps in the ad startup chain

3

layout tiers, picked from the usable screen height

24

unit tests in 5 files

2

one-time purchases: Remove Ads and Anota Pro

Fig. 1 Ad startup: five steps, each with a timeout
  1. 1 · limit 8 s Foreground Waits for the app to be active. Then it goes on anyway.
  2. 2 · limit 15 s UMP consent Runs the UMP consent flow. An error leaves the decision to step 4.
  3. 3 · limit 5 s ATT status Asks for tracking permission only if iOS has no answer yet. An error means non-personalized ads.
  4. 4 · limit 5 s Consent info Reads whether ads may be requested. A clear no means no ads.
  5. 5 · limit 15 s SDK init Starts the Google Mobile Ads SDK. An error means no ads.

Result

  • Ready The banner slot shows an ad.
  • Not ready No ads. The app works the same.

The scoreboard never waits for this chain: it runs in the background after launch, and a Remove Ads or Anota Pro purchase skips it.

The idea

Anota is an iPhone scorekeeper for Dominican-style two-team dominoes. Both teams, the round history and the controls fit on one screen, so nobody keeps score on paper.

What I built

The app is built with Expo and React Native in strict TypeScript. Match logic is a pure reducer: after every change, a settle step recomputes the winner. A saved match is checked field by field before it loads, so a corrupt save cannot crash the app. Spanish and English share one dictionary type.

Anota is free with a banner ad and has two one-time purchases: Remove Ads at $1.99, and Anota Pro at $2.99, which removes ads and unlocks every theme. Ads start only after the UMP consent and App Tracking Transparency steps. The winner screen renders a 1080 px score card and opens the share sheet.

The hard parts

  1. 01 An ad startup that cannot block the scoreboard

    The ad SDK starts through five steps: wait for the app to be in the foreground, UMP consent, the App Tracking Transparency status, consent info, then SDK init. Each step has its own timeout, so a stalled network or a stuck app state falls back to non-personalized ads or no ads instead of hanging.

    Where in the code: src/ads/ads.ts (opens in a new tab)

  2. 02 Purchases that report the right failure

    The purchase layer registers its listeners before it opens the store connection and puts a timeout on every store call. It ignores store errors that arrive when no purchase is in progress, and it reports a missing product separately from a failed payment.

    Where in the code: src/iap/purchases.ts (opens in a new tab)

  3. 03 One screen on every iPhone

    The scoreboard never scrolls. The app picks one of three size tiers from the usable height, after subtracting the banner ad, so the layout still fits an iPhone SE while an ad is showing.

    Where in the code: src/layout/metrics.ts (opens in a new tab)

What’s next

Prepare the Android version.

Anota winner screen at the end of a match, 202 to 80.
Anota keypad for entering a round’s points.

Try it

Related by idea