My personal website — a one-page iOS developer portfolio, plus articles on Swift and iOS development.
- About / Highlights — 10+ years of iOS, 20+ apps shipped, Tech Lead at Oodrive
- Experience — timeline from Mobile Powered (2016) to Oodrive (today)
- Projects & Apps — Work, Oodrive OTP, Meet, Share, ENGIE Particulier, Mhikes, Who's Up, SogeSmart and more
- Skills — Swift 6, SwiftUI, Clean Architecture, modular SPM, and my AI tooling (Claude Code, MCP, Claude Skills)
- Articles — writing about Swift and SwiftUI
Plain HTML5, vanilla CSS (design tokens, grid/flex) and a few lines of vanilla JS. No frameworks, no build step — served by GitHub Pages.
/stats shows exact download numbers for my iOS apps. A GitHub Actions cron
(.github/workflows/appstore-stats.yml) runs every morning: it queries the
App Store Connect API (scripts/fetch-appstore-stats.mjs, zero-dependency
Node), rebuilds data/appstore-stats.json and commits it if it changed.
Numbers come from Apple's Sales & Trends reports — exact download counts,
unlike the opt-in Analytics metrics.
Setup (once): create an App Store Connect API key (Users and Access →
Integrations, role Admin or Finance) and add four repository secrets:
ASC_ISSUER_ID, ASC_KEY_ID, ASC_PRIVATE_KEY (full .p8 content) and
ASC_VENDOR_NUMBER (Sales & Trends → About Reports). Then trigger the
workflow manually once (Actions → App Store stats → Run workflow).
Tests: node --test
The same job requests Apple's App Sessions Standard reports and publishes
apps[].usage: dated daily reports (recent 35 processing days), plus the latest
weekly and monthly instances. It follows pagination, reads every gzip TSV segment,
and replaces each event-date partition when a newer processing date corrects it.
Daily figures are labelled provisional until the report's five-day completeness
window has elapsed. Dates without reports remain absent, never zero-filled.
status distinguishes available, unavailable (not yet generated / possibly
below Apple's privacy threshold), and error (collection failed). A first ongoing
request is asynchronous: rerun the daily workflow after Apple prepares reports.
HTTP failures are logged without publishing API responses or signed URLs.
Sessions and device counts cover only users who opted in. deviceCountSum
is the sum of row-level Unique Devices; devices may occur in more than one
version/dimension, so it must not be labelled exact DAU, WAU, or MAU. Weekly and
monthly figures come from their own reports, not sums of daily unique counts.
The dashboard does not infer J7/J30 retention from these aggregates; consult
App Store Connect's Retention view instead. No SDK or app release is required.
After deploying this change, run Actions → App Store stats → Run workflow (or wait for the daily schedule). Local collection requires the four ASC variables listed above; the repository's GitHub secrets are not available locally.
Apple references: App Sessions, corrections, privacy thresholds.