iiitl / iiitl/CForge

[Refactor]: Migrate Contest feature to MVVM architecture with Service/Repository layers

Open
#10 5 comments 0 reactions 1 assignee View on GitHub

@harshitraj1236 is already working on this.

Since Apr 12, 2026.

architecture enhancement medium refactor
Dominant language
Swift
Stars
0
Forks
4
PR merge metrics
No merged PRs in 30d

Description

### Description:

The **Problem** feature follows a clean, testable MVVM architecture:
```
ProblemService → ProblemRepository (actor, caching) → ProblemListViewModel → ProblemListView
```

However, the **Contest** feature has no such layering. All networking, state management, and UI logic are tightly coupled inside `ContestListView` and its extensions:

- `ContestAPI.swift` — Network calls defined as View extensions (`loadContests()`, `fetchContests()`)
- `ContestHelpers.swift` — Filtering logic defined as a View extension
- `ContestListView.swift` — Holds `@State` variables directly for `contests`, `searchText`, `isRefreshing`, and `errorMessage`

This makes the Contest feature untestable, inconsistent with the rest of the codebase, and harder to maintain.

### Current Behavior:

- `ContestListView` directly manages `@State` properties for contest data, loading, and errors.
- API calls are made as View extension methods, not through a service layer.
- There is no caching — every time the user switches tabs, contests are re-fetched.
- Error handling uses `UIAlertController` via a `showAlert()` function that accesses `UIApplication.shared` directly.

### Feature Requirements:

- **Create `ContestService`** (in `CForge/Services/`) — Handles raw API calls using `async/await`. Must follow `ProblemService` patterns: protocol-based, structured `AppLog` logging, proper `NetworkError` usage.
- **Create `ContestRepository`** (in `CForge/Repositories/`) — Swift `actor` with in-memory caching, cache TTL, and request deduplication. Mirror `ProblemRepository`.
- **Create `ContestListViewModel`** (in `CForge/ViewModels/`) — `@MainActor`, `ObservableObject` with `ViewState` enum (`idle`, `loading`, `loaded([CFContest])`, `error(String)`). Handles filtering internally.
- **Refactor `ContestListView`** — Remove all `@State` data properties. Use `@StateObject var viewModel`.
- **Delete `ContestAPI.swift`** — Networking moves to `ContestService`.
- **Delete `ContestHelpers.swift`** — Filtering moves to `ContestListViewModel` or a `ContestFilterEngine`.
- Remove `showAlert()` UIKit hack — use SwiftUI `.alert` pattern (already partially present).

### Key Files:

| Action | File |
|--------|------|
| **CREATE** | `CForge/Services/ContestService.swift` |
| **CREATE** | `CForge/Repositories/ContestRepository.swift` |
| **CREATE** | `CForge/ViewModels/ContestListViewModel.swift` |
| **MODIFY** | `CForge/Views/Contest/ContestListView.swift` |
| **MODIFY** | `CForge/Views/Contest/ContestModels.swift` (un-nest from `ContestListView`) |
| **DELETE** | `CForge/Views/Contest/ContestAPI.swift` |
| **DELETE** | `CForge/Views/Contest/ContestHelpers.swift` |

### Expected Outcome:

- Contest feature follows `Service → Repository → ViewModel → View`, matching the Problem feature.
- All networking uses structured `async/await` with `AppLog` and `NetworkError`.
- Contest data is cached to avoid unnecessary API calls on tab switches.
- Models are standalone structs, not nested inside View extensions.
- UIKit `showAlert()` hack is removed in favor of pure SwiftUI.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.