Skip to content

RyanMKrol/Basket

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

102 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Basket 🧺

A friendly, whimsical pixel-art iOS shopping list. Adding is frictionless (a pinned bottom bar with history-backed suggestions); checking an item off pops a spark burst and an animated strikethrough, then slides it into a faded "Got it" section that clears itself after an hour.

Built with SwiftUI + SwiftData, on-device only (no account, no backend).

The look is a theme (Theme / ThemeStyle): the default is Pastel Dots — creamy cards on a soft green pixel-dot backdrop, with pixel fonts (VT323 + Silkscreen) and fresh fruity accents. Other themes ship in code and can be tried via the BASKET_THEME env var (soft, pixel, dive, cozy, arcade, fresh1fresh10).

Features

  • Quick add — always-visible bottom bar with a green add button; as you type, suggestions float up as one-tap chips: your personal history first (things you've bought in the last month, ranked by frequency + recency), then a built-in food dictionary (SuggestionDictionary) for instant autocomplete. That dictionary unifies the grocery + regional corpora with the emoji table's whole vocabulary, so anything the app can put an emoji on it can also suggest (e.g. "cord" → Cordial). Items already on the list are filtered out.

  • Tap the check circle to check an item off — the check pops with a burst of gold sparks while a strikethrough draws left-to-right, then the row glides into a dimmed "Got it" section (tap it to restore it). Check several off at once and they hold their place until the last spark finishes, then glide down together — so the list never shuffles under your taps.

  • Tap the row (or its faint "+ Qty" chip) to set a quantity. An inline stepper slides down with a smart default unit guessed from the item — pour-y things start in ml, weighed things in g, everything else as a plain count. You can switch units freely from a pill row: every item can be counted in plain "units", and unrecognised items offer the full set (ml/L/g/kg/units), since we can't always know what you mean ("300 ml of milk" vs "1 bottle"). Switching ml↔L or g↔kg keeps the amount; switching to a different kind of unit starts fresh (so it never shows "500 units"). Tap the number itself (between the − and +) to type an exact amount on the keyboard — so a big quantity doesn't mean tapping + over and over. The field is forgiving: it ignores the unit letters ("750 ml" → 750), takes a comma or dot decimal, rounds plain counts to whole numbers, and quietly keeps the old amount if you clear it or type nonsense. The amount shows as a small chip on the row; long names truncate so the chip keeps its place.

  • 1-hour TTL on the "Got it" section, so it tidies itself between shops — or tap Clear all in the section header to empty it immediately.

  • Duplicate-aware — re-adding something already listed bumps + flashes the existing row instead of creating a copy.

  • About sheet — the ⓘ in the header opens a small sheet with the app version and an optional tip jar (☕ / 🥪 / 🎁, in-app purchases via StoreKit). Basket is free; tipping unlocks nothing — but once you've tipped, the Basket title turns into a per-letter rainbow with a solid red heart; tap it to toggle between the rainbow and classic looks (your choice persists), as a small thank-you.

  • Little touches — a sub-second basket flourish on cold launch (never on resume), a full-screen "All done!" celebration when you check off the last item, and quiet living details: a faint time-of-day tint and the occasional seasonal accent (🎃, 🎄…) on the empty state.

  • Playful auto-emoji per item, via a three-stage cascade (Sources/Services/Emoji.swift):

    1. Curated table (EmojiTable, ~1750 entries, generated by tools/gen_emoji.py from inline data + tools/emoji_supplement.txt) covering produce, proteins, dairy, bakery, grains, pantry, drinks, snacks, frozen, prepared dishes, household/toiletry/baby/pet/health goods, and global cuisines — East/South/Southeast Asian, Middle Eastern, African, Latin American and European staples. The matcher prefers the longest keyword match, so prefix collisions resolve correctly (peach≠pea, ginger≠gin, hamburger≠ham).
    2. Semantic fallback (SemanticEmoji) — Apple's on-device word embeddings (NLEmbedding, fully offline) map novel items to the nearest "anchor" food word's emoji (e.g. "Flounder" → 🐟). This also collapses variants: "Frozen peas" resolves to the same glyph as "Peas".
    3. Basket default (🧺) when nothing else fits.

    Coverage is audited against a ~3650-item global grocery corpus (tools/corpus/*.txt, spanning world cuisines) with tools/audit_coverage.swift — currently 100% (0 fall-throughs; ~91% curated, ~9% semantic).

  • A warm tri-colour (green/yellow/tomato) background bloom.

Build & run (CLI, no Xcode GUI)

Requires Xcode's command-line tools and XcodeGen (brew install xcodegen).

./build_run.sh                 # generate → build → install → launch → screenshot
./build_run.sh "iPhone 17"     # target a different simulator

build_run.sh builds by -target with an explicit SUPPORTED_PLATFORMS because this machine's Xcode generates a scheme whose supported-platforms list is empty.

Signing for a device

Simulator builds need no signing. To run on a physical device, drop your Apple Team ID into a git-ignored override (the committed project stays team-agnostic):

echo 'DEVELOPMENT_TEAM = ABCDE12345' > Signing.local.xcconfig
xcodegen generate

Signing.xcconfig (committed) optionally includes Signing.local.xcconfig, so without the local file the build still works for the simulator.

Tests

Pure logic (emoji mapping, suggestion ranking, formatting) is covered two ways:

  • Tests/BasketTests.swift — the XCTest suite, run on the simulator:

    xcodegen generate
    xcodebuild test -project Basket.xcodeproj -scheme Basket \
      -destination 'platform=iOS Simulator,name=iPhone 17 Pro'
  • tools/main.swift — the same source files run natively on macOS (fast, no simulator needed):

    swiftc Sources/Services/Emoji.swift Sources/Services/EmojiTable.swift \
           Sources/Services/SemanticEmoji.swift Sources/Services/Suggestions.swift \
           Sources/Services/SuggestionDictionary.swift Sources/Models/Suggestion.swift \
           Sources/Services/Formatting.swift \
           Sources/Services/Measure.swift Sources/Services/Seasonality.swift \
           tools/main.swift -o /tmp/basket_check && /tmp/basket_check

Note: xcodebuild test and app-icon (asset catalog) compilation require an installed iOS simulator runtime matching the SDK. If you hit "No simulator runtime version … available", run xcodebuild -downloadPlatform iOS.

About

Whimsical pixel-art iOS shopping list app — SwiftUI + SwiftData, on-device only, with history-based suggestions and playful check-off animations

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors