Skip to content

minhbi245/Pok-API

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Pok-API

iOS Pokedex app demonstrating Combine framework, MVVM, and Clean Architecture patterns using UIKit.

Overview

A production-grade learning project that fetches Pokemon data from PokeAPI v2 and displays a paginated grid list, detailed stats, evolution chains, and search/filter capabilities. Zero external networking dependencies—uses native URLSession + Combine for reactive data binding.

Target Audience: iOS developers learning reactive programming and scalable architecture patterns.

Features

  • Pokemon Grid List — 2-column infinite-scroll pagination (20 per page)
  • Pokemon Detail — Stats bars, abilities, evolution chain, official artwork
  • Search — Debounced, client-side name filtering (Combine pipeline)
  • Type Filter — Chip bar to filter by type (Fire, Water, Electric, etc.)
  • Favorites — Heart toggle to save/unsave locally (UserDefaults)
  • Error Handling — Network errors with retry button, empty state fallbacks

Tech Stack

Component Tool Version Purpose
Language Swift 5.9+ Modern, type-safe
UI Framework UIKit iOS 15+ Native, no SwiftUI (for learning)
Reactive Combine Native Publishers, async data binding
Layout SnapKit latest Programmatic Auto Layout
Images Kingfisher latest Image loading + HTTP caching
Networking URLSession Native Lightweight, Combine-first
Package Manager SPM Xcode Native dependency management
Storage UserDefaults Native Favorites persistence

Architecture

┌─────────────────────────────────────┐
│  PRESENTATION (UIKit + ViewModel)  │
│  PokemonListVC, PokemonDetailVC    │
└──────────────┬──────────────────────┘
               │ calls
┌──────────────▼──────────────────────┐
│  DOMAIN (Pure Swift, Testable)     │
│  Entities, UseCases, Protocols     │
└──────────────┬──────────────────────┘
               │ uses
┌──────────────▼──────────────────────┐
│  DATA (APIClient, Repositories)    │
│  DTOs, Network, Local Storage      │
└──────────────┬──────────────────────┘
               │ requests
┌──────────────▼──────────────────────┐
│  PokeAPI v2 (Public REST)          │
└─────────────────────────────────────┘

Getting Started

Prerequisites

  • Xcode 15+
  • Swift 5.9+
  • iOS 15.0+ deployment target

Installation

  1. Clone the repository

    git clone git@github.com:minhbi245/Pok-API.git
    cd Pok-API
  2. Open Xcode project

    open PokeAPI/PokeAPI.xcodeproj
  3. Build & Run

    • Select a simulator or device
    • Press Cmd + R to build and run
    • App launches with Pokemon list screen

Project Structure

PokeAPI/
├── Application/
│   ├── AppDelegate.swift
│   ├── SceneDelegate.swift
│   └── DIContainer.swift              # Dependency injection setup
│
├── Domain/                            # Pure Swift, no external deps
│   ├── Entities/
│   │   ├── Pokemon.swift              # List item model
│   │   ├── PokemonDetail.swift        # Detail screen model
│   │   ├── PokemonType.swift          # Type enum + colors
│   │   ├── PokemonStat.swift          # Stat structure
│   │   └── EvolutionChain.swift       # Evolution data
│   ├── Repositories/
│   │   ├── PokemonRepositoryProtocol.swift
│   │   └── FavoritesRepositoryProtocol.swift
│   └── UseCases/
│       ├── FetchPokemonListUseCase.swift
│       ├── FetchPokemonDetailUseCase.swift
│       ├── FetchEvolutionChainUseCase.swift
│       └── SearchPokemonUseCase.swift
│
├── Data/
│   ├── Network/
│   │   ├── APIClient.swift            # URLSession wrapper
│   │   ├── APIEndpoint.swift          # Enum of routes
│   │   └── APIError.swift             # Error types
│   ├── DTOs/
│   │   ├── PokemonListResponseDTO.swift
│   │   ├── PokemonDetailDTO.swift
│   │   ├── PokemonSpeciesDTO.swift
│   │   └── EvolutionChainDTO.swift
│   └── Repositories/
│       ├── PokemonRepository.swift    # PokeAPI impl
│       └── FavoritesRepository.swift  # UserDefaults impl
│
└── Presentation/
    ├── PokemonList/
    │   ├── PokemonListViewController.swift
    │   ├── PokemonListViewModel.swift
    │   ├── PokemonCell.swift
    │   └── TypeFilterView.swift
    ├── PokemonDetail/
    │   ├── PokemonDetailViewController.swift
    │   ├── PokemonDetailViewModel.swift
    │   ├── DetailHeaderView.swift
    │   ├── StatBarView.swift
    │   ├── AbilitiesView.swift
    │   └── EvolutionChainView.swift
    └── Common/
        ├── BaseViewController.swift
        ├── UIColor+Hex.swift
        ├── TypeBadgeView.swift
        ├── ErrorStateView.swift
        └── EmptyStateView.swift

Key Combine Patterns Demonstrated

1. dataTaskPublisher (Networking)

URLSession.shared.dataTaskPublisher(for: url)
    .tryMap { data, _ in
        try JSONDecoder().decode(T.self, from: data)
    }

2. Debounce (Search)

searchSubject
    .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
    .sink { query in ... }

3. CombineLatest (Search + Filter)

Publishers.CombineLatest(searchPublisher, filterPublisher)
    .map { search, filter in ... }

4. @Published (ViewModel Binding)

@Published var pokemonList: [Pokemon] = []
// ViewController observes via sink
viewModel.$pokemonList
    .receive(on: DispatchQueue.main)
    .sink { [weak self] pokemon in
        self?.updateUI(pokemon)
    }

5. Map & TryMap (Transformation)

apiPublisher
    .tryMap { dto in try dto.toDomain() }
    .mapError { APIError.parse($0) }

6. Sink (Subscription)

cancellable = publisher.sink(
    receiveCompletion: { completion in ... },
    receiveValue: { value in ... }
)

Design Highlights

Clean Architecture Layers

  • Domain — Pure Swift, framework-agnostic, fully testable
  • Data — Concrete implementations, API calls, local storage
  • Presentation — UIKit + ViewModel with @Published properties
  • Application — Dependency injection container

Reactive Data Flow

Every screen observes @Published properties from its ViewModel. Changes flow automatically from network response → ViewModel → UI, eliminating manual KVO or delegation boilerplate.

Error Handling

Network errors display a user-friendly error state with a retry button. Empty states appear when no Pokemon match the search/filter.

Offline Favorites

Favorites persist locally and survive app restarts. List fetch requires network; detail requires network. Caching is intentionally minimal for learning purposes.

Color & Typography System

See docs/design-guidelines.md for:

  • Official Pokemon type colors
  • Typography scale (SF Pro system font)
  • Spacing grid (8pt baseline)
  • Component specifications (cell, badge, stat bar)

Code Standards & Conventions

See docs/code-standards.md for:

  • Naming conventions (PascalCase files, camelCase properties)
  • Combine subscription management
  • Error handling patterns
  • Comment guidelines

Documentation

Future Enhancements

  • Unit Tests — Domain layer fully testable; add XCTest suite
  • UI Tests — XCUITest for critical user flows
  • CoreData Caching — Persist list responses for offline browsing
  • Dark Mode — Extend color palette for appearance API
  • SwiftUI Migration — Rewrite screens in SwiftUI while keeping ViewModel logic

License

MIT License — See LICENSE file for details.


Project Stats:

  • 38 Swift files
  • ~2,054 lines of code
  • 7 completed development phases
  • 8+ Combine patterns demonstrated

Perfect for learning Combine reactivity, Clean Architecture, MVVM, and scalable iOS app structure.

About

Pokemon API iOS app - fetching and displaying Pokemon data

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages