- Instead of
UserDefaultsfor caching, any other framework could be used thanks to the protocol-oriented approach.UserDefaultswas chosen only for its simplicity. - It would be beneficial to introduce Dependency Injection (e.g., Swiftject or Needle) for more flexible dependency management.
- SwiftUI was selected because it was explicitly mentioned in the job requirements, however, I also have experience working with UIKit.
- The
Configfolder should contain theSecrets.swiftfile, which holds the API keys and tokens (ignored in git, with aSecrets.sample.swifttemplate included).
Demo videos: tvOS demo is available here, and the iOS demo is available here.
- Genres screen: cached for 1 hour locally; tap a genre to open the movie grid
- Movies screen: grid with posters + titles, infinite scrolling with prefetching
- Image loading via AsyncImage (TMDB w200 image base)
- Clean MVVM structure with protocol-based services
- tvOS target that reuses the same SwiftUI code
- Genres screen: Done
- Navigate to movies on genre tap: Done (NavigationStack + Router)
- Data from TMDB: Done (themoviedb.org v3 API)
- Genres cache: Done (UserDefaults TTL = 1 hour)
- Movies pagination: Done (load-more prefetch; threshold-based)
- Bonus – Grid: Done (2 columns on iPhone, 4 on tablet)
- Bonus – tvOS support: Done (separate tvOS target, shared sources)
- Swift 6, SwiftUI, Concurrency (async/await)
- Xcode 16.3+
- iOS 18.0+, tvOS 18.0+
- Alamofire (SPM)
- Pattern: MVVM with a state
- Networking: Request protocol + NetworkService.execute<R: Request>(R) async
- Services:
- TMDBService: domain-specific client built on NetworkService
- CacheService: UserDefaults-based genre cache with 1-hour TTL
- Views: SwiftUI screens bound to view models with @StateObject
- Router: simple enum-based router injected via environment
- GenresViewModel loads genres
- Checks CacheService.isGenresCacheValid() and loadGenres() first
- If invalid/missing, requests TMDBService.fetchGenres() then saveGenres()
- Tap a genre -> Router pushes MoviesView
- MoviesViewModel loads first page via TMDBService.discoverMovies(genreId:page:)
- Keeps
page/totalPagesand appends unique movies while paginating - Triggers
loadMoreIfNeedednear the end via onAppear threshold
- Keeps
- Unit tests for GenresViewModel and MoviesViewModel (loading, cache hit/miss, pagination, error handling)
- tvOS improvments
- Pull-to-refresh and manual retry on movies list
- Image caching (e.g., via Nuke/Kingfisher or URLCache tuning)
- Error handling improvements