{"id":34767919,"url":"https://github.com/ronstorm/country-facts","last_synced_at":"2026-05-26T09:31:49.779Z","repository":{"id":322152734,"uuid":"1088362829","full_name":"ronstorm/country-facts","owner":"ronstorm","description":"A SwiftUI app using GraphQL and LLMs to explore and summarize country data.","archived":false,"fork":false,"pushed_at":"2025-11-02T21:51:48.000Z","size":69,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-02T22:18:19.843Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ronstorm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2025-11-02T20:26:36.000Z","updated_at":"2025-11-02T21:51:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ronstorm/country-facts","commit_stats":null,"previous_names":["ronstorm/country-facts"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ronstorm/country-facts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronstorm%2Fcountry-facts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronstorm%2Fcountry-facts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronstorm%2Fcountry-facts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronstorm%2Fcountry-facts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ronstorm","download_url":"https://codeload.github.com/ronstorm/country-facts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronstorm%2Fcountry-facts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33513905,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T03:12:49.672Z","status":"ssl_error","status_checked_at":"2026-05-26T03:12:47.976Z","response_time":63,"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":[],"created_at":"2025-12-25T07:46:55.279Z","updated_at":"2026-05-26T09:31:49.770Z","avatar_url":"https://github.com/ronstorm.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CountryFacts\n\nCountryFacts is an iOS application that combines data from a public GraphQL API with AI-powered insights to help users explore and learn about countries around the world.\n\n## API Selection\n\nThis project uses the following APIs:\n\n### GraphQL API\n- **Countries API** - https://countries.trevorblades.com/\n  - Chosen from available options: Rick \u0026 Morty API, Countries API, SpaceX API\n  - Provides country information including names, codes, capitals, currencies, languages, and continents\n  - No authentication required\n  - Free public API\n\n### LLM API\n- **Gemini API (Google AI Studio)** - https://ai.google.dev/\n  - Chosen from available options: OpenAI API (gpt-3.5-turbo), Cohere API, Gemini API\n  - Uses Gemini 2.5 Flash model\n  - Free-tier available with usage quotas\n  - Used for generating fun facts and answering questions about countries\n\n## Features\n\n### Core Features\n- **Country List Display** - Browse all countries with flag emojis and continent information\n  - Data fetched from Countries GraphQL API (https://countries.trevorblades.com/)\n- **Country Detail View** - Comprehensive information about each country including capital, currency, languages, and continent\n  - Detailed data fetched from Countries GraphQL API\n- **AI-Powered Fun Facts** - Engaging fun facts generated using Gemini 2.5 Flash LLM\n  - Powered by Gemini API (Google AI Studio) - Free tier\n- **Search \u0026 Filter** - Real-time search by country name, continent, or country code\n- **Favorites System** - Save and filter favorite countries (stored locally)\n- **Ask a Question** - Interactive Q\u0026A feature powered by Gemini AI\n  - Uses Gemini 2.5 Flash model from Google AI Studio\n\n### User Experience\n- **Pull-to-Refresh** - Refresh country data with a simple swipe gesture\n- **Dark Mode Support** - Beautiful UI that adapts to light and dark modes\n- **Loading States** - Skeleton screens and progress indicators\n- **Error Handling** - User-friendly error messages with retry functionality\n\n## Architecture\n\nThis project follows **MVVM (Model-View-ViewModel)** architecture with clear separation of concerns:\n\n### Architecture Overview\n\n- **SwiftUI Views** - `LaunchScreen`, `CountryListView` \u0026 `CountryDetailView`\n- **ViewModels** - `CountryListViewModel` \u0026 `CountryDetailViewModel`\n- **Services** - `CountryService`, `GeminiService` \u0026 `FavoritesManager`\n- **External APIs** - `GraphQL API` \u0026 `Gemini API`\n\n### Layer Responsibilities\n\n- **Models** - Pure data structures (`Country`, `CountryDetail`) that conform to `Codable`, `Sendable`, and `Identifiable`\n- **Views** - SwiftUI views for presentation only, no business logic\n- **ViewModels** - Business logic, state management, and coordination between views and services\n  - Marked with `@Observable` (iOS 17+) for reactive updates\n  - Use `@MainActor` for UI thread safety\n- **Services** - Protocol-based API clients for easy testing and swapping implementations\n  - `CountryServiceProtocol` - GraphQL data fetching\n  - `LLMServiceProtocol` - AI-powered content generation\n  - `FavoritesManagerProtocol` - Local storage management\n\n### Key Design Patterns\n\n- **Dependency Injection** - All services injected via initializers\n- **Protocol-Oriented Programming** - Services defined as protocols for testability\n- **Async/Await** - Modern Swift concurrency throughout\n- **MVVM** - Clear separation between UI and business logic\n\n## 📋 Requirements\n\n- iOS 16.0+\n- Xcode 16.0+\n- Swift 6.2+\n- Apollo iOS SDK 1.25.2+\n- Gemini API key (free tier available)\n\n## Setup Instructions\n\n### 1. Clone the Repository\n\n```bash\ngit clone https://github.com/ronstorm/country-facts.git\ncd country-facts\n```\n\n### 2. Install Apollo iOS SDK\n\nAdd Apollo iOS SDK using Swift Package Manager:\n\n1. Open `CountryFacts.xcodeproj` in Xcode\n2. Select the project (blue icon) → Select **CountryFacts** target\n3. Go to **Package Dependencies** tab\n4. Click **+** button\n5. Enter this URL: `https://github.com/apollographql/apollo-ios.git`\n6. Select version **1.25.2**\n7. Click **Add Package**\n8. Ensure **Apollo** product is added to your target\n9. Go to **General** tab → **Frameworks, Libraries, and Embedded Content**\n10. Verify **Apollo** is listed (if not, click **+** and add it)\n\n### 3. Install Apollo iOS CLI\n\nDownload the Apollo iOS CLI binary directly from GitHub:\n\n```bash\n# Create bin directory if it doesn't exist\nmkdir -p bin\n\n# Download Apollo iOS CLI\ncurl -L https://github.com/apollographql/apollo-ios/releases/download/1.25.2/apollo-ios-cli.tar.gz -o bin/apollo-ios-cli.tar.gz\n\n# Extract the archive\ntar -xzf bin/apollo-ios-cli.tar.gz -C bin/\n\n# Make it executable\nchmod +x bin/apollo-ios-cli\n\n# Verify installation\n./bin/apollo-ios-cli --version\n```\n\n**Note:** Use `./bin/apollo-ios-cli` to run commands from the project root.\n\n### 4. Download GraphQL Schema\n\nDownload the schema from the Countries API:\n\n```bash\n# From project root\ncurl -X POST https://countries.trevorblades.com/graphql \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"query\":\"query IntrospectionQuery { __schema { queryType { name } mutationType { name } subscriptionType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } } fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef } } fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue } fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } } } } }\"}' \\\n  -o CountryFacts/Models/GraphQL/schema.json\n```\n\n### 5. Generate GraphQL Code\n\nRun code generation to create Swift files from your GraphQL queries:\n\n```bash\n# From project root\n./bin/apollo-ios-cli generate\n```\n\n**Note:** The generated files (`CountryFacts/Models/GraphQL/API.swift` and `*.graphql.swift`) are already committed to the repository, so the project builds immediately after clone. You only need to run this if you modify GraphQL queries.\n\n### 6. Configure Gemini API Key\n\nYou need a Gemini API key from [Google AI Studio](https://ai.google.dev/).\n\n**Important:** Never commit your API key to git. The `Config.xcconfig` file is already in `.gitignore`.\n\n#### Get Your API Key:\n\n1. Visit [Google AI Studio](https://ai.google.dev/)\n2. Sign in with your Google account\n3. Click \"Get API Key\" or navigate to API Keys section\n4. Create a new API key (free tier available)\n5. Copy your API key\n\n#### Setup Options:\n\n**Using Config.xcconfig (Recommended)**\n\n1. Copy `Config.xcconfig.example` to `Config.xcconfig`:\n   ```bash\n   cp Config.xcconfig.example Config.xcconfig\n   ```\n\n2. Open `Config.xcconfig` and add your API key:\n   ```\n   GEMINI_API_KEY = YOUR_API_KEY_HERE\n   ```\n\n3. The project is already configured to read from `Config.xcconfig` via `Info.plist`\n\n### 7. Build and Run\n\n1. Open `CountryFacts.xcodeproj` in Xcode\n2. Select your target device or simulator\n3. Build and run (⌘ + R)\n\n## Testing\n\n### Running Tests\n\n**In Xcode:**\n- Press `⌘ + U` to run all tests\n- Or use Test Navigator (⌘ + 6) to run specific test suites\n\n**Command Line:**\n```bash\n# Run all tests\nxcodebuild test -scheme CountryFacts -destination 'platform=iOS Simulator,name=iPhone 17'\n\n# Run specific test suite\nxcodebuild test -scheme CountryFacts -destination 'platform=iOS Simulator,name=iPhone 17' -only-testing:CountryFactsTests/CountryListViewModelTests\n```\n\n### Test Coverage\n\nThe project includes comprehensive unit tests for:\n\n- **ViewModels** - `CountryListViewModelTests`, `CountryDetailViewModelTests`\n- **Services** - `GeminiServiceTests`, `MockCountryServiceTests`\n- **Error Handling** - `CountryServiceErrorTests`, `LLMServiceErrorTests`\n- **Mock Services** - `MockCountryService`, `MockLLMService`, `MockFavoritesManager`\n\n### Test Architecture\n\n- **Mock Services** - Protocol-based mocks for isolated testing\n- **URLProtocol Mocking** - Custom `MockURLSession` for network testing\n- **Test Helpers** - Reusable utilities in `TestHelpers.swift`\n\n## CI/CD\n\nThis project includes a GitHub Actions workflow for automated build and testing:\n\n- **Workflow file:** `.github/workflows/ci.yml`\n- **Triggers:** Runs on push and pull requests to `main` and `develop` branches\n- **Environment:** macOS 15 with Xcode 16.0\n- **Actions:**\n  - Builds the iOS project\n  - Runs all unit tests\n  - Generates code coverage reports\n\n## Known Limitations \u0026 Tradeoffs\n\n### API Limitations\n\n- **Countries GraphQL API** (https://countries.trevorblades.com/) - Free public API with limited data depth\n  - No population, GDP, or historical data\n  - Basic country information only\n  - No authentication required (rate limits may apply)\n  - Selected from options: Rick \u0026 Morty API, Countries API, SpaceX API\n\n- **Gemini API Free Tier** (Google AI Studio) - Rate limits apply\n  - Free tier has usage quotas\n  - Requests may be throttled during high usage\n  - Error handling includes retry logic for transient failures\n  - Selected from options: OpenAI API (gpt-3.5-turbo), Cohere API, Gemini API\n  - Uses Gemini 2.5 Flash model for fun fact generation and Q\u0026A\n\n### Technical Tradeoffs\n\n- **MVVM over TCA** - Chosen for simplicity\n  - Less boilerplate than The Composable Architecture\n  - Easier to understand for developers new to the codebase\n  - Sufficient for app size and complexity\n\n- **No Offline Caching** - Always requires network\n  - Countries data fetched fresh on each app launch\n  - Fun facts generated on-demand (not cached)\n  - Favorites stored locally but country data requires network\n\n- **UserDefaults for Favorites** - Simple storage solution\n  - Suitable for small data sets (country codes)\n  - Not suitable for large amounts of data\n  - Could be upgraded to Core Data if needed\n\n### Performance Considerations\n\n- **LLM Response Time** - Fun facts take 2-5 seconds to generate\n  - Loading states clearly indicated to users\n  - Generation happens on-demand (not auto-load)\n  - Retry logic included for failures\n\n- **List Rendering** - Optimized with `ForEach` within `List`\n  - Handles ~250 countries efficiently\n  - Search filtering happens in-memory\n  - No pagination needed for current data size\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fronstorm%2Fcountry-facts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fronstorm%2Fcountry-facts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fronstorm%2Fcountry-facts/lists"}