Skip to content

mangosteencorp/everythingclient

Repository files navigation

iOS Best Practices Demo Project

This repository serves its purpose as comprehensive iOS project demonstrating current best practices and modern iOS development approaches. It tries to replicate as many best practices as possible across mulitple iOS architectures and free public APIs. Testflight: https://testflight.apple.com/join/FaXX2mUY

How to enable Pokedex on TestFlight version

Please search for a Pokemon film and click on "Pocket Monster" keyword from detail page to enable Pokedex tab.

Getting started Create a xcconfig file with this pattern in Build.xcconfig:
TMDB_API_KEY=
PRODUCT_BUNDLE_IDENTIFIER=

Also adding a GoogleService-Info.plist file to the root of the project for Firebase Analytics. (Or setting the false flag in Rebuild/Rebuild/RebuildApp.swift to skip Firebase Analytics)

Overview

What you'll find looking at this repo:

  • feature based modularization with Swift packages
  • VIPER, Clean & MVVM, SwiftUI & UIKit
  • REST API & GraphQL networking & OAuth
  • SwiftGen, SwiftLint, SwiftFormat
  • GitHub CI build & test

Screenshots

Special Features

Design Switching Theme Switching
Design Switch Theme Switch

Screens

TMDB

TMDB Movie List TV list search & filters Movie Detail TV Detail Profile endless loading
Movie Feed TV Feed Search & Filters TV Detail TV Detail Profile Endless Loading

Pokedex

Pokedex Poke list Pokemon detail
Pokemon List Pokemon Detail

Design inspirations

Small projects

Commercial apps

Screen name Home screen
Reference My TMDB_Discover module

Large open source apps

Best Practices

Security

  • Secure Credential Management
    • βœ… API keys stored in GitHub Secrets for CI/CD and read when running workflows (see .github/workflows/ci.yml)
    • βœ… Sensitive tokens (session_id here) stored in iOS Keychain after authentication (see TMDB_Shared_Backend module)

Testing

🚧 Testing:

  • βœ… 100% code coverage for TMDB_Discover module. For testabbility purpose:
    • Data layer & domain layer should be wrapped in protocol for easy mocking. Mocking URLProtocol is for testing URLSession Task creation.
    • Using ViewInspector library for SwiftUI unit testing.

Development Tools, Build tools & Automation

  • βœ… Code Generation
    • πŸ”΄ Sourcery for mock generation
    • βœ… SwiftGen for type-safe assets and localizations (Note: SwiftGen still not supporting Xcode 15 String catalog so this project still use .strings files.)
  • βœ… Linting & Formatting: run swiftformat . or swift package plugin swiftlint at project root level
    • SwiftFormat and SwiftLint are also run as "run script phase" in Xcode build settings.
    • βœ… SwiftLint is also run as a GitHub Action workflow, Linting on every pull request. (SwiftLint doesn't work well with // swiftlint:disable comments on Ubuntu)

UI/UX

  • 🚧 UI Development
    • βœ… SwiftUI Previews for all UI components, including UIView and UIViewController,
      • 🚧 Preview should be covering all states from view models.
    • 🚧 Demo implementations for all UI modules

Multiple themes

  • βœ… Support multiple themes:
    • βœ… Light theme
    • βœ… Dark theme
    • βœ… Sepia theme
    • βœ… System theme

Other app features

Tracking:

  • βœ… Using Firebase Analytics for tracking events with abstraction to avoid direct dependency of each module.
    • GoogleService-Info.plist is ignored by Git and should be set as a secret in GitHub.

Project Structure

CI/CD

Using GitHub Actions for CI/CD.

  • βœ… ios.yml and test.sh files can generate coverage report for modules with tests. 3 actions workflow:
    • SwiftLint run
    • Build project
    • Test pre-defined schemes on available simulators
  • πŸ”΄ upload to TestFlight (or BrowserStack, Remote Testkit, etc.)

Collaboration

Setup on GitHub for team collaboration:

  • βœ… automatically running unit tests and code coverage on newly opened pull requests. (Due to SwiftPM limitation mentioned above, test.sh using a custom command of xcrun llvm-cov instead of a normal xctestplan file)
  • 🚧 Block merging pull requests unless certain conditions are met (e.g. code coverage is 100%, 2 approvals from other team members, etc.)

Progress status is classified as: βœ… Finished 🚧 In Progress πŸ”΄ Not Started πŸ”” Finished but needs updates

Architecture & Design

Navigation

Using Router Pattern, NavigationStack and Coordinator pattern

TMDB Navigation Matrix

The TMDB section uses a Coordinator pattern with NavigationStack for routing between screens. Below is a comprehensive matrix showing navigation capabilities:

Tab Routes (Root Level)

  • Movie Feed (movieFeed)
  • Marketplace/Discover (marketplace)
  • Profile (profile)

Navigation Destinations

From Screen To Screen Route Type Status Notes
Movie Feed Movie Detail TMDBRoute.movieDetail βœ… Tap on movie in feed
Movie Feed TV Show Detail TMDBRoute.tvShowDetail βœ… Tap on TV show in feed
Movie Feed Movie List (Filtered) TMDBRoute.movieList βœ… Apply filters/search
Marketplace/Discover Movie Detail TMDBRoute.movieDetail βœ… Tap on trending movie
Marketplace/Discover TV Show Detail TMDBRoute.tvShowDetail βœ… Tap on trending TV show
Marketplace/Discover TV Show List TMDBRoute.tvShowList βœ… Tap genre/cast/on-the-air
Profile Movie Detail TMDBRoute.movieDetail βœ… Tap favorite/watchlist movie
Profile TV Show Detail TMDBRoute.tvShowDetail βœ… Tap favorite/watchlist TV show
Movie Detail Movie List (by Keyword) TMDBRoute.movieList(.keyword) βœ… Tap keyword tag
Movie Detail Cast/Crew Detail πŸ”΄ Not implemented No route for person detail
TV Show Detail Season Detail πŸ”΄ Not implemented Internal to TVShowDetail
TV Show Detail Cast Detail πŸ”΄ Not implemented No route for person detail
TV Show List TV Show Detail TMDBRoute.tvShowDetail βœ… Tap TV show in filtered list
Movie List Movie Detail TMDBRoute.movieDetail βœ… Tap movie in filtered list
Any Screen Person/Cast Detail πŸ”΄ Missing Would need TMDBRoute.personDetail

Navigation Parameters

  1. TMDBRoute.movieDetail(MovieRouteModel)

    • Required: movie ID
    • Optional: title, overview, poster path, backdrop path, vote average, etc.
  2. TMDBRoute.tvShowDetail(Int)

    • Required: TV show ID
  3. TMDBRoute.movieList(AdditionalMovieListParams)

    • Supports: keyword filtering, genre filtering, cast filtering
    • Example: .movieList(.keyword(123))
  4. TMDBRoute.tvShowList(TVShowFeedType)

    • Supports: .onTheAir, .discoverWithGenre, .discoverWithTVGenre, .discoverWithCast

Missing Navigation Routes

  • πŸ”΄ Person/Cast Detail page (tap on actor/crew member)
  • πŸ”΄ Season Detail page (separate from TV show detail)
  • πŸ”΄ Episode Detail page
  • πŸ”΄ Reviews/Comments page
  • πŸ”΄ Similar Movies/TV Shows (currently might be handled via movieList/tvShowList)

Feature based Modularization:

βœ… Using Swift Package Manager to manage dependencies and only leaving a thin app shell using Xcode project. This is way more Git friendly than using Xcode project. However, it's still debatable if this is better than using new XC16 buildable folders (Package.swift at root level is also difficult to setup on latest Xcode version.). - Pros: Swift, readable & Git friendly - Cons: code suggestions, previews not as smooth as using Xcode project, coverage report also not ignoring test files.

Swinject are used for dependency injection.

Details of packages:

  • TMDB_MVVM_Detail: Showing details of a movie including overview, cast, crew, keywords, etc. Using SwiftUI and MVVM architecture.
  • TMDB_Discover: Display list of TV shows (from "up in the air" or "airing today" TMDB API) using Clean Architecture and SwiftUI.
  • TMDB_Feed: same as TMDB_Discover but using MVVM architecture. Also supports endless loading
  • TMDB_Clean_Profile: Handling authentication and displaying user profile (including avatar, favourite movies & TV shows & watchlist). Using Clean Architecture and UIKit and Combine for concurrency.
  • TMDB_TVShowDetail: Showing details of a TV show including overview, cast, crew, TV seasons. Using SwiftUI and Data Store pattern. Support multiple themes.
  • Pokedex_Pokelist: loading a Pokemon list from Pokedex GraphQL API. Using VIPER architecture and UIKit
  • Pokedex_Detail: loading a Pokemon detail from Pokedex GraphQL API. Using RxSwift & RxCocoa for reactive programming. MVVM architecture.

Module Architecture Layers

Below are the folder structures showing the architectural layers for each module:

Pokedex (Coordinator Pattern)

Pokedex/
β”œβ”€β”€ PokedexView.swift
└── Router/
    └── PokelistRouter.swift

Pokedex_Detail (MVVM)

Pokedex_Detail/
β”œβ”€β”€ View/
β”‚   β”œβ”€β”€ PokemonDetailViewController.swift
β”‚   β”œβ”€β”€ PokemonContentDetailViewController.swift
β”‚   └── LoadingViewController.swift
└── ViewModel/
    └── PokemonDetailViewModel.swift

Pokedex_Pokelist (VIPER)

Pokedex_Pokelist/
β”œβ”€β”€ Entities/
β”‚   └── PokemonEntity.swift
β”œβ”€β”€ Interactor/
β”‚   └── PokelistInteractor.swift
β”œβ”€β”€ Presenter/
β”‚   └── PokelistPresenter.swift
β”œβ”€β”€ View/
β”‚   β”œβ”€β”€ PokelistViewController.swift
β”‚   └── PokemonCell.swift
└── Protocols/
    └── PokelistProtocols.swift

TMDB_Discover (Clean Architecture)

TMDB_Discover/
β”œβ”€β”€ app/ (DI)
β”‚   └── DiscoverAssembly.swift
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ datasource/
β”‚   └── repositories/
β”œβ”€β”€ domain/
β”‚   β”œβ”€β”€ entities/
β”‚   β”œβ”€β”€ repositories/
β”‚   └── usecase/
└── presentation/
    β”œβ”€β”€ pages/
    β”œβ”€β”€ view_models/
    └── widgets/

TMDB_Feed (MVVM)

TMDB_Feed/
β”œβ”€β”€ Backend/
β”‚   └── APIServiceProtocol.swift
β”œβ”€β”€ Model/
β”‚   β”œβ”€β”€ MovieModel.swift
β”‚   └── MovieListResponse.swift
β”œβ”€β”€ ViewModels/
β”‚   β”œβ”€β”€ MovieFeedViewModel.swift
β”‚   └── TVShowFeedViewModel.swift
└── Views/
    β”œβ”€β”€ Pages/
    └── Widgets/

TMDB_MovieDetail (MVVM)

TMDB_MovieDetail/
β”œβ”€β”€ Model/
β”‚   β”œβ”€β”€ Movie.swift
β”‚   β”œβ”€β”€ People.swift
β”‚   └── Genre.swift
β”œβ”€β”€ ViewModels/
β”‚   β”œβ”€β”€ MovieDetailViewModel.swift
β”‚   β”œβ”€β”€ MovieCastingViewModel.swift
β”‚   └── MovieWatchProvidersViewModel.swift
└── Views/
    β”œβ”€β”€ Pages/
    β”œβ”€β”€ Sections/
    └── StaticViews/

TMDB_Profile (Clean Architecture)

TMDB_Profile/
β”œβ”€β”€ DI/
β”‚   └── ProfileAssembly.swift
β”œβ”€β”€ Data/
β”‚   └── Repositories/
β”œβ”€β”€ Domain/
β”‚   β”œβ”€β”€ Entities/
β”‚   β”œβ”€β”€ Repositories/
β”‚   └── UseCases/
└── Presentation/
    β”œβ”€β”€ controllers/
    β”œβ”€β”€ ViewModels/
    └── Views/

TMDB_TVShowDetail (Data Store Pattern - SwiftUI)

TMDB_TVShowDetail/
β”œβ”€β”€ Views/
β”‚   β”œβ”€β”€ TVShowDetailView.swift
β”‚   β”œβ”€β”€ TVShowDetailContentView.swift
β”‚   └── (Component views)
└── Resources/

About

Clients of common APIs (Pokedex, TMDB) for demoing best practices and iOS architectures

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages