[Feature Request]: Implement offline-first architecture with SwiftData/CoreData persistence
- Dominant language
- Swift
- Stars
- 0
- Forks
- 4
- PR merge metrics
- No merged PRs in 30d
Description
### Description:
Currently, the app has **no persistent storage**. All data (problems, contests, profile data, submissions) exists only in memory and is lost when the app is closed. The only persistence is `@AppStorage("userHandle")` for login state. This means:
- Opening the app with no internet shows an error screen -- no cached data to display.
- Switching tabs triggers API calls every time (the in-memory cache only lasts within a single `ProblemRepository` instance).
- Problem history and favorite problems cannot be tracked across app launches.
- Rating chart data is re-fetched every time the profile tab is viewed.
### Feature Requirements:
**Core Persistence Layer:**
1. **Choose and implement a persistence framework** -- either SwiftData (iOS 17+) or CoreData (broader compatibility). SwiftData is recommended for its modern Swift-first API.
2. **Design the data models:**
```swift
@Model class PersistedProblem {
@Attribute(.unique) var id: String // contestId + index
var contestId: Int
var index: String
var title: String
var rating: Int?
var tags: [String]
var isFavorite: Bool = false
var lastUpdated: Date
}
@Model class PersistedContest {
@Attribute(.unique) var id: Int
var name: String
var type: String
var phase: String
var durationSeconds: Int
var startTimeSeconds: Int?
var lastUpdated: Date
}
@Model class PersistedProfile {
@Attribute(.unique) var handle: String
var rank: String?
var rating: Int?
var maxRating: Int?
var contribution: Int?
var avatarURL: String?
var lastUpdated: Date
}
```
3. **Update the Repository layer** to be the bridge between network and persistence:
- On app launch with no internet → serve persisted data immediately
- On app launch with internet → fetch fresh data, update persistence, serve to UI
- When cache TTL expires → attempt network fetch; if it fails, serve stale persisted data with a "Last updated X ago" indicator
- All reads should be from persistence; all writes should come from network → persistence
4. **Add a "Last Updated" indicator**: Show a subtle timestamp on each screen (e.g., "Updated 2 hours ago") so users know when data was last refreshed.
5. **Add a "Favorites" feature for Problems**: Users can star/favorite problems. These persist across launches and can be viewed in a dedicated "Favorites" tab or filter.
### Architecture:
```
Network (ProblemService)
↓
Repository (ProblemRepository) ←→ SwiftData/CoreData (PersistedProblem)
↓
ViewModel (ProblemListViewModel)
↓
View (ProblemListView)
```
The Repository becomes the single source of truth:
- It reads from persistence first (instant UI).
- It fetches from network in the background.
- It writes network results to persistence.
- It notifies the ViewModel of updates.
### Key Files:
| Action | File |
|--------|------|
| **CREATE** | `CForge/Persistence/PersistedModels.swift` |
| **CREATE** | `CForge/Persistence/PersistenceController.swift` |
| **MODIFY** | `CForge/CForgeApp.swift` (inject ModelContainer) |
| **MODIFY** | `CForge/Repositories/ProblemRepository.swift` |
| **MODIFY** | `CForge/Views/Problem/ProblemListView.swift` (favorites UI) |
| **MODIFY** | `CForge/Views/Problem/ProblemModels.swift` (add `isFavorite`) |
| **CREATE** (optional) | `CForge/Repositories/ContestRepository.swift` |
### Expected Outcome:
- The app works offline by displaying previously fetched data.
- Data persists across app launches.
- "Last Updated" indicators show data freshness.
- Users can favorite problems and access them across sessions.
- The Repository layer seamlessly decides between network and persistence.
- Network failures gracefully degrade to cached data instead of error screens.
Contributor guide
Research direction
Begin by reading CForge/CForgeApp.swift and CForge/Repositories/ProblemRepository.swift, then inspect the problem models and views listed in the issue. Define the persistence approach and trace how network results reach the ViewModel before changing the repository flow. Done means cached data survives launches, offline and stale-cache paths work, freshness is visible, and favorites persist and can be accessed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- database, mobile
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100