Heroes App is a Swift-based iOS application built using TCA architecture. The app displays a list of Heroes with their details and allows search Heroes by their names.
- Introduction
- Features
- App Screenshots
- How It Works
- Setup & Installation
- Architecture Overview
- Git Workflow
- Modules
- Diagrams
- Key Improvements
- Dependencies
- API Integration
- Future Enhancements
- Issues
- Contributing
- License
- 📜 List of Heroes: Displays a List of all Heroes with their name, Image, and description.
- 🔍 Search: Search Heroes by their names.
- 🧍♂️ Hero Details: View detailed information about a selected hero, including:
- Name
- Image
- Full description
- Comics
- Series
- Stories
Heroes List | Heroes Details |
---|---|
![]() |
![]() |
The main screen is a table view displaying all heroes fetched from the API.
- Each row displays:
- hero's name.
- hero's image.
- hero's short description.
On selecting a hero from the list, a detail view is shown with the character's:
- Profile image.
- Name.
- Description.
- Comics.
- Series.
- Stories.
-
Clone the Repository
git clone https://github.com/knight6700/WallaMarvel
-
Install all dependancies with Gem
- Open
Terminal
cd project-path
make onBoarding
- Write private and public keys Api for
Debug
andRelease
- Open
-
Install Dependencies
- make
resolve
.
- make
-
Open in Xcode
- Open the
.xcodeproj
- Open the
-
Run the App
- Select a simulator or a connected device.
- Click the Run button or press
⌘R
.
- 🔐 Note: For production builds, you can update the API keys via GitHub Secrets or your CI/CD environment.
- To get help on available commands, run:
make help
Available targets: onBoarding - Run onboarding setup (should be run first) lint - Run SwiftLint with autofix lintReport - Generate SwiftLint HTML report build - Build the WallaMarvel app test - Run MarvelCoreTests snapshotTest - Run MarvelSnapshotTests clean - Clean build and cache artifacts resolve - Reset and resolve Swift packages in MarvelCore generatePackage - Inject Package.yaml into Package.swift help - Show this help message
This project uses a Package.yaml
file for package configuration. Do not modify Package.swift directly as it is auto-generated.
- Make changes in
Package.yaml
- Run command:
This will automatically generate the updated
make generatePackage
Package.swift
file.
- name: NewFeature
type: target
dependencies:
- ComposableArchitecture
- HorizonNetwork
resources:
- Resources/FeatureAssets.xcassets
- name: NewFeatureTests
type: testTarget
dependencies:
- NewFeature
- SnapshotTesting
dependencies:
- TCADependency # ComposableArchitecture
- snapshotTesting # SnapshotTesting
- kingFisher # Kingfisher
- horizonKeys # HorizonKeys
- horizonComponent # HorizonComponent
- NETWORK # HorizonNetwork
- Maintain Swift version compatibility (6.0+)
- Verify platform requirements (iOS 17+/macOS 12+)
- The
make generatePackage
command must be run after anyPackage.yaml
changes - Generated
Package.swift
The app adopts TCA (The Composable Architecture) to structure features in a modular, testable, and scalable way, following principles of unidirectional data flow and state management.
We use GitFlow to manage our development process. The main branches include:
main
: Production-ready code.develop
: Ongoing development and integration.feature/*
: New features are developed here and then merged intodevelop
.hotfix/*
: For urgent fixes onmain
.
main ← hotfix/*
↑
develop ← feature/*
Always create branches from
develop
for new features and frommain
for hotfixes.
The project is organized into multiple Swift modules to ensure separation of concerns and modular scalability:
-
Heroes
- Manages everything related to hero listing and details.
- Includes:
- Hero List view.
- Hero Details view.
- Coordinator logic for navigation between these screens.
-
HorizonComponents
- Contains all reusable UI components.
- Encourages UI consistency and code reusability across the app.
-
HorizonNetwork
- Handles all remote API communications.
- Includes:
- API client.
- Endpoint configuration.
- Network error handling.
graph TD
subgraph "Feature Layer"
Store[Store]
Action[Action]
State[State]
Reducer[Reducer]
Store --> Reducer
Action --> Reducer
Reducer --> State
end
subgraph "Domain Layer"
UseCase[UseCase]
Repository[Repository]
Mapper[Mapper]
UseCase --> Repository
UseCase --> Mapper
end
subgraph "Data Layer"
RemoteDataSource[Remote Data Source]
LocalCache[Local Caching]
Repository --> RemoteDataSource
Repository --> LocalCache
end
%% Layer interactions
Store -.-> UseCase
UseCase -.-> Store
%% Styling
classDef featureLayer fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef domainLayer fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef dataLayer fill:#e8f5e8,stroke:#388e3c,stroke-width:2px
class Store,Action,State,Reducer featureLayer
class UseCase,Repository,Mapper domainLayer
class RemoteDataSource,LocalCache dataLayer
- Grid View or CollectionVieew: Create a
Grid View
orCollectionView
to efficiently display resources (e.g., comics). - Encryption Module: Implement an encryption module using the Arkana library and MD5, or any other suitable encryption technique.
- Network: remore static func for remote.
SwiftUI
: For building the UI.URLSession
: For API communication.Kingfisher
: For image downloading and caching.Snapshot-Testing
: For snapshot views.Netfox
: For debugging network requests.TCA Framework
: A Swift framework for managing state, side effects, and app architecture in a modular and testable wayIdentifiedCollections
: When modeling a collection of elements in your application's state, it is easy to reach for a standard Array.swift-dependencies
: A dependency management library inspired by SwiftUI's "environment.".
The app fetches data from the Marvel API.
To fetch characters from the Marvel API, use the following URL format:
https://gateway.marvel.com/v1/public/characters?limit={Int}&offset={Int}&ts={Int}&apikey={String}&hash={String}
Parameter | Description |
---|---|
limit |
Number of results to return. |
offset |
Pagination offset. |
ts (timestamp) |
A unique timestamp. Usually the current time in seconds:String(Int(Date().timeIntervalSince1970)) |
apikey |
Your public API key from Marvel Developer Portal. |
hash |
An MD5 hash of: timestamp + privateKey + publicKey For example: "\(timestamp)\(privateKey)\(publicKey)" hashed using MD5. |
privateKey |
Your private API key (keep this secret). Available after registering on Marvel Developer Portal. |
name (Optional) |
Name of the hero you want to search for. |
let timestamp = String(Int(Date().timeIntervalSince1970))
let input = "\(timestamp)\(privateKey)\(publicKey)"
let hash = input.md5() // Use an MD5 hashing function
✅ The
hash
is required for authenticating API requests and must be computed for every call.
- Fetch All Characters
- URL:
https://gateway.marvel.com/v1/public/characters?limit={Int}&offset={Int}&ts={Int}&apikey={String}&hash={String}
- URL:
- Character Search
- URL:
https://gateway.marvel.com/v1/public/characters?limit={Int}&offset={Int}&ts={Int}&apikey={String}&hash={String}&name={String}
- URL:
- Fetch Resources
- URL:
https://gateway.marvel.com/v1/public/characters/{character_Id}/{resourceType}?limit={Int}&offset={Int}&ts={Int}&apikey={String}&hash={String}
- Resource Type:
comics
,series
, orstories
.
- URL:
- 🌎 Connectivity Check: Show a "No Internet" alert automatically when the device is offline.
- 🔥 Caching: Implement API caching using an appropriate technique to improve performance and reduce network usage.
- 📊 Statistics: Displays statistics for the heroes fetched through the details and search features.
- Duplicated Id: The API was returning duplicate heroes with the same ID, so we resolved it by using a unique id (generated with UUID().uuidString) alongside the original heroId (Int) from the API.
Contributions are welcome! Follow these steps to contribute:
- Fork the repository.
- Create a feature branch:
git checkout -b feature-branch
- Commit your changes:
git commit -m "Add new feature"
- Push to the branch:
git push origin feature-branch
- Create a pull request.
This project is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License.
You may not use the material for commercial purposes.