Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions Projects/App/Resources/Base.lproj/LaunchScreen.storyboard
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
Expand All @@ -11,15 +14,32 @@
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Exchanges APP" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uAe-QE-b85">
<rect key="frame" x="101.66666666666669" y="409.33333333333331" width="190" height="33.666666666666686"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="uAe-QE-b85" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="Ndy-wH-f13"/>
<constraint firstItem="uAe-QE-b85" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="YQJ-Tc-zhK"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
14 changes: 13 additions & 1 deletion Projects/Home/Sources/Coordinator/HomeCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
//
//

import DependencyInjectionInterfaces
import HomeInterfaces
import NavigationInterfaces
import NetworkingInterfaces
import UIKit

public final class HomeCoordinator: HomeCoordinating {
Expand All @@ -22,6 +24,16 @@ public final class HomeCoordinator: HomeCoordinating {
}

public func start() {

let resolver = SharedContainer.shared.resolver()
let networkService: NetworkServiceProtocol = resolver.resolve()
let service = HomeService(networkService: networkService)
let viewModel = HomeViewModel(service: service)
viewModel.coordinatorDelegate = self
let homeVC = HomeViewController(viewModel: viewModel)
navigationController.pushViewController(homeVC, animated: false)
}
}

extension HomeCoordinator: HomeViewModelCoordinatorDelegate {
func navigateToDetails(of exchange: Exchange) {}
}
61 changes: 61 additions & 0 deletions Projects/Home/Sources/Model/Exchange.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

struct Exchange: Equatable, Identifiable {
let id: Int
let name: String
let description: String?
let logo: String
let spotVolumeUsd: Double?
let makerFee: Double
let takerFee: Double
let dateLaunched: String
let websiteUrl: String?
let twitterUrl: String?

let isLoadingDetails: Bool

init(id: Int,
name: String,
description: String? = nil,
logo: String,
spotVolumeUsd: Double? = nil,
makerFee: Double,
takerFee: Double,
dateLaunched: String,
websiteUrl: String? = nil,
twitterUrl: String? = nil) {
self.id = id
self.name = name
self.description = description
self.logo = logo
self.spotVolumeUsd = spotVolumeUsd
self.makerFee = makerFee
self.takerFee = takerFee
self.dateLaunched = dateLaunched
self.websiteUrl = websiteUrl
self.twitterUrl = twitterUrl
self.isLoadingDetails = false
}

init(summary: ExchangeSummary) {
self.id = summary.id
self.name = summary.name
self.description = nil
self.logo = ""
self.spotVolumeUsd = nil
self.makerFee = 0.0
self.takerFee = 0.0
self.dateLaunched = ""
self.websiteUrl = nil
self.twitterUrl = nil
self.isLoadingDetails = true
}
}
35 changes: 35 additions & 0 deletions Projects/Home/Sources/Model/ExchangeDetail.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

struct ExchangeDetail: Equatable {
let id: Int
let name: String
let description: String?
let logo: String
let spotVolumeUsd: Double?
let makerFee: Double
let takerFee: Double
let dateLaunched: String
let urls: ExchangeURLs
}

extension ExchangeDetail: Codable {
enum CodingKeys: String, CodingKey {
case id
case name
case description
case logo
case spotVolumeUsd = "spot_volume_usd"
case makerFee = "maker_fee"
case takerFee = "taker_fee"
case dateLaunched = "date_launched"
case urls
}
}
14 changes: 14 additions & 0 deletions Projects/Home/Sources/Model/ExchangeDetailResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

struct ExchangeDetailResponse<T: Codable>: Codable {
let status: Status
let data: [String: T]
}
14 changes: 14 additions & 0 deletions Projects/Home/Sources/Model/ExchangeResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

struct ExchangeResponse<T: Codable>: Codable {
let status: Status
let data: [T]
}
14 changes: 14 additions & 0 deletions Projects/Home/Sources/Model/ExchangeSummary.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

struct ExchangeSummary: Equatable, Codable {
let id: Int
let name: String
}
14 changes: 14 additions & 0 deletions Projects/Home/Sources/Model/ExchangeUrls.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

struct ExchangeURLs: Equatable, Codable {
let website: [String]
let twitter: [String]
}
17 changes: 17 additions & 0 deletions Projects/Home/Sources/Model/HomeViewState.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

enum HomeViewState: Equatable {
case loading
case loadingMore
case empty
case loaded
case error(String)
}
27 changes: 27 additions & 0 deletions Projects/Home/Sources/Model/Status.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

struct Status: Equatable {
let timestamp: String
let errorCode: Int?
let errorMessage: String?
let elapsed: Int
let creditCount: Int
}

extension Status: Codable {
enum CodingKeys: String, CodingKey {
case timestamp
case errorCode = "error_code"
case errorMessage = "error_message"
case elapsed
case creditCount = "credit_count"
}
}
14 changes: 14 additions & 0 deletions Projects/Home/Sources/Protocols/HomeServiceProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

protocol HomeServiceProtocol: AnyObject, Sendable {
func fetchExchangesList(page: Int, limit: Int) async throws -> [ExchangeSummary]
func fetchDetailsFor(ids: [String]) async throws -> [ExchangeDetail]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

@MainActor
protocol HomeViewModelCoordinatorDelegate: AnyObject {
func navigateToDetails(of exchange: Exchange)
}
14 changes: 14 additions & 0 deletions Projects/Home/Sources/Protocols/HomeViewModelDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

@MainActor
protocol HomeViewModelDelegate: AnyObject {
func didUpdateState(_ state: HomeViewState)
}
19 changes: 19 additions & 0 deletions Projects/Home/Sources/Protocols/HomeViewModelProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import Foundation

@MainActor
protocol HomeViewModelProtocol: AnyObject {
var delegate: HomeViewModelDelegate? { get set }
var numberOfItems: Int { get }

func loadData()
func item(at index: Int) -> Exchange
func didSelectRow(at index: Int)
}
40 changes: 40 additions & 0 deletions Projects/Home/Sources/Service/HomeEndpoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
//
// Created by Vitor Conceicao.
//
// github.com/vitor-rc1
//
//

import NetworkingInterfaces
import Foundation

enum HomeEndpoint: APIEndpointProtocol {
case fetchItems(page: Int, limit: Int)
case fetchDetail(ids: [String])

var baseURL: String {
ProcessInfo.processInfo.environment["CM_API_BASE_URL"] ?? ""
}

var path: String {
let basePath: String = "/v1/exchange/"
switch self {
case let .fetchItems(page, limit):
return "\(basePath)map?start=\(page)&limit=\(limit)"
case let .fetchDetail(ids):
let idsQuery = ids.joined(separator: ",")
return "\(basePath)info?id=\(idsQuery)"
}
}

var method: NetworkingInterfaces.HTTPMethod {
.get
}

var headers: [String: String]? {
[
"X-CMC_PRO_API_KEY": ProcessInfo.processInfo.environment["CM_API_KEY"] ?? ""
]
}
}
Loading
Loading