Squarely mobile-app technical inventory — 2026-05-13
Summary
The iOS app is functionally complete and stable: SwiftUI on iOS 17+, ~9,556 Swift LOC across 20 source files, Firebase backend live with 11 deployed Cloud Functions, 150 unit tests across 3 test files, and a hardened crash-resolution history (5 distinct concurrency/Firebase crashes shipped fixes for in April 2026). Last meaningful work was 2026-04-09 (one month idle); the codebase is in a "would launch tomorrow" state from a correctness standpoint. The realistic gap to App Store launch is 2-4 weeks of small, mostly-cosmetic blockers (App Store ID wiring, share sheet, email feedback, domain fix, monetization plumbing, App Store metadata + screenshots). The biggest open strategic question is monetization model, not engineering: recommendation is conditional-continue — finish the punchlist and ship a free v1, defer monetization to v1.1 once daily-puzzle usage data is in hand.
Current state
- Platform: iOS 17+ deployment target, builds on Xcode 16+, Swift 6
- Architecture: SwiftUI + @Observable singletons + NavigationStack
- Backend: Firebase project
squarely-puzzles-12b05(us-central1), Firestore default DB, 11 Cloud Functions on Node 24 runtime, firebase-functions v7 - Repo:
bwilson668/squarely-ios(GitHub), main + feature branches, PR-merge workflow - Last meaningful commit: 2026-04-09 (developer-experience setup + test suite). Crash-fix cluster ran 2026-04-07 to 2026-04-09. One earlier commit 2026-02-15. Otherwise quiet.
- Working tree: clean, on
docs/developer-experience-setupbranch - Source LOC: 9,556 Swift across 20 files in main target.
GameView.swiftalone is 109KB / ~2,970 lines (acknowledged smell, documented in known-issues.md as KI-002) - Tests: 1,835 LOC across 3 unit-test files (
DailyPuzzleServiceTests,GameDataTests,ScoringSystemTests) + a 74-LOC UI test stub. Founder reports 150 tests via commit message64225aa. Coverage focus is the service layer, scoring math, and puzzle-data model. UI tests are smoke-only. - Stability: 5 separate crash classes were patched April 7-9 (daily-game-crash, refreshAll concurrency, MainActor concurrency, async-let cancellation, Firebase SDK SafeCall workaround). All resolved. KI-001 documents the permanent
safeCall()URLSession workaround for the Firebase HTTPSCallable iOS 26 / Swift 6 crash — must not be reverted until Firebase ships a fixed SDK. - Identity: anonymous device ID (
odId), no Firebase Auth. Trade-off: no cross-device sync; documented in ADR-003.
Architecture
- App (
squarely-ios/, 20 Swift files): SwiftUI screens for Home (ContentView), Game (GameView, the 2,970-line behemoth), DailyPuzzle, Calendar, Leaderboard, Stats, Options, Help, About, PrivacyPolicy, GameCreator (dev/admin), GameSelection. Service layer:DailyPuzzleService(Firebase),AnalyticsService(deviceId + events),StatsManager(UserDefaults persistence),ScoringSystem. Bundledpuzzles.json(~2.9MB) ships the offline free-play library. - Backend (
squarely-backend/): singlefunctions/src/index.tshouses all 11 Cloud Functions:healthCheck,rotateDailyPuzzle(scheduled midnight UTC),getTodaysPuzzles,getDailyProgress,submitDailyScore,getDailyLeaderboard,updateDisplayName,getUserStreak,getCalendarData,seedDailyPuzzles,seedCurrentMonth. Firestore rules + indexes in repo. Predeploy runs eslint + tsc. Known issue: monolithic index.ts (KI-003), low priority. - Generator (
squarely-generator/, Python): offline puzzle pipeline producing pre-rated, uniquely-solvable puzzles. SQLite-backed (schema.sql), zero runtime dependencies, exports topuzzles.jsonfor iOS bundling. Files:squarely_core.py(Board, Solver, Symmetries),database.py,pipeline.py,export.py,test_generator.py. Standalone, not invoked by the app. - Docs (
squarely-docs/):technical/holds 6 docs — architecture-decisions.md (5 ADRs), known-issues.md (3 KIs), deployment-guide.md (8-step TestFlight procedure), 2015-12-08-app-scoring-mechanism.md, 2015-12-27-analytics-events.md, 2025-12-27-firebase-setup.md.strategy/has 1 doc (feature-prioritization). Plusbook-introduction-and-how-to-play.md+book-solving-tips.md.
Punchlist to launch
Mining sources: in-code TODOs (5), known-issues.md, deployment-guide.md, prior state-review (2026-04-25), and missing-but-required App Store assets.
- [BLOCKER] Replace
YOUR_APP_IDplaceholder inAboutView.swift:191— currently hardcodeditms-apps://itunes.apple.com/app/idYOUR_APP_ID?action=write-review. Requires App Store Connect record + assigned app ID first. - [BLOCKER] Fix domain mismatch — iOS hardcodes
squarelypuzzles.com(with s); live web issquarelypuzzle.com(no s). Affects AboutView lines 173, 184, 206 (mailto, terms URL, website link). Decide canonical domain + update one side or the other. - [BLOCKER] App Store Connect record — create the app listing, bundle ID confirmation, App Store Connect API key for automated submission if using
/squarely-deployskill. - [BLOCKER] App Store assets — icon at all required sizes, 6.7"/6.5"/5.5" screenshots, description copy, keywords, support URL, privacy policy URL (PrivacyPolicyView exists in-app; needs hosted web version too per App Store rules), age rating questionnaire.
- [BLOCKER] TestFlight verification pass — internal testers install + verify daily puzzle, score submit, leaderboard, calendar all work on a non-dev device. Deployment guide step 8 documents the procedure.
- [BLOCKER] Terms of service URL —
AboutView.swift:184points tosquarelypuzzles.com/termswhich does not exist. Either ship a hosted terms page or remove the link. - [SHOULD] Implement email feedback —
AboutView.swift:172TODO. Currently opensmailto:support@squarelypuzzles.com. Confirm that inbox routes somewhere monitored (Gmail? founder personal?), or wire to an actual feedback form. - [SHOULD] Implement share sheet —
AboutView.swift:197TODO. Needed for any organic word-of-mouth loop.UIActivityViewControllerwrapper, share App Store URL + tagline. - [SHOULD] Monetization plumbing —
AboutView.swift:202TODOopenUpgrade()is a no-op. Decision in section below blocks build-out. If IAP/subscription, need StoreKit2 wiring + a paywall view. If ad-supported, need ad SDK (Google AdMob / AppLovin) integration. If free v1, just hide the button. - [SHOULD] iOS launch event coordination with web + Amazon — per 2026-04-25 state-review: don't ship the iOS app before the web site (squarelypuzzle.com) is updated to Books 2+3 + App Store coming-soon section. Otherwise the web → iOS funnel breaks day one.
- [SHOULD] Crashlytics symbolication verified — deployment guide mentions upload of symbols on Archive. Confirm dSYM upload working end-to-end on a TestFlight build before public submission.
- [SHOULD] Increment build number automation — deployment guide has manual Xcode bump as step 1. The
/squarely-deployskill (RDCO-canonical iOS deployment rails per skill registry) should handle this; verify integration. - [NICE-TO-HAVE] Casual-contact viral share — per growth strategy, the iOS app's potential casual-contact viral loop (Wordle-style shareable result format) is identified as Loop 2 in
growth-strategy.md. Share-sheet implementation (#8) is the prerequisite; a custom share-image renderer is the v1.1 fast-follow. - [NICE-TO-HAVE] GameView.swift refactor — known-issues KI-002. Extract subviews into separate files. No user impact, developer experience only. Defer.
- [NICE-TO-HAVE] Backend module split — known-issues KI-003.
index.tsmodularization. Defer until backend grows past ~15 functions. - [NICE-TO-HAVE] Cross-device sync — ADR-003 future-work. Adds Firebase Auth (anonymous → email/Google upgrade), maps
odIdto auth UID. Not a launch blocker; consider for v1.2. - [NICE-TO-HAVE] Amazon/KDP cross-promotion in-app — surface the 3 paperback books inside the app (About screen module? End-of-puzzle prompt?). Loop the iOS → Amazon engine. Defer to v1.1.
- [NICE-TO-HAVE] Daily push notifications — opt-in reminder to play today's puzzle. Adds an iOS retention lubricant. Needs Firebase Cloud Messaging setup and a notifications permission flow. v1.1 candidate.
- [NICE-TO-HAVE] Game Center integration — global leaderboards via Apple's native infrastructure. Either complement or replace the custom Firestore leaderboard. Defer.
- [NICE-TO-HAVE] App Privacy details (App Store Connect) — beyond the in-app privacy policy, the App Store Connect Privacy section needs filling out (data types collected, tracking declaration). Required before submission so technically a BLOCKER, but a 30-min form-fill, not engineering work.
Monetization model options
Surface the four shapes; founder picks.
Option A: In-App Purchase (IAP) — one-time unlock
- Implementation effort: M. StoreKit2, paywall view, restore-purchases plumbing, App Store Connect IAP product setup.
- Revenue pattern: lumpy, one-time payments. Predictable per-install conversion rate. No recurring billing complexity.
- Friction-to-conversion: low-medium. Players try free tier, hit a paywall (limited puzzles, no daily mode, or ad-removal), pay once to unlock.
- Fit with brand: strong. Aligns with the existing paperback-book purchase pattern (one-time spend for content). No subscription-fatigue resistance from a puzzle audience.
- Risk: revenue per user caps at the one-time price. No long-tail compounding. Needs a meaningful gating decision (what's free vs. paid).
Option B: Subscription
- Implementation effort: L. StoreKit2 subscriptions, monthly/annual tiers, paywall, trial periods, server-side receipt verification (Cloud Function), churn handling, App Store Connect subscription group setup.
- Revenue pattern: recurring. Higher LTV ceiling than IAP. Compounds with retention.
- Friction-to-conversion: high. Puzzle apps face subscription resistance; users expect to pay once for a game, not monthly. Free-trial gates this somewhat.
- Fit with brand: weak-to-medium. Squarely's positioning (indie puzzle, dad-invented) is closer to a paperback-book purchase than a SaaS app. Subscription feels mismatched to the casual-puzzle vibe. Best subscription puzzle case (NYT Games) has 100+ puzzle types and a daily editorial promise.
- Risk: low conversion + churn complexity. Operationally heaviest of the four.
Option C: Ad-supported
- Implementation effort: M. Ad SDK (Google AdMob or AppLovin MAX), ad-placement decisions (banner, interstitial between puzzles, rewarded ad for hints), App Tracking Transparency prompt, ad-mediation setup.
- Revenue pattern: scales with daily active users + impressions. Low per-user but compounds with volume. Predictable RPM curves once you have data.
- Friction-to-conversion: zero — no purchase friction. The cost is UX friction (ad interruptions).
- Fit with brand: medium-weak. A polished wood-and-glass aesthetic clashes with intrusive ads. Banner ads are tolerable; interstitials between every puzzle would degrade the premium feel the iOS aesthetic earns. Rewarded ads for hints fits better than forced interstitials.
- Risk: requires meaningful DAU to generate non-trivial revenue. With current zero-launch installed base, ad revenue is theoretical for months. Ad-tracking + ATT prompt adds privacy-policy and store-listing complexity.
Option D: Hybrid (free + ad-removal IAP)
- Implementation effort: M-L. Combines C (ads default) + a single ad-removal IAP. StoreKit2 + ad SDK + a setting to disable ads after purchase.
- Revenue pattern: ad revenue floor + IAP topline from premium-friction-avoiders. Both compound.
- Friction-to-conversion: low — players who don't pay still produce revenue.
- Fit with brand: strongest. Free entry preserves the dad-invented-indie-puzzle accessibility; the ad-removal IAP rewards engaged users with the polished ad-free experience. Mirrors the existing AboutView
openUpgrade()stub which already implies a "remove ads" pattern. - Risk: most surface area to build (both ad SDK + IAP). Roughly 1.5x the effort of either C or A alone. But: it's the de-facto standard for casual puzzle apps.
Continue-or-park recommendation
Conditional continue. Honest read:
The app is in better shape than the strategy doc implies. 9,556 LOC of working SwiftUI + a hardened Firebase backend + 150 tests is not a project to mothball. The April crash-fix cluster proves the founder (or a contractor — commits are from bwilson668) actively stabilized this code recently; abandoning it would write off real recent investment.
But the strategic context matters:
- Per STRATEGY.md, the load-bearing growth engine for Squarely is Amazon KDP, not the iOS app. The iOS app is a fuel-source for Engine #2 (casual-contact viral loop) which doesn't exist yet and depends on a share-sheet that isn't built.
- Per 2026-04-25 state-review, the web + Amazon surfaces are stale (Books 2 + 3 missing from web; A+ Content on Amazon is 0 modules populated) and those are higher-leverage fixes than iOS launch.
- The trademark is not filed (USPTO verified zero filings); ideally that's in motion before App Store launch makes the brand name more visible.
Recommended posture: ship a free v1 without monetization as a 2-week sprint to clear blockers 1-7 + 11-12. Treat v1 as instrumentation — get the casual-contact viral loop tested, daily-puzzle DAU measured, and store listing live so Amazon ads can co-promote later. Defer the monetization decision to v1.1 when 4-6 weeks of usage data tells us whether ad-supported (need DAU), IAP (need conversion-rate signal), or hybrid is the right shape. Don't park — the app is too close to launch to write off — but don't accelerate monetization plumbing into v1; that's gold-plating ahead of evidence.
If the founder disagrees and wants monetization in v1, recommend Option D (hybrid) as the highest-fit + standard-pattern option. Option B (subscription) is the only one with a strong "don't do this" — the puzzle-app subscription pattern doesn't fit Squarely's positioning.
What the founder picks next
- Monetization model: A (IAP one-time) / B (subscription) / C (ad-supported) / D (hybrid) / defer to v1.1. Surfaced as a separate /decisions click-back page at
~/rdco-hq/public/decisions/2026-05-13-squarely-monetization-pick.html. - Continue vs park: ship v1 free this sprint (recommended) / pause iOS until Amazon engine is lubricated / park entirely. Implicit fourth option in the decision page is "park-the-mobile-bet."
- Domain canonical:
squarelypuzzle.com(web) orsquarelypuzzles.com(iOS hardcoded). Pick one, fix the other side. Listed as Open Question #5 in 2026-04-25 state-review. - Web + Amazon sequencing: per 2026-04-25 recommendation #4, don't ship iOS before web is updated and A+ Content is populated. Confirm this constraint still binds, or accept the risk and ship anyway.
- Trademark filing: gated on founder; not blocking iOS launch per se but ideally in motion before public App Store listing.
Related
- [[STRATEGY.md]] — bet-level strategy and 4-layer targeting system
- [[growth-strategy.md]] — Racecar diagnosis, growth loops, casual-contact viral pattern
- [[../squarely/state-reviews/2026-04-25-squarely-current-state-review.md]] — 4-surface state review (web/iOS/Amazon/trademark)
- Squarely iOS codebase:
/Users/ray/Projects/squarely-ios - Firebase project:
squarely-puzzles-12b05 - Decision page:
~/rdco-hq/public/decisions/2026-05-13-squarely-monetization-pick.html