Skip to content

msadiqaadil/TADAAssignment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TADAAssign

iOS implementation of the air-quality booking assignment.

Quick Start

make setup     # one-time: installs XcodeGen, trusts SPM macros, generates the project
# edit TADAAssign/Configuration/Secrets.xcconfig and paste your AQICN_API_KEY
make open      # opens the project in Xcode

That's it. Cmd+R to run, Cmd+U to run tests.

If Xcode was already open before make setup ran, quit it (Cmd+Q) and run make open so the macro-trust defaults take effect.

CLI alternatives

make build     # build for iPhone 17 Pro Simulator
make test      # run the unit test suite (47 tests)
make clean     # remove generated artifacts

Requirements

  • Xcode 16+
  • iOS 17+ deployment target
  • Homebrew (for the make install-tools step)

Architecture

  • SwiftUI + MapKit — code-based layout, no storyboards
  • The Composable Architecture (TCA) — unidirectional data flow, state management, DI
  • Alamofire — HTTP layer in liveValue clients
  • XcodeGen — project generation from project.yml (no .xcodeproj churn in git)

Layout

TADAAssign/
├── App/               TCA root feature + app entry + navigation stack
├── Features/          One folder per screen (Map, LocationDetail, BookingConfirmation, History, CachedLocations)
│                      Each has a Reducer (.swift) + View (.swift)
├── Clients/           TCA @DependencyClient interfaces with live + mock implementations
├── Models/            Domain models (Coordinate, LocationData, Book, SetLocation, LocationSlot)
├── Configuration/     AppConfig, APIConfig, MapConfig — no scattered constants
├── DesignSystem/      AppFont, Spacing, CornerRadius, LayoutMetrics, AppColor
├── Strings/           L10n typed accessor; strings in Resources/en.lproj/Localizable.strings
├── MockData/          Sample data used by mock clients (kept out of business logic)
└── Resources/         Info.plist, Assets, entitlements, .strings

Mock / Live separation

Every dependency client (AirQualityClient, ReverseGeocodingClient, BookingClient, LocationManagerClient, LocationCacheClient) defines:

  • liveValue — real Alamofire / CoreLocation implementation
  • mockValue / previewValue — sample-data implementation, with data sourced from MockData/
  • testValue — unimplemented stubs (generated by @DependencyClient)

The reducers depend only on the interface. liveValue contains zero hardcoded sample data.

Because the assignment provides no /books backend, the app opts into BookingClient.mockValue at startup via a single line in TADAAssignApp.swift. Swapping to real is a one-line change.

Configuration

Lives in What
Configuration/AppConfig.swift nickname max length, coordinate cache precision
Configuration/APIConfig.swift base URLs, mocked network delay
Configuration/MapConfig.swift fallback coordinate, default zoom span

API key handling

Keys live in TADAAssign/Configuration/Secrets.xcconfig. The file is committed with empty values; paste your own locally and strip them again before pushing.

Key Required? Notes
AQICN_API_KEY Required for real AQI data. Falls back to AQICN's "demo" token (sample stations) if empty. Free at https://aqicn.org/data-platform/token/
ADDRESS_API_KEY Optional BigDataCloud key (bdc_…). Falls back to the keyless free tier if empty. https://www.bigdatacloud.com/

At runtime, APIKeyProvider reads both from Info.plist, which is preprocessed at build time with the xcconfig values.

Coverage notes

Core spec

  • Screen 1 (Map): full-width map with a black center pin (per Figma), top-right AQI badge updating on drag, A/B labels, V button state machine (Set A → Set B → Book)
  • Screen 2 (Detail): location info + nickname input (20 char max); back-with-V-button saves nickname
  • Screen 3 (Confirmation): POST /books mock, shows A/B/price; back button resets state
  • Screen 4 (History): GET /books?year=&month= mock, total count + total price + list

Add-ons (all implemented)

  • Unidirectional data flow (TCA)
  • Clean architecture (Models / Clients / Features / DesignSystem / Configuration)
  • Full DI (TCA @Dependency, switchable mock ↔ live)
  • Coordinate caching (3rd-decimal truncation; same-location matching from spec)
  • Screen 5 (cached locations) reached by tapping an unset A/B label
  • State reset when returning from booking confirmation
  • History tap → Screen 1 pre-filled with selected booking, AQI re-fetched in parallel

Tests

make test

47 tests across 7 suites:

  • CoordinateTests — cache key truncation, same/different location matching, negative coordinates
  • MapFeatureTests — initial state, button progression, onAppear, map drag, set A/B, cache reuse, set-failure alert, booking, label taps, prefill from history, set from cache
  • LocationDetailFeatureTests — nickname init, 20-char truncation, save with/without nickname, slot B
  • BookingConfirmationFeatureTests — view-history + back-to-map delegates
  • HistoryFeatureTests — load books, totals, selection, error handling, idempotent onAppear
  • CachedLocationsFeatureTests — load cache, select for slot A/B
  • AppFeatureTests — navigation pushes, nickname propagation, reset on back, history selection, cached → map

Why the macro-trust step

Xcode 16+ verifies SPM macro plugin binaries by fingerprint (security feature). Each time a macro plugin is rebuilt, Xcode wants explicit re-approval. The defaults write commands in make trust-macros pre-approve them at the user level. There is no project-level way to disable this validation — that's intentional, so a project can't silently auto-trust its own macro code.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors