Skip to content

Commit

Permalink
Merge pull request #3 from helloItsHEssam/feature/modularization-data
Browse files Browse the repository at this point in the history
Pull request from 'Feature/modularization-data' to 'Feature/modularization'
  • Loading branch information
helloItsHEssam authored Nov 5, 2023
2 parents db39acf + 7998b84 commit 41f5e00
Show file tree
Hide file tree
Showing 70 changed files with 3,196 additions and 1 deletion.
29 changes: 29 additions & 0 deletions .github/workflows/Data.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Data Layer

on:
pull_request:
branches:
- '*'
- '*/*'

jobs:
build:
runs-on: macos-13

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up SwiftLint
run: brew install swiftlint

- name: Lint code
run: |
cd Data/Sources/
swiftlint
- name: run unit test
run: |
cd Data/
swift build
swift test
29 changes: 29 additions & 0 deletions .github/workflows/Domain.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Domain Layer

on:
pull_request:
branches:
- '*'
- '*/*'

jobs:
build:
runs-on: macos-13

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up SwiftLint
run: brew install swiftlint

- name: Lint code
run: |
cd Domain/Sources/
swiftlint
- name: run unit test
run: |
cd Domain/Sources/
swift build
swift test
31 changes: 31 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.DS_Store
*.swp
*~.nib
DerivedData/
build/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
!xcshareddata
!xcschemes
*.moved-aside
/Pods
/Carthage

.swiftpm
/node_modules
/tmp

.env
.env.*

**/fastlane/report.xml
**/fastlane/Preview.html
**/fastlane/screenshots
**/fastlane/test_output
9 changes: 9 additions & 0 deletions Data/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
31 changes: 31 additions & 0 deletions Data/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "Data",
platforms: [.iOS(.v14), .macOS(.v10_15)],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "Data",
targets: ["Data"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(path: "../Domain"),
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.8.1"))
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "Data",
dependencies: ["Alamofire", "Domain"]),
.testTarget(
name: "DataTests",
dependencies: ["Data"]),
]
)
3 changes: 3 additions & 0 deletions Data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Data

A description of this package.
29 changes: 29 additions & 0 deletions Data/Sources/Data/Common/Mapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// Mapper.swift
//
//
// Created by Hessam Mahdiabadi on 11/5/23.
//

import Foundation

public protocol Mapper {

associatedtype Entity
associatedtype Dto

func mapEntityToDto(input: Entity) -> Dto
func mapDtoToEntity(input: Dto) -> Entity
}

public extension Mapper {

func mapEntitiesToDtos(input: [Entity]) -> [Dto] {
return input.map { mapEntityToDto(input: $0) }
}

func mapDtosToEntities(input: [Dto]) -> [Entity] {
return input.map { mapDtoToEntity(input: $0) }
}
}

13 changes: 13 additions & 0 deletions Data/Sources/Data/Http/Api.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Api.swift
//
//
// Created by Hessam Mahdiabadi on 11/5/23.
//

import Foundation

public protocol Api {

func callApi<T: Decodable>(route: ApiRouter, decodeType type: T.Type) async throws -> T
}
76 changes: 76 additions & 0 deletions Data/Sources/Data/Http/ApiImpl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// ApiImpl.swift
//
//
// Created by Hessam Mahdiabadi on 11/5/23.
//

import Foundation
import Alamofire

final public class ApiImpl: Api {

private var sessionManager: Session
private var decoder: JSONDecoder!

#if DEBUG
public init(configuration: URLSessionConfiguration) {
sessionManager = Session(configuration: configuration)
setupDecoder()
}
#endif

public init() {
sessionManager = Session()
setupDecoder()
}

private func setupDecoder() {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"

decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
}

public func callApi<T: Decodable>(route: ApiRouter, decodeType type: T.Type) async throws -> T {
return try await withCheckedThrowingContinuation { [weak self] continuation in
guard let self else {
continuation.resume(throwing: NetworkError.cannotConnectToServer)
return
}

sessionManager.request(route)
.validate(statusCode: 200 ..< 300)
.responseData { [weak self] responseData in
guard let self else {
continuation.resume(throwing: NetworkError.cannotParseJson)
return
}

switch responseData.result {
case .success(let data):
do {
let retVal = try decoder.decode(type, from: data)
continuation.resume(returning: retVal)
} catch {
continuation.resume(throwing: NetworkError.cannotParseJson)
}

case .failure:
guard let data = responseData.data else {
continuation.resume(throwing: NetworkError.cannotConnectToServer)
return
}

guard let responseError = try? decoder.decode(ResponseError.self, from: data) else {
continuation.resume(throwing: NetworkError.cannotConnectToServer)
return
}

continuation.resume(throwing: NetworkError.serverError(message: responseError))
}
}
}
}
}
64 changes: 64 additions & 0 deletions Data/Sources/Data/Http/ApiRouter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// ApiRouter.swift
//
//
// Created by Hessam Mahdiabadi on 11/5/23.
//

import Foundation
import Alamofire

public enum ApiRouter: URLRequestConvertible {

public typealias Params = [String: Any]

case transferList(offset: Int)

public func asURLRequest() throws -> URLRequest {

let httpMethod = getHttpMethod()
let url = createURL()
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = httpMethod.rawValue
urlRequest.timeoutInterval = 20.0
urlRequest.cachePolicy = .reloadIgnoringLocalCacheData

let encoding: ParameterEncoding = {
switch httpMethod {
default:
return URLEncoding.queryString
}
}()

return try encoding.encode(urlRequest, with: self.getParams())
}
}

public extension ApiRouter {

func getHttpMethod() -> HTTPMethod {
switch self {
case .transferList:
return .get
}
}

func getParams() -> Params? {
return nil
}

var urlPath: String {
switch self {
case .transferList(let offset):
return "/transfer-list/\(offset)"
}
}

func createURL() -> URL {
var component = URLComponents()
component.scheme = "https"
component.host = "191da1ac-768c-4c6a-80ad-b533beafec25.mock.pstmn.io"
component.path = urlPath
return component.url!
}
}
30 changes: 30 additions & 0 deletions Data/Sources/Data/Http/DataResponse/CardDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// CardDTO.swift
//
//
// Created by Hessam Mahdiabadi on 11/5/23.
//

import Foundation

struct CardDTO: Decodable {

var cardNumber: String?
var cardType: String?

init(cardNumber: String? = nil, cardType: String? = nil) {
self.cardNumber = cardNumber
self.cardType = cardType
}

enum CodingKeys: String, CodingKey {
case cardNumber = "card_number"
case cardType = "card_type"
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.cardNumber = try container.decodeIfPresent(String.self, forKey: .cardNumber)
self.cardType = try container.decodeIfPresent(String.self, forKey: .cardType)
}
}
Loading

0 comments on commit 41f5e00

Please sign in to comment.