Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/modularization #9

Merged
merged 38 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
97532fc
Add .gitignore file
helloItsHEssam Nov 4, 2023
a095d26
Adjust Minimum Deployment Target to iOS 14.0
helloItsHEssam Nov 4, 2023
5287d10
Add Entities: Person, Card, CardTransferCount, and PersonBankAccount
helloItsHEssam Nov 4, 2023
7c735fc
Add Repository, UseCase, and AnyPublisher Extension
helloItsHEssam Nov 4, 2023
f3ecb97
Refine AnyPublisher Extension to Constrain Error Type, Add GitHub Wor…
helloItsHEssam Nov 4, 2023
4dd9b83
Merge pull request #1 from helloItsHEssam/feature/modularization-domain
helloItsHEssam Nov 4, 2023
adacf5f
Add API Components: Protocol, Router, DTO Model, and Alamofire Library
helloItsHEssam Nov 4, 2023
2366efb
Add Mapper, Rename Folder, and Implement Repository
helloItsHEssam Nov 5, 2023
b12c3fc
Add Database Protocol and Error Handling
helloItsHEssam Nov 5, 2023
7fc9d28
Add Core Data Transformer, Convert DTOs to Classes, and Implement Per…
helloItsHEssam Nov 5, 2023
4f4eb30
Add One-to-One Relationships, Local Protocol, and New Database Entities
helloItsHEssam Nov 5, 2023
557d873
Add Local Repository Integration in PersonBankAccountRepositoryImpl
helloItsHEssam Nov 5, 2023
e374d01
Fix Error Message Retrieval from Server on Invalid Page Number Request
helloItsHEssam Nov 5, 2023
852364d
Resolve Name Violation: Variable Name Contains Only Alphanumeric Char…
helloItsHEssam Nov 5, 2023
1692986
Add Update of Favorite Status If Exist in Database
helloItsHEssam Nov 5, 2023
653f3a4
fixes compiler error in mock test. implemented updatefavoriteStatusBa…
helloItsHEssam Nov 5, 2023
af0ff73
Merge pull request #2 from helloItsHEssam/fix/removeExtraFunctionInDo…
helloItsHEssam Nov 5, 2023
7998b84
Add GitHub Workflow for Linting and Testing and Add Fetch Index
helloItsHEssam Nov 5, 2023
f9ae381
Merge pull request #5 from helloItsHEssam/feature/modularization-data
helloItsHEssam Nov 5, 2023
b7a4764
Add Theme Color, Raleway Font with Dynamic Type Support, and Instanti…
helloItsHEssam Nov 5, 2023
1cc9512
Add Label Components, BaseCollectionView, Base ViewController, Font S…
helloItsHEssam Nov 5, 2023
40a0eb2
Add BaseCollectionCell, BorderCompositionalLayout, Vertical Layout, a…
helloItsHEssam Nov 5, 2023
c26f532
Add SafeArea Extension, Pin to Superview, and CollectionView Cell Reg…
helloItsHEssam Nov 5, 2023
a2959e4
Merge pull request #6 from helloItsHEssam/feature/modularization-ui
helloItsHEssam Nov 5, 2023
aff299b
Fix Access Control to Open
helloItsHEssam Nov 6, 2023
0e45fb5
Merge pull request #7 from helloItsHEssam/fix/module-ui
helloItsHEssam Nov 6, 2023
8299716
Add DiContainer Protocol, HomeViewController, and BaseCollectionViewC…
helloItsHEssam Nov 6, 2023
8284464
Add LargeHeaderCell, HomeCollectionViewDataSource, and HomeItem
helloItsHEssam Nov 6, 2023
7b59910
Add VerticalAccount Cell, All Title and Account Display, ViewModels
helloItsHEssam Nov 6, 2023
5c33b5b
Add DataTransfer, PaginationMode, and Datasource Updates
helloItsHEssam Nov 6, 2023
1088042
Implement Pagination Support in Collection View with Continuous Data …
helloItsHEssam Nov 6, 2023
f2a7231
Add AccountCell, Inheritance for VerticalAccountCell and FavoriteCell…
helloItsHEssam Nov 6, 2023
19ec39e
Add DetailViewController, HeaderInformationCell, and TitleValueCell
helloItsHEssam Nov 6, 2023
1adf068
Add Save and Remove from Favorites, Data Reload in DetailViewControll…
helloItsHEssam Nov 6, 2023
f86c65d
Added Handling of Save and Remove in Favorites, HomeViewController Li…
helloItsHEssam Nov 6, 2023
c7234b3
Fix Error Display for ViewControllers and Enhance Performance
helloItsHEssam Nov 6, 2023
65f8cca
Refactor Variable Names, Function Renaming, ViewModel Adjustments, an…
helloItsHEssam Nov 6, 2023
5268871
Merge pull request #8 from helloItsHEssam/feature/modularization-pres…
helloItsHEssam Nov 6, 2023
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
23 changes: 23 additions & 0 deletions .github/workflows/Data.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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
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
32 changes: 32 additions & 0 deletions Data/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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"],
resources: [.process("Resources")]),
.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)
}
}
45 changes: 45 additions & 0 deletions Data/Sources/Data/Http/DataResponse/CardTransferCountDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// CardTransferCountDTO.swift
//
//
// Created by Hessam Mahdiabadi on 11/5/23.
//

import Foundation

class CardTransferCountDTO: NSObject, NSSecureCoding, Decodable {

static var supportsSecureCoding: Bool = true
var numberOfTransfers: Int?
var totalTransfer: Int?

init(numberOfTransfers: Int? = nil, totalTransfer: Int? = nil) {
self.numberOfTransfers = numberOfTransfers
self.totalTransfer = totalTransfer
}

func encode(with coder: NSCoder) {
coder.encode(numberOfTransfers, forKey: CodingKeys.numberOfTransfers.rawValue)
coder.encode(totalTransfer, forKey: CodingKeys.totalTransfer.rawValue)
}

enum CodingKeys: String, CodingKey {
case numberOfTransfers = "number_of_transfers"
case totalTransfer = "total_transfer"
}

required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.numberOfTransfers = try container.decodeIfPresent(Int.self, forKey: .numberOfTransfers)
self.totalTransfer = try container.decodeIfPresent(Int.self, forKey: .totalTransfer)
}

required convenience init?(coder: NSCoder) {
let numberOfTransfers = coder.decodeObject(of: NSNumber.self,
forKey: CodingKeys.numberOfTransfers.rawValue)?.intValue
let totalTransfer = coder.decodeObject(of: NSNumber.self,
forKey: CodingKeys.totalTransfer.rawValue)?.intValue

self.init(numberOfTransfers: numberOfTransfers, totalTransfer: totalTransfer)
}
}
Loading