{"id":50903787,"url":"https://github.com/hatimhtm/adpulse-ios","last_synced_at":"2026-06-16T05:05:29.657Z","repository":{"id":337492708,"uuid":"1148856626","full_name":"hatimhtm/adpulse-ios","owner":"hatimhtm","description":"Open-source iOS 17+ analytics dashboard for ad-creative performance — SwiftUI · Swift Charts · Live Activities · SwiftData · @Observable · MVVM. Anomaly detection in Z-score. MIT licensed.","archived":false,"fork":false,"pushed_at":"2026-05-10T18:56:46.000Z","size":1233,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-10T19:32:56.534Z","etag":null,"topics":["analytics","anomaly-detection","charts","dynamic-island","ios","ios17","live-activities","mvvm","observable","open-source","swift","swift-charts","swiftdata","swiftui"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hatimhtm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-03T12:56:22.000Z","updated_at":"2026-05-10T18:56:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hatimhtm/adpulse-ios","commit_stats":null,"previous_names":["hatimhtm/ag1dashboard","hatimhtm/adpulse-ios"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/hatimhtm/adpulse-ios","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hatimhtm%2Fadpulse-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hatimhtm%2Fadpulse-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hatimhtm%2Fadpulse-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hatimhtm%2Fadpulse-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hatimhtm","download_url":"https://codeload.github.com/hatimhtm/adpulse-ios/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hatimhtm%2Fadpulse-ios/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34391761,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["analytics","anomaly-detection","charts","dynamic-island","ios","ios17","live-activities","mvvm","observable","open-source","swift","swift-charts","swiftdata","swiftui"],"created_at":"2026-06-16T05:05:28.936Z","updated_at":"2026-06-16T05:05:29.641Z","avatar_url":"https://github.com/hatimhtm.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"assets/hero-banner-dark.svg\" /\u003e\n    \u003cimg src=\"assets/hero-banner.svg\" alt=\"AdPulse — Creative Analytics for iOS\" width=\"100%\" /\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/hatimhtm/adpulse-ios/actions/workflows/ios.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/hatimhtm/adpulse-ios/ios.yml?branch=main\u0026style=for-the-badge\u0026label=CI\u0026labelColor=1A1A1A\u0026color=CCFF00\" alt=\"CI\" /\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/STATUS-OPEN_SOURCE-CCFF00?style=for-the-badge\u0026labelColor=1A1A1A\" alt=\"Open source\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/iOS-17%2B-1A1A1A?style=for-the-badge\u0026logo=apple\u0026logoColor=CCFF00\" alt=\"iOS 17+\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/SwiftUI-1A1A1A?style=for-the-badge\u0026logo=swift\u0026logoColor=CCFF00\" alt=\"SwiftUI\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/License-MIT-1A1A1A?style=for-the-badge\u0026labelColor=1A1A1A\" alt=\"MIT License\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cem\u003eAdPulse is an open-source iOS 17+ analytics dashboard for monitoring ad-creative performance — Meta Ads, ad spend, ROAS, conversions, anomalies. Built as a reference implementation of the modern iOS feature surface: \u003ccode\u003e@Observable\u003c/code\u003e, Symbol Effects, Sensory Feedback, Live Activities, interactive Swift Charts, presentation detents, SwiftData persistence, and Live Activity widgets.\u003c/em\u003e\n\u003c/p\u003e\n\n---\n\n### `/// WHAT IT DOES`\n\nA native dashboard for a creative-economy brand to monitor ad performance across products, creators, content types, and campaigns. Mock data ships in the repo: **1,200 campaign records**, **5 months**, **7 products**, **10 creators**, **5 content types**, **8 marketing angles**. Swap the CSV for a real backend and you have a working production surface.\n\n---\n\n### `/// FEATURES`\n\n| | |\n|---|---|\n| **Adaptive layout** | `NavigationSplitView` on iPad, `TabView` on iPhone — same code, two surfaces |\n| **iOS 17 `@Observable` ViewModel** | No `ObservableObject` / `@Published` boilerplate; computed `filteredCreatives` recomputes on filter mutation |\n| **Symbol Effects** | `.bounce`, `.pulse`, `.wiggle` on KPI tiles, status indicators, refresh controls |\n| **Sensory Feedback** | `.sensoryFeedback(.selection / .impact / .success)` on every meaningful interaction |\n| **Interactive Swift Charts** | ROAS-by-Month and Budget-by-Product with `chartXSelection` / `chartYSelection` |\n| **Live Activities + Dynamic Island** | `ActivityKit` driver + Widget Extension; the filtered cohort streams to the Lock Screen and the Dynamic Island |\n| **SwiftData persistence** | Favorites and search history survive restarts (`@Model` + `@Query`) |\n| **Anomaly detection** | Z-score on ROAS against the cohort mean — surfaces winners and losers in the Overview |\n| **Search suggestions** | iOS 17 `.searchable` with persisted recent-search completion |\n| **Presentation Detents** | Sheets resize to their content (`.presentationDetents([.medium, .large])`) |\n| **Accessibility** | Combined elements, VoiceOver labels + values on every card and chart, Reduce Motion respected |\n| **Settings** | Appearance picker (System / Light / Dark), sensory-feedback toggle, Live Activity control, anomaly threshold slider, data cleanup |\n\n---\n\n### `/// SCREENS`\n\n**Overview** — six headline KPIs · ROAS / Budget charts with selection · Anomalies surfaced when threshold exceeded · Top 5 creatives by ROAS · Top 5 creators by conversions · pull-to-refresh + haptics · error banner when data load fails.\n\n**Creatives List** — smart search backed by SwiftData history · filter by Product / Month / Status / Type · sort six ways · context menus with preview · adaptive sheets for quick view.\n\n**Creative Detail** — eight selectable KPIs with sensory feedback · flow-layout tag stack · star to favorite (persists) · quick actions in toolbar.\n\n**Settings** — appearance, haptics, Live Activity tracking, anomaly threshold, data cleanup, repo \u0026 portfolio links.\n\n**Onboarding** — three-step intro on first launch.\n\n---\n\n### `/// ARCHITECTURE`\n\n```\nMVVM Clean\n├── Models/             Creative, FilterState, FavoriteCreative (@Model), SearchHistoryItem (@Model)\n├── ViewModels/         DashboardViewModel — computed KPIs, filtered cohort, anomalies\n├── Views/\n│   ├── Screens/        OverviewView, CreativesListView, CreativeDetailView, SettingsView, OnboardingView\n│   └── Components/     KPICard, RankingRow, StatusBadge, CreativeRow, AnomalyRow, ErrorBanner, …\n├── Services/           CSVParser, LiveActivityManager, AnomalyDetector\n├── Shared/             CampaignActivityAttributes (cross-target — App + Widget)\n├── Widgets/            AdPulseWidgetBundle, CampaignLiveActivity (Widget Extension target)\n├── Tests/AdPulseTests/ CSVParser, DashboardViewModel, FilterState\n├── Theme/              brutalist palette + design tokens\n└── Resources/Assets.xcassets/  AppIcon.appiconset (1024×1024 universal)\n```\n\nReactive surface is pure `@Observable` — every mutation re-renders dependents automatically. Live Activity state is owned by a singleton `LiveActivityManager`. The Widget Extension imports `Shared/CampaignActivityAttributes.swift` as a member of both targets.\n\n---\n\n### `/// SETUP`\n\n```bash\ngit clone https://github.com/hatimhtm/adpulse-ios.git\ncd adpulse-ios\nopen AdPulse.xcodeproj\n# ⌘R to run on an iOS 17+ simulator or device. ⌘U to run the unit tests.\n```\n\nAll three targets — App, Widget Extension (Live Activity), Unit Tests — are wired up in `AdPulse.xcodeproj`. No extra setup needed.\n\nMock data lives in `AdPulse-Data.csv` — 1,200 rows for the fictional **Vital** wellness brand. Column headers are in French (the original brief was for a French-speaking team); the data is parsed by `Services/CSVParser.swift`. Swap the CSV (or replace the parser with a network fetch) to wire up a real backend.\n\n\u003e **Regenerating the Xcode project.** The project is generated from [`project.yml`](project.yml) via [XcodeGen](https://github.com/yonaskolb/XcodeGen). If you change the file layout, run `xcodegen generate` to refresh `AdPulse.xcodeproj`. Both files are committed; XcodeGen is only required when re-generating.\n\n---\n\n### `/// CONTRIBUTING`\n\nPull requests welcome. The code follows Swift API design guidelines, tabs four spaces wide, and prefers `@Observable` over `ObservableObject` everywhere. Run the tests before opening a PR (`⌘U`).\n\n---\n\n### `/// TECH`\n\n`SwiftUI` · `Swift Charts` · `SwiftData` · `ActivityKit` · `Observation` (`@Observable`) · `WidgetKit` · `MVVM Clean`\n\n---\n\n### `/// LICENSE`\n\nMIT — see [LICENSE](LICENSE). Use it, fork it, ship something with it.\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://hatimelhassak.is-a.dev\"\u003e\u003cimg src=\"https://img.shields.io/badge/PORTFOLIO-1A1A1A?style=for-the-badge\u0026logo=vercel\u0026logoColor=CCFF00\" alt=\"Portfolio\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://cal.com/hatimelhassak/engineering-discovery\"\u003e\u003cimg src=\"https://img.shields.io/badge/BOOK_A_CALL-CCFF00?style=for-the-badge\u0026logo=googlecalendar\u0026logoColor=1A1A1A\" alt=\"Book a call\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.linkedin.com/in/hatim-elhassak/\"\u003e\u003cimg src=\"https://img.shields.io/badge/LINKEDIN-1A1A1A?style=for-the-badge\u0026logo=linkedin\u0026logoColor=CCFF00\" alt=\"LinkedIn\" /\u003e\u003c/a\u003e\n  \u003ca href=\"mailto:hatimelhassak.official@gmail.com\"\u003e\u003cimg src=\"https://img.shields.io/badge/EMAIL-1A1A1A?style=for-the-badge\u0026logo=gmail\u0026logoColor=CCFF00\" alt=\"Email\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ccode\u003e///\u0026nbsp;\u0026nbsp;OPEN FOR NEW WORK\u0026nbsp;\u0026nbsp;///\u0026nbsp;\u0026nbsp;CONTRACT \u0026amp; FREELANCE\u0026nbsp;\u0026nbsp;///\u0026nbsp;\u0026nbsp;REMOTE WORLDWIDE\u0026nbsp;\u0026nbsp;///\u003c/code\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhatimhtm%2Fadpulse-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhatimhtm%2Fadpulse-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhatimhtm%2Fadpulse-ios/lists"}