Skip to content

[iOS] Akash — PokéDex Assignment#6

Open
akashkahalkar wants to merge 31 commits into
Robustrade:mainfrom
akashkahalkar:ios/feature/akash
Open

[iOS] Akash — PokéDex Assignment#6
akashkahalkar wants to merge 31 commits into
Robustrade:mainfrom
akashkahalkar:ios/feature/akash

Conversation

@akashkahalkar

Copy link
Copy Markdown

[iOS] Akash — PokéDex Assignment

Overview

A Pokédex app UI using Swift-UI integrating the provided KMP shared module. Covers Pokémon listing with infinite scroll and search, a detail screen, and a persistent favourites tab — all wired to the KMP layer through a clean, lifecycle-safe Store architecture.


Screenshots / Screen Recording

list detail favorite
list_dark detail_dark favorite_dark

light.mov
dark.mov

Implementation Decisions

ViewModelFactory + SwiftUI Environment DI

A ViewModelFactory protocol drives all ViewModel creation and is injected into the view tree via a custom SwiftUI EnvironmentKey:

.environment(\.viewModelFactory, AppDependencies())

Views request ViewModels through @Environment(\.viewModelFactory) rather than constructing them directly. This keeps the dependency flow strictly top-down, avoids singletons in views, and makes every screen independently testable by swapping in a mock factory. AppDependencies implements the factory and holds the single PokemonRepository resolved from Koin at startup.

StateFlow → Swift Concurrency Bridge

A StateFlowCollector wraps Kotlinx_coroutines_coreFlowCollector and exposes a typed AsyncStream extension, letting every Store write clean structured-concurrency loops:

stateTask = Task { [weak self] in
    for await state in stateFlow.stream(of: PokemonListState.self) {
        guard let self else { return }
        await MainActor.run { self.listState = state }
    }
}

Each Store syncs the current StateFlow value synchronously on init calls viewModel.onCleared() in deinit to release Kotlin coroutine scopes.

Koin DI from Swift

Because Koin's reified get<T>() doesn't cross the Kotlin/Native boundary, KoinDependencyManager resolves PokemonRepository by iterating koin.instanceRegistry.instances and matching on qualifiedName, then calling koin.get(clazz:qualifier:parameters:) with the resolved KotlinKClass.

Haptic Feedback

A protocol-based HapticsManager (HapticsProviding) fires a UINotificationFeedbackGenerator(.success) pulse when a Pokémon is added to favourites. The protocol is injected into DetailsView as a default parameter, making the haptics trivially mockable in tests without any extra setup.

init(name: String, viewModel: ..., hapticsManager: HapticsProviding = HapticsManager.shared)

Deleting Favourites

The Favourites screen is reactively driven by FavouritesViewModel, which observes the KMP FavoritesViewModel.favorites StateFlow (backed by SQLDelight). Removing a Pokémon is exposed as a trailing swipe action with full-swipe support:

.swipeActions(edge: .trailing, allowsFullSwipe: true) {
    Button(role: .destructive) { viewModel.unfavourite(pokemon) } label: {
        Label("Unfavourite", systemImage: "heart.slash")
    }
}

unfavourite calls repository.setFavorite(isFavorite: false) on the KMP layer — the SQLDelight-backed flow emits the updated list automatically, so no manual state refresh is needed.


Screens

Screen Key features
Pokémon List LazyVGrid, infinite scroll, .searchable, pull-to-refresh, empty/error states
Pokémon Detail Type-coloured glow animation, Swift Charts stat bars, abilities grid, ♥ toolbar toggle with haptics
Favourites Reactive SQLDelight list, swipe-to-delete, empty state

Navigation uses a TabView (Pokédex / Favourites) with each tab owning its own NavigationStack. Detail is pushed via NavigationLink using the factory closure injected at the call site.


Known Limitations & Future Improvements

  • Koin DI brittleness — the qualifiedName string match breaks if the shared module package is renamed. A KoinHelper.kt shim in the shared module would be the clean fix.
  • ** KMP-NativeCoroutines** — with more time, adopting KMP-NativeCoroutines would eliminate the manual StateFlowCollector bridge and provide exhaustive switch on sealed classes automatically.

added xcode project.
created koinRepository manager
handles image fetch
handles caching
handles retry mechanism
completed integration with view model
fixed Ui glitches.
added compatibility with dark mode
added view for empty treatment
used environment to inject view model factory
used viewModel factory to create the viewmodels
added type chip view
added stats view
added type color helper
changed color pallete for stats view
updated padding for stats title
broken into smaller components
updated factory to create favorite view model
on favorite click user gets a feedback
cosmetics changes to the details view
added test cases for view model, formatter and flow collector
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant