{"id":13558953,"url":"https://github.com/criticalmaps/criticalmaps-ios","last_synced_at":"2026-01-11T22:03:43.163Z","repository":{"id":20551897,"uuid":"23831718","full_name":"criticalmaps/criticalmaps-ios","owner":"criticalmaps","description":"Critical Maps iOS App 🚲✊","archived":false,"fork":false,"pushed_at":"2025-12-29T20:03:43.000Z","size":108195,"stargazers_count":310,"open_issues_count":3,"forks_count":42,"subscribers_count":8,"default_branch":"main","last_synced_at":"2026-01-01T06:36:00.556Z","etag":null,"topics":["bike","bike-data","composable-architecture","criticalmaps","criticalmass","swiftui","tca"],"latest_commit_sha":null,"homepage":"http://www.criticalmaps.net","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/criticalmaps.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","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},"funding":{"open_collective":"criticalmaps"}},"created_at":"2014-09-09T12:04:17.000Z","updated_at":"2025-12-29T16:21:36.000Z","dependencies_parsed_at":"2023-12-22T09:01:09.700Z","dependency_job_id":"8e56903f-c1d9-45e8-a2b3-26af195c3de6","html_url":"https://github.com/criticalmaps/criticalmaps-ios","commit_stats":{"total_commits":1613,"total_committers":37,"mean_commits":43.5945945945946,"dds":0.759454432734036,"last_synced_commit":"55fbc3b658bf701433faa02bfe89a6ae9587258a"},"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"purl":"pkg:github/criticalmaps/criticalmaps-ios","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/criticalmaps%2Fcriticalmaps-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/criticalmaps%2Fcriticalmaps-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/criticalmaps%2Fcriticalmaps-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/criticalmaps%2Fcriticalmaps-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/criticalmaps","download_url":"https://codeload.github.com/criticalmaps/criticalmaps-ios/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/criticalmaps%2Fcriticalmaps-ios/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28324853,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T18:42:50.174Z","status":"ssl_error","status_checked_at":"2026-01-11T18:39:13.842Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["bike","bike-data","composable-architecture","criticalmaps","criticalmass","swiftui","tca"],"created_at":"2024-08-01T12:05:15.277Z","updated_at":"2026-01-11T22:03:43.153Z","avatar_url":"https://github.com/criticalmaps.png","language":"Swift","readme":"\u003cp align=\"center\"\u003e\u003ca href=\"https://itunes.apple.com/app/critical-maps/id918669647\"\u003e\u003cimg src=\"_images/Icon.svg\" width=\"250\" /\u003e\u003c/a\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"https://itunes.apple.com/app/critical-maps/id918669647\"\u003e\u003cimg src=\"_images/appstore-badge.png\" width=\"150\" /\u003e\u003c/a\u003e\u003c/p\u003e\n\n## Critical Maps iOS App\n\n[![CI](https://github.com/criticalmaps/criticalmaps-ios/actions/workflows/tests.yml/badge.svg)](https://github.com/criticalmaps/criticalmaps-ios/actions/workflows/tests.yml)\n\u003ca title=\"Crowdin\" target=\"_blank\" href=\"https://crowdin.com/project/critical-maps\"\u003e\n\t\u003cimg src=\"https://badges.crowdin.net/critical-maps/localized.svg\" alt=\"Localized Status\" /\u003e\n\u003c/a\u003e\n\n## What's \"Critical Mass\"?\n\n\u003e Critical Mass has been described as 'monthly political-protest rides', and characterized as being part of a social movement.\n\nhttp://en.wikipedia.org/wiki/Critical_Mass_(cycling)\n\n## What's this app?\n\nThis iOS app is made for Critical Maps. It tracks your location and shares it with all other participants of the Critical Mass bicycle protest.\n\n## How the App Works\n\nCritical Maps connects cyclists during Critical Mass rides through real-time location sharing and communication.\n\n### Core Functionality\n- **🗺️ Real-time Location Tracking**: Your location is shared anonymously with other riders on an interactive map\n- **💬 Live Chat System**: Communicate with all participants in real-time during rides\n- **📅 Next Ride Events**: Discover upcoming Critical Mass events in your area\n- **🌐 Social Integration**: Stay connected through an integrated Mastodon feed\n- **🔒 Privacy-Focused**: Anonymous participation with observationmode. Additional protection through privacy zones.\n- **🎨 Customizable Experience**: Personalize your rider appearance and app settings\n\n## Project Setup\n\nThe iOS client's logic is built in the [`The Composable Architecture`](https://github.com/pointfreeco/swift-composable-architecture) and the UI is built in SwiftUI.\n\n**Minimum platform requirements**: iOS 17.0\n\n### Architecture Overview\n\n#### Modular Design Philosophy\nThe application follows a hyper-modularized architecture with feature modules:\n\n**Core Features:**\n- `AppFeature` - Main app coordination and navigation\n- `MapFeature` - Interactive map with real-time rider locations\n- `ChatFeature` - Real-time messaging system\n- `NextRideFeature` - Event discovery and management\n- `SettingsFeature` - User preferences and app configuration\n- `SocialFeature` - Mastodon feed integration\n\n**Supporting Modules:**\n- `ApiClient` - Network layer and API communication\n- `SharedModels` - Data structures (e.g. `Rider`, `Location`, `Coordinate`)\n- `Styleguide` - Design system and UI components\n- `L10n` - Internationalization and localization\n- `IDProvider` - Unique identifier generation for anonymous riders\n\n#### Benefits of Modularization\n- **Faster Build Times**: Work on individual features without building the entire app\n- **Improved SwiftUI Previews**: More stable preview environments for each feature\n- **Independent Development**: Features can be developed and tested in isolation\n- **Mini-Apps**: Each feature can be built as a standalone app for development\n- **Scalable Architecture**: Easy to add new features without affecting existing code\n\n#### Data Flow \u0026 State Management\nThe app uses TCA's unidirectional data flow:\n1. **Actions** represent user interactions and system events\n2. **Reducers** handle state mutations and side effects\n3. **Effects** manage asynchronous operations (API calls, location services)\n4. **State** is the single source of truth for each feature\n\nExample: Location sharing flow\n```\nUser enables tracking → LocationAction → LocationReducer → API Effect → State Update → UI Re-render\n```\n\n### Getting Started\n\nThis repo contains both the client for running the entire [Critical Maps](https://itunes.apple.com/app/critical-maps/id918669647) application, as well as an extensive test suite.\n\n#### Quick Start\n1. **Clone the repository**:\n2. **Open the Xcode project**: `CriticalMaps.xcodeproj`\n3. **Run the app**: Select the `Critical Maps` target in Xcode and run (`⌘R`)\n\n\nInstall dependencies individually:\n```sh\nmake bootstrap  # Install Swift tools (SwiftLint, SwiftFormat, SwiftGen)\nmake ruby         # Install Ruby dependencies (bundler, fastlane)\n```\n\n### Assets \u0026 Code Generation\n\n#### SwiftGen Integration\nThe project uses type-safe assets generated with [SwiftGen](https://github.com/SwiftGen/SwiftGen):\n\n```sh\n# After adding new images or localization strings\nmake assets\n```\n\nThis generates strongly-typed Swift code for:\n- **Images**: `Asset.Images.iconName` instead of `\"icon-name\"`\n- **Localizations**: `L10n.buttonSave` instead of `NSLocalizedString`\n\n\n### Testing\n\nThe project includes comprehensive testing across all feature modules:\n\n#### Running Tests\n```sh\n# Run all tests\n⌘U in Xcode\n\n# Run specific feature tests\nxcodebuild test -scheme MapFeatureTests -destination 'platform=iOS Simulator,name=iPhone 17 Pro'\n```\n\n#### Test Architecture\n- **Unit Tests**: Logic and reducer testing with TCA's testing utilities\n- **Snapshot Tests**: UI component visual regression testing\n- **Integration Tests**: Feature-level behavior testing\n\n**Test Coverage by Module:**\n- `AppFeatureTests` - Main app flow and navigation\n- `ChatFeatureTests` - Messaging functionality\n- `MapFeatureTests` - Location and map interactions\n- `SettingsFeatureTests` - User preference management\n- And more...\n\n## Contribute\n\n- Please report bugs or feature requests with GitHub [issues](https://github.com/CriticalMaps/criticalmaps-ios/issues).\n- If you can code please check the build \u0026 contribute guide below.\n- If you have some coins left you can help to finance the project on [Open Collective](https://opencollective.com/criticalmaps).\n\n### How to contribute\n\nIn general, we follow the \"fork-and-pull\" Git workflow.\n\n1.  **Fork** the repo on GitHub\n2.  **Clone** the project to your own machine\n3.  **Commit** changes to your own branch. Please add your changes also to the [`CHANGELOG`](CHANGELOG.md). We're following the standard of [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)\n4.  **Push** your work back up to your fork\n5.  Submit a **Pull request** so that we can review your changes\n\n#### Development Guidelines\n- Follow the existing code style and SwiftLint rules but not a hard rule\n- Write tests for new features and bug fixes\n- Run `make assets` after adding new images or localization strings\n- Ensure all CI checks pass before requesting review\n\n#### Pre-commit Hooks\nThe project uses pre-commit hooks to automatically format and lint code before commits:\n\n**Manual Setup:**\n```sh\nmake install-pre-commit  # Install hooks only\n```\n\n**What runs on each commit:**\n- **SwiftFormat** - Automatically formats Swift code\n- **SwiftLint Auto-fix** - Fixes violations that can be corrected automatically\n- **SwiftLint** - Reports remaining violations (warnings only, won't block commits)\n\n\u003e [!NOTE]\n\u003e Pre-commit hooks currently only process Swift files. Other file types (YAML, JSON, etc.) are not automatically processed.\n\n**Manual pre-commit run:**\n```sh\nmake pre-commit-run      # Run on all files\npre-commit run --files changed_file.swift  # Run on specific files\n```\n\n**Notes**:\n- Be sure to merge the latest from \"upstream\" before making a pull request!\n- For significant changes, consider opening an issue first to discuss the approach\n- Pre-commit hooks ensure code consistency across all contributors\n\n## Open Source \u0026 Copying\n\nCriticalMaps is licensed under the MIT License, which means you can freely use, modify, and distribute the code.\n\n**However, we kindly request** that you don't republish this exact app on the App Store under your own account. Instead, we encourage you to:\n- Contribute improvements back to this project\n- Fork it to create something new and different\n- Support the project on [Open Collective](https://opencollective.com/criticalmaps)\n\nThis is a community request, not a legal restriction.\n\n## Credits\n\n\u003c!-- readme: contributors -start --\u003e\n\u003ctable\u003e\n\t\u003ctbody\u003e\n\t\t\u003ctr\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/lennet\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/7677738?v=4\" width=\"100;\" alt=\"lennet\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eLeo Thomas\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/mltbnz\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/14075359?v=4\" width=\"100;\" alt=\"mltbnz\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eMalte Bünz\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/normansander\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/1220469?v=4\" width=\"100;\" alt=\"normansander\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eNorman Sander\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/fbernutz\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/26111180?v=4\" width=\"100;\" alt=\"fbernutz\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eFelizia Bernutz\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/i5glu\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/9765299?v=4\" width=\"100;\" alt=\"i5glu\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eI5glu\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/maxxx777\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/2142832?v=4\" width=\"100;\" alt=\"maxxx777\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eMaxim Tsvetkov\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/besilva\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/20118834?v=4\" width=\"100;\" alt=\"besilva\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eBernardo Silva\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/stephanlindauer\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/1323145?v=4\" width=\"100;\" alt=\"stephanlindauer\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eStephan Lindauer\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/jacquealvesb\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/86978515?v=4\" width=\"100;\" alt=\"jacquealvesb\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eJacqueline Alves\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/huschu\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/879754?v=4\" width=\"100;\" alt=\"huschu\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eJoscha\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/woxtu\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/5673994?v=4\" width=\"100;\" alt=\"woxtu\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eNull\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/jkandzi\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/9692434?v=4\" width=\"100;\" alt=\"jkandzi\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eJustus Kandzi\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/zutrinken\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/888679?v=4\" width=\"100;\" alt=\"zutrinken\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003ePeter Amende\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/ravi-aggarwal-code\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/91732598?v=4\" width=\"100;\" alt=\"ravi-aggarwal-code\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eRavi Aggarwal\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/AlbanSagouis\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/25483578?v=4\" width=\"100;\" alt=\"AlbanSagouis\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eAlban Sagouis\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/thisIsTheFoxe\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/18512366?v=4\" width=\"100;\" alt=\"thisIsTheFoxe\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eHenry\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/k-nut\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/1096357?v=4\" width=\"100;\" alt=\"k-nut\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eKnut Hühne\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/wacumov\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/2861871?v=4\" width=\"100;\" alt=\"wacumov\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eMikhail Akopov\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n            \u003ctd align=\"center\"\u003e\n                \u003ca href=\"https://github.com/StartingCoding\"\u003e\n                    \u003cimg src=\"https://avatars.githubusercontent.com/u/43170443?v=4\" width=\"100;\" alt=\"StartingCoding\"/\u003e\n                    \u003cbr /\u003e\n                    \u003csub\u003e\u003cb\u003eLoris\u003c/b\u003e\u003c/sub\u003e\n                \u003c/a\u003e\n            \u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\u003ctbody\u003e\n\u003c/table\u003e\n\u003c!-- readme: contributors -end --\u003e\n\n## Copyright \u0026 License\n\nCopyright (c) 2025 headione - Released under the [MIT license](https://github.com/criticalmaps/criticalmaps-ios/blob/main/LICENSE).\n","funding_links":["https://opencollective.com/criticalmaps"],"categories":["Swift","Social","swiftui"],"sub_categories":["Password"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcriticalmaps%2Fcriticalmaps-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcriticalmaps%2Fcriticalmaps-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcriticalmaps%2Fcriticalmaps-ios/lists"}