diff --git a/Projects/NetworkService/.gitignore b/Projects/NetworkService/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/Projects/NetworkService/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Projects/NetworkService/Package.swift b/Projects/NetworkService/Package.swift new file mode 100644 index 0000000..095ed28 --- /dev/null +++ b/Projects/NetworkService/Package.swift @@ -0,0 +1,26 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "NetworkService", + platforms: [ + .iOS(.v16) + ], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "NetworkService", + targets: ["NetworkService"]), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "NetworkService"), + .testTarget( + name: "NetworkServiceTests", + dependencies: ["NetworkService"]), + ] +) diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/BaseServiceProtocol.swift b/Projects/NetworkService/Sources/NetworkService/Base/BaseServiceProtocol.swift similarity index 100% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/BaseServiceProtocol.swift rename to Projects/NetworkService/Sources/NetworkService/Base/BaseServiceProtocol.swift diff --git a/Projects/NetworkService/Sources/NetworkService/Base/NetworkLoader.swift b/Projects/NetworkService/Sources/NetworkService/Base/NetworkLoader.swift new file mode 100644 index 0000000..44066d0 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/Base/NetworkLoader.swift @@ -0,0 +1,16 @@ +// +// NetworkLoader.swift +// SampleAppSwiftUI +// +// Created by Saglam, Fatih on 10.01.2023. +// Copyright © 2023 Adesso Turkey. All rights reserved. +// + +import Foundation + +public class NetworkLoader: NetworkLoaderProtocol { + public static let shared = NetworkLoader() + + public var session: URLSessionProtocol = URLSession.shared + public var decoder: JSONDecoder = JSONDecoder() +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/NetworkLoaderProtocol.swift b/Projects/NetworkService/Sources/NetworkService/Base/NetworkLoaderProtocol.swift similarity index 89% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/NetworkLoaderProtocol.swift rename to Projects/NetworkService/Sources/NetworkService/Base/NetworkLoaderProtocol.swift index 7784c18..bcc53e0 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/NetworkLoaderProtocol.swift +++ b/Projects/NetworkService/Sources/NetworkService/Base/NetworkLoaderProtocol.swift @@ -8,13 +8,13 @@ import Foundation -protocol URLSessionProtocol { +public protocol URLSessionProtocol { func data(for request: URLRequest, delegate: URLSessionTaskDelegate?) async throws -> (Data, URLResponse) } extension URLSession: URLSessionProtocol { } -protocol NetworkLoaderProtocol { +public protocol NetworkLoaderProtocol { var session: URLSessionProtocol { get set } var decoder: JSONDecoder { get set } @@ -22,7 +22,7 @@ protocol NetworkLoaderProtocol { } extension NetworkLoaderProtocol { - func request(with requestObject: RequestObject, responseModel: T.Type) async throws -> T { + public func request(with requestObject: RequestObject, responseModel: T.Type) async throws -> T { let (data, response) = try await session.data(for: prepareURLRequest(with: requestObject), delegate: nil) let successCodeRange = 200...299 guard let statusCode = (response as? HTTPURLResponse)?.statusCode else { throw AdessoError.badResponse } diff --git a/Projects/NetworkService/Sources/NetworkService/Configuration/Configuration.swift b/Projects/NetworkService/Sources/NetworkService/Configuration/Configuration.swift new file mode 100644 index 0000000..d4b6ab5 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/Configuration/Configuration.swift @@ -0,0 +1,30 @@ +// +// Configuration.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +final class Configuration: ConfigurationProtocol { + static var baseURL: String { + let url: String? = try? self.value(for: "base_url") + return url ?? "" + } + + static var coinApiKey: String { + let key: String? = try? self.value(for: "personal_api") + guard let key, !key.isEmpty else { + /// Get your API key from https://www.cryptocompare.com/ + #warning("Please Enter an API Key") + return "" + } + return key + } + + static var webSocketBaseUrl: String { + let url: String? = try? self.value(for: "webSocket_base_url") + return url ?? "" + } +} diff --git a/Projects/NetworkService/Sources/NetworkService/Configuration/ConfigurationError.swift b/Projects/NetworkService/Sources/NetworkService/Configuration/ConfigurationError.swift new file mode 100644 index 0000000..f9d6961 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/Configuration/ConfigurationError.swift @@ -0,0 +1,12 @@ +// +// ConfigurationError.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +enum ConfigurationError: Swift.Error { + case missingKey, invalidValue +} diff --git a/Projects/NetworkService/Sources/NetworkService/Configuration/ConfigurationProtocol.swift b/Projects/NetworkService/Sources/NetworkService/Configuration/ConfigurationProtocol.swift new file mode 100644 index 0000000..10549f5 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/Configuration/ConfigurationProtocol.swift @@ -0,0 +1,29 @@ +// +// ConfigurationProtocol.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +protocol ConfigurationProtocol {} + +extension ConfigurationProtocol { + static func value(for key: String) throws -> T where T: LosslessStringConvertible { + guard let object = Bundle.main.object(forInfoDictionaryKey: key) else { + throw ConfigurationError.missingKey + } + + switch object { + case let value as T: + return value + case let string as String: + guard let value = T(string) else { fallthrough } + return value + default: + throw ConfigurationError.invalidValue + } + } +} + diff --git a/Projects/NetworkService/Sources/NetworkService/Endpoints/AllCoinEndpoints.swift b/Projects/NetworkService/Sources/NetworkService/Endpoints/AllCoinEndpoints.swift new file mode 100644 index 0000000..92d05e9 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/Endpoints/AllCoinEndpoints.swift @@ -0,0 +1,23 @@ +// +// AllCoinEndpoints.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +enum AllCoinEndpoints: TargetEndpointProtocol { + case allCoin(limit: Int, unitToBeConverted: String, page: Int) + + private struct Constants { + static let allCoinEndpoint = "top/mktcapfull?limit=%d&tsym=%@&page=%d&api_key=%@" + } + + var path: String { + switch self { + case .allCoin(let limit, let toConvert, let page): + return BaseEndpoints.base.path + String(format: Constants.allCoinEndpoint, limit, toConvert, page, Configuration.coinApiKey) + } + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Endpoints/BaseEndpoint.swift b/Projects/NetworkService/Sources/NetworkService/Endpoints/BaseEndpoints.swift similarity index 68% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Endpoints/BaseEndpoint.swift rename to Projects/NetworkService/Sources/NetworkService/Endpoints/BaseEndpoints.swift index 5408b31..8745e92 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Endpoints/BaseEndpoint.swift +++ b/Projects/NetworkService/Sources/NetworkService/Endpoints/BaseEndpoints.swift @@ -1,5 +1,5 @@ // -// BaseEndpoint.swift +// BaseEndpoints.swift // SampleAppSwiftUI // // Created by Saglam, Fatih on 11.01.2023. @@ -8,7 +8,11 @@ import Foundation -enum BaseEndpoint: TargetEndpointProtocol { +protocol TargetEndpointProtocol { + var path: String { get } +} + +enum BaseEndpoints: TargetEndpointProtocol { case base var path: String { diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinNewsService/CoinNewsServiceEndpoint.swift b/Projects/NetworkService/Sources/NetworkService/Endpoints/CoinNewsEndpoints.swift similarity index 51% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinNewsService/CoinNewsServiceEndpoint.swift rename to Projects/NetworkService/Sources/NetworkService/Endpoints/CoinNewsEndpoints.swift index 67d6fe2..cef7af6 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinNewsService/CoinNewsServiceEndpoint.swift +++ b/Projects/NetworkService/Sources/NetworkService/Endpoints/CoinNewsEndpoints.swift @@ -1,13 +1,13 @@ // -// CoinNewsServiceEndpoint.swift -// SampleAppSwiftUI +// CoinNewsEndpoints.swift +// NetworkService // -// Created by Meryem Şahin on 20.07.2023. +// Created by Abay, Batuhan on 6.12.2023. // import Foundation -enum CoinNewsServiceEndpoint: TargetEndpointProtocol { +enum CoinNewsEndpoints: TargetEndpointProtocol { case coinNews(coinCode: String) @@ -18,7 +18,7 @@ enum CoinNewsServiceEndpoint: TargetEndpointProtocol { var path: String { switch self { case .coinNews(coinCode: let coinCode): - return BaseEndpoint.base.path + String(format: Constants.coinNewsEndpoint, coinCode, Configuration.coinApiKey) + return BaseEndpoints.base.path + String(format: Constants.coinNewsEndpoint, coinCode, Configuration.coinApiKey) } } } diff --git a/Projects/NetworkService/Sources/NetworkService/Endpoints/CoinPriceHistoryEndpoints.swift b/Projects/NetworkService/Sources/NetworkService/Endpoints/CoinPriceHistoryEndpoints.swift new file mode 100644 index 0000000..dfa989b --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/Endpoints/CoinPriceHistoryEndpoints.swift @@ -0,0 +1,27 @@ +// +// CoinPriceHistoryEndpoints.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +enum CoinPriceHistoryEndpoints: TargetEndpointProtocol { + case daily(coinCode: String, unitToBeConverted: String, dayLimit: Int, aggregate: Int) + case hourly(coinCode: String, unitToBeConverted: String, hourLimit: Int, aggregate: Int) + + private struct Constants { + static let dailyEndpoint = "v2/histoday?fsym=%@&tsym=%@&limit=%d&aggregate=%d&api_key=%@" + static let hourlyEndpoint = "v2/histohour?fsym=%@&tsym=%@&limit=%d&aggregate=%d&api_key=%@" + } + + var path: String { + switch self { + case .daily(let coinCode, let unitToBeConverted, let dayLimit, let aggregate): + return BaseEndpoints.base.path + String(format: Constants.dailyEndpoint, coinCode, unitToBeConverted, dayLimit, aggregate, Configuration.coinApiKey) + case .hourly(let coinCode, let unitToBeConverted, let hourLimit, let aggregate): + return BaseEndpoints.base.path + String(format: Constants.hourlyEndpoint, coinCode, unitToBeConverted, hourLimit, aggregate, Configuration.coinApiKey) + } + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/ExampleService/ExampleServiceEndpoint.swift b/Projects/NetworkService/Sources/NetworkService/Endpoints/ExampleEndpoints.swift similarity index 68% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/ExampleService/ExampleServiceEndpoint.swift rename to Projects/NetworkService/Sources/NetworkService/Endpoints/ExampleEndpoints.swift index df0041e..ea6bc95 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/ExampleService/ExampleServiceEndpoint.swift +++ b/Projects/NetworkService/Sources/NetworkService/Endpoints/ExampleEndpoints.swift @@ -1,5 +1,5 @@ // -// ExampleServiceEndpoint.swift +// ExampleEndpoints.swift // SampleAppSwiftUI // // Created by Saglam, Fatih on 16.01.2023. @@ -8,7 +8,7 @@ import Foundation -enum ExampleServiceEndpoint: TargetEndpointProtocol { +enum ExampleEndpoints: TargetEndpointProtocol { case example(firstParameter: String, secondParameter: String) private struct Constants { @@ -18,7 +18,7 @@ enum ExampleServiceEndpoint: TargetEndpointProtocol { var path: String { switch self { case .example(let firstParameter, let secondParameter): - return BaseEndpoint.base.path + String(format: Constants.exampleEndpoint, firstParameter, secondParameter) + return BaseEndpoints.base.path + String(format: Constants.exampleEndpoint, firstParameter, secondParameter) } } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/AdessoError.swift b/Projects/NetworkService/Sources/NetworkService/Entities/AdessoError.swift similarity index 97% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/AdessoError.swift rename to Projects/NetworkService/Sources/NetworkService/Entities/AdessoError.swift index 2e2e677..1dc4b05 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/AdessoError.swift +++ b/Projects/NetworkService/Sources/NetworkService/Entities/AdessoError.swift @@ -8,7 +8,7 @@ import Foundation -enum AdessoError: Error, Equatable { +public enum AdessoError: Error, Equatable { case httpError(status: HTTPStatus, data: Data? = nil) case badURL(_ url: String) case unknown(error: NSError) diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/ErrorResponse.swift b/Projects/NetworkService/Sources/NetworkService/Entities/ErrorResponse.swift similarity index 55% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/ErrorResponse.swift rename to Projects/NetworkService/Sources/NetworkService/Entities/ErrorResponse.swift index d2d09cc..6c730c6 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/ErrorResponse.swift +++ b/Projects/NetworkService/Sources/NetworkService/Entities/ErrorResponse.swift @@ -8,8 +8,8 @@ import Foundation -class ErrorResponse: Decodable { - var code: Int? - var message: String? - var messages: [String: String]? +public class ErrorResponse: Decodable { + public var code: Int? + public var message: String? + public var messages: [String: String]? } diff --git a/Projects/NetworkService/Sources/NetworkService/Entities/HTTPMethod.swift b/Projects/NetworkService/Sources/NetworkService/Entities/HTTPMethod.swift new file mode 100644 index 0000000..661c0d3 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/Entities/HTTPMethod.swift @@ -0,0 +1,9 @@ +import Foundation + +public enum HTTPMethod: String { + case delete = "DELETE" + case get = "GET" + case patch = "PATCH" + case post = "POST" + case put = "PUT" +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/HTTPStatus.swift b/Projects/NetworkService/Sources/NetworkService/Entities/HTTPStatus.swift similarity index 98% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/HTTPStatus.swift rename to Projects/NetworkService/Sources/NetworkService/Entities/HTTPStatus.swift index 192a9c4..41a2af8 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/HTTPStatus.swift +++ b/Projects/NetworkService/Sources/NetworkService/Entities/HTTPStatus.swift @@ -8,7 +8,7 @@ import Foundation -enum HTTPStatus: Int, Error { +public enum HTTPStatus: Int, Error { // Default case notValidCode = 0 diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/RequestObject.swift b/Projects/NetworkService/Sources/NetworkService/Entities/RequestObject.swift similarity index 72% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/RequestObject.swift rename to Projects/NetworkService/Sources/NetworkService/Entities/RequestObject.swift index 0defcb7..4956142 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Entities/RequestObject.swift +++ b/Projects/NetworkService/Sources/NetworkService/Entities/RequestObject.swift @@ -8,13 +8,13 @@ import Foundation -struct RequestObject { +public struct RequestObject { var url: String let method: HTTPMethod var data: Encodable? var headers: [String: String]? - init(url: String, + public init(url: String, method: HTTPMethod = .get, data: Encodable? = nil, headers: [String: String] = [:]) { @@ -24,11 +24,3 @@ struct RequestObject { self.headers = headers } } - -enum HTTPMethod: String { - case delete = "DELETE" - case get = "GET" - case patch = "PATCH" - case post = "POST" - case put = "PUT" -} diff --git a/Projects/NetworkService/Sources/NetworkService/Extensions/CodableExtensions.swift b/Projects/NetworkService/Sources/NetworkService/Extensions/CodableExtensions.swift new file mode 100644 index 0000000..ae0938c --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/Extensions/CodableExtensions.swift @@ -0,0 +1,39 @@ +// +// CodableExtensions.swift +// SampleAppSwiftUI +// +// Created by Alver, Tunay on 5.04.2023. +// + +import Foundation + +public extension Encodable { + func encode() -> Data? { + let encoder = JSONEncoder() + encoder.dataEncodingStrategy = .deferredToData + return try? encoder.encode(self) + } + + func encode() -> String? { + let encoder = JSONEncoder() + encoder.dataEncodingStrategy = .deferredToData + if let jsonData = try? encoder.encode(self) { + return String(data: jsonData, encoding: .utf8) + } + return nil + } +} + +public extension Decodable { + static func decode(_ data: Data) -> Self? { + let decoder = JSONDecoder() + decoder.dataDecodingStrategy = .deferredToData + return try? decoder.decode(self, from: data) + } + + static func decode(_ data: String) -> Self? { + let decoder = JSONDecoder() + decoder.dataDecodingStrategy = .deferredToData + return try? decoder.decode(self, from: Data(data.utf8)) + } +} diff --git a/Projects/NetworkService/Sources/NetworkService/Extensions/EncodableExtensions.swift b/Projects/NetworkService/Sources/NetworkService/Extensions/EncodableExtensions.swift new file mode 100644 index 0000000..bb23e12 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/Extensions/EncodableExtensions.swift @@ -0,0 +1,16 @@ +// +// EncodableExtensions.swift +// SampleAppSwiftUI +// +// Created by Bozkurt, Umit on 22.02.2023. +// + +import Foundation + +public extension Encodable { + + func toJSONString() -> String? { + guard let jsonData = try? JSONEncoder().encode(self) else { return nil } + return String(data: jsonData, encoding: .utf8) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Extensions/HTTPURLResponseExtensions.swift b/Projects/NetworkService/Sources/NetworkService/Extensions/HTTPURLResponseExtensions.swift similarity index 100% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Extensions/HTTPURLResponseExtensions.swift rename to Projects/NetworkService/Sources/NetworkService/Extensions/HTTPURLResponseExtensions.swift diff --git a/Projects/NetworkService/Sources/NetworkService/RequestModels/AllCoinRequestModel.swift b/Projects/NetworkService/Sources/NetworkService/RequestModels/AllCoinRequestModel.swift new file mode 100644 index 0000000..3e9b8fe --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/RequestModels/AllCoinRequestModel.swift @@ -0,0 +1,28 @@ +// +// AllCoinRequestModel.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +public struct AllCoinRequestModel: Encodable { + let limit: Int + let unitToBeConverted: String + let page: Int + + public init(limit: Int = 3, + unitToBeConverted: String = "USD", + page: Int = 1) { + self.limit = limit + self.unitToBeConverted = unitToBeConverted + self.page = page + } + + enum CodingKeys: String, CodingKey { + case limit = "limit" + case unitToBeConverted = "unitToBeConverted" + case page = "page" + } +} diff --git a/Projects/NetworkService/Sources/NetworkService/RequestModels/CoinNewsRequestModel.swift b/Projects/NetworkService/Sources/NetworkService/RequestModels/CoinNewsRequestModel.swift new file mode 100644 index 0000000..b79cccc --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/RequestModels/CoinNewsRequestModel.swift @@ -0,0 +1,20 @@ +// +// CoinNewsRequestModel.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +public struct CoinNewsRequestModel: Encodable { + let coinCode: String + + public init(coinCode: String) { + self.coinCode = coinCode + } + + enum CodingKeys: String, CodingKey { + case coinCode = "coinCode" + } +} diff --git a/Projects/NetworkService/Sources/NetworkService/RequestModels/CoinPriceHistoryRequestModel.swift b/Projects/NetworkService/Sources/NetworkService/RequestModels/CoinPriceHistoryRequestModel.swift new file mode 100644 index 0000000..38242cb --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/RequestModels/CoinPriceHistoryRequestModel.swift @@ -0,0 +1,32 @@ +// +// CoinPriceHistoryRequestModel.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +public struct CoinPriceHistoryRequestModel: Encodable { + let coinCode: String + let unitToBeConverted: String + let limit: Int + let aggregate: Int + + public init(coinCode: String, + unitToBeConverted: String = "USD", + limit: Int = 30, + aggregate: Int = 1) { + self.coinCode = coinCode + self.unitToBeConverted = unitToBeConverted + self.limit = limit + self.aggregate = aggregate + } + + enum CodingKeys: String, CodingKey { + case coinCode = "coinCode" + case unitToBeConverted = "unitToBeConverted" + case limit = "limit" + case aggregate = "aggregate" + } +} diff --git a/Projects/NetworkService/Sources/NetworkService/RequestModels/ExampleRequestModel.swift b/Projects/NetworkService/Sources/NetworkService/RequestModels/ExampleRequestModel.swift new file mode 100644 index 0000000..90efad0 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/RequestModels/ExampleRequestModel.swift @@ -0,0 +1,23 @@ +// +// ExampleRequestModel.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +public struct ExampleRequestModel: Encodable { + let firstParameter: String + let secondParameter: String + + public init(firstParameter: String, secondParameter: String) { + self.firstParameter = firstParameter + self.secondParameter = secondParameter + } + + enum CodingKeys: String, CodingKey { + case firstParameter = "firstParameter" + case secondParameter = "secondParameter" + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/AllCoinResponse.swift b/Projects/NetworkService/Sources/NetworkService/ResponseModels/AllCoinResponse.swift similarity index 53% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/AllCoinResponse.swift rename to Projects/NetworkService/Sources/NetworkService/ResponseModels/AllCoinResponse.swift index 2364564..b317763 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/AllCoinResponse.swift +++ b/Projects/NetworkService/Sources/NetworkService/ResponseModels/AllCoinResponse.swift @@ -7,25 +7,23 @@ import Foundation -typealias CoinCode = String - // MARK: - AllCoinResponse -struct AllCoinResponse: Codable, Hashable, Identifiable { - var id = UUID().uuidString - let data: [CoinData]? +public struct AllCoinResponse: Codable, Hashable, Identifiable { + public var id = UUID().uuidString + public let data: [CoinData]? enum CodingKeys: String, CodingKey { case data = "Data" } } -// MARK: - Datum -struct CoinData: Codable, Hashable, Identifiable { - var id = UUID().uuidString - var coinInfo: CoinMarketCapsCoinInfo? - var detail: CoinRaw? +// MARK: - CoinData +public struct CoinData: Codable, Hashable, Identifiable { + public var id = UUID().uuidString + public var coinInfo: CoinMarketCapsCoinInfo? + public var detail: CoinRaw? - static func == (lhs: CoinData, rhs: CoinData) -> Bool { + public static func == (lhs: CoinData, rhs: CoinData) -> Bool { lhs.coinInfo == rhs.coinInfo && lhs.detail == rhs.detail } @@ -35,18 +33,18 @@ struct CoinData: Codable, Hashable, Identifiable { case detail = "RAW" } - static let demo = CoinData(coinInfo: CoinMarketCapsCoinInfo(code: "BTC", title: "Demo"), + public static let demo = CoinData(coinInfo: CoinMarketCapsCoinInfo(code: "BTC", title: "Demo"), detail: CoinRaw(usd: RawUsd(price: 29467.560, changeAmount: 28.015, changePercentage: 29.74))) } // MARK: - CoinInfo -struct CoinMarketCapsCoinInfo: Codable, Hashable { - let code: CoinCode? - var title: String? +public struct CoinMarketCapsCoinInfo: Codable, Hashable { + public let code: String? + public var title: String? - static func == (lhs: CoinMarketCapsCoinInfo, rhs: CoinMarketCapsCoinInfo) -> Bool { + public static func == (lhs: CoinMarketCapsCoinInfo, rhs: CoinMarketCapsCoinInfo) -> Bool { lhs.code == rhs.code } @@ -56,25 +54,26 @@ struct CoinMarketCapsCoinInfo: Codable, Hashable { } } -// MARK: - Raw -struct CoinRaw: Codable, Hashable { - var usd: RawUsd? +// MARK: - CoinRaw +public struct CoinRaw: Codable, Hashable { + public var usd: RawUsd? - static func == (lhs: CoinRaw, rhs: CoinRaw) -> Bool { + public static func == (lhs: CoinRaw, rhs: CoinRaw) -> Bool { lhs.usd == rhs.usd } + enum CodingKeys: String, CodingKey { case usd = "USD" } } // MARK: - RawUsd -struct RawUsd: Codable, Hashable { - var price: Double? - var changeAmount: Double? - var changePercentage: Double? +public struct RawUsd: Codable, Hashable { + public var price: Double? + public var changeAmount: Double? + public var changePercentage: Double? - static func == (lhs: RawUsd, rhs: RawUsd) -> Bool { + public static func == (lhs: RawUsd, rhs: RawUsd) -> Bool { lhs.price == rhs.price && lhs.changeAmount == rhs.changeAmount && lhs.changePercentage == rhs.changePercentage diff --git a/Projects/NetworkService/Sources/NetworkService/ResponseModels/CoinNewsResponse.swift b/Projects/NetworkService/Sources/NetworkService/ResponseModels/CoinNewsResponse.swift new file mode 100644 index 0000000..7391d56 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/ResponseModels/CoinNewsResponse.swift @@ -0,0 +1,39 @@ +// +// CoinNewsResponse.swift +// SampleAppSwiftUI +// +// Created by Meryem Şahin on 20.07.2023. +// + +import Foundation + +public struct CoinNewsResponse: Codable, Hashable, Identifiable { + public var id = UUID() + public let type: Int + public let message: String + public let data: [CoinNewData]? + public let hasWarning: Bool + + enum CodingKeys: String, CodingKey { + case type = "Type" + case message = "Message" + case data = "Data" + case hasWarning = "HasWarning" + } +} + +// MARK: - Datum +public struct CoinNewData: Codable, Hashable, Identifiable { + public let id: String + public let imageurl: String + public let title: String + public let url: String + public let body: String + public let source: String + + enum CodingKeys: String, CodingKey { + case id + case imageurl, title, url, body + case source + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/CoinPriceHistoryResponse.swift b/Projects/NetworkService/Sources/NetworkService/ResponseModels/CoinPriceHistoryResponse.swift similarity index 52% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/CoinPriceHistoryResponse.swift rename to Projects/NetworkService/Sources/NetworkService/ResponseModels/CoinPriceHistoryResponse.swift index 6ee990c..9d5d75c 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/CoinPriceHistoryResponse.swift +++ b/Projects/NetworkService/Sources/NetworkService/ResponseModels/CoinPriceHistoryResponse.swift @@ -7,19 +7,19 @@ import Foundation -struct CoinPriceHistoryResponse: Codable { - let data: CoinPriceHistoryData? +public struct CoinPriceHistoryResponse: Codable { + public let data: CoinPriceHistoryData? enum CodingKeys: String, CodingKey { case data = "Data" } } -struct CoinPriceHistoryData: Codable { - let aggregated: Bool? - let timeFrom: TimeInterval? - let timeTo: TimeInterval? - let data: [CoinPriceInfo]? +public struct CoinPriceHistoryData: Codable { + public let aggregated: Bool? + public let timeFrom: TimeInterval? + public let timeTo: TimeInterval? + public let data: [CoinPriceInfo]? enum CodingKeys: String, CodingKey { case aggregated = "Aggregated" @@ -29,14 +29,14 @@ struct CoinPriceHistoryData: Codable { } } -struct CoinPriceInfo: Codable { - let time: TimeInterval? - let high: Float? - let low: Float? - let open: Float? - let volumeFrom: Float? - let volumeTo: Float? - let close: Float? +public struct CoinPriceInfo: Codable { + public let time: TimeInterval? + public let high: Float? + public let low: Float? + public let open: Float? + public let volumeFrom: Float? + public let volumeTo: Float? + public let close: Float? enum CodingKeys: String, CodingKey { case time diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/ExampleResponse.swift b/Projects/NetworkService/Sources/NetworkService/ResponseModels/ExampleResponse.swift similarity index 71% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/ExampleResponse.swift rename to Projects/NetworkService/Sources/NetworkService/ResponseModels/ExampleResponse.swift index f34b39d..6c1b2f2 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/ExampleResponse.swift +++ b/Projects/NetworkService/Sources/NetworkService/ResponseModels/ExampleResponse.swift @@ -8,6 +8,6 @@ import Foundation -struct ExampleResponse: Decodable { - var value: String? +public struct ExampleResponse: Decodable { + public let value: String? } diff --git a/Projects/NetworkService/Sources/NetworkService/Utility/LoggerManagerProtocol.swift b/Projects/NetworkService/Sources/NetworkService/Utility/LoggerManagerProtocol.swift new file mode 100644 index 0000000..ee3b5a9 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/Utility/LoggerManagerProtocol.swift @@ -0,0 +1,16 @@ +// +// LoggerManagerProtocol.swift +// NetworkService +// +// Created by Abay, Batuhan on 7.12.2023. +// + +import Foundation + +public protocol LoggerManagerProtocol { + func setVerbose(_ message: String) + func setDebug(_ message: String) + func setInfo(_ message: String) + func setWarn(_ message: String) + func setError(_ message: String) +} diff --git a/Projects/NetworkService/Sources/NetworkService/WebServices/AllCoinService.swift b/Projects/NetworkService/Sources/NetworkService/WebServices/AllCoinService.swift new file mode 100644 index 0000000..01c65ad --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/WebServices/AllCoinService.swift @@ -0,0 +1,29 @@ +// +// AllCoinService.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +public protocol AllCoinServiceProtocol { + func allCoinRequest(requestModel: AllCoinRequestModel) async throws -> AllCoinResponse +} + +public final class AllCoinService: AllCoinServiceProtocol, BaseServiceProtocol { + typealias Endpoint = AllCoinEndpoints + var networkLoader: NetworkLoaderProtocol + + public init(networkLoader: NetworkLoaderProtocol = NetworkLoader.shared){ + self.networkLoader = networkLoader + } + + public func allCoinRequest(requestModel: AllCoinRequestModel) async throws -> AllCoinResponse { + let urlString = build(endpoint: .allCoin(limit: requestModel.limit, + unitToBeConverted: requestModel.unitToBeConverted, + page: requestModel.page)) + + return try await request(with: RequestObject(url: urlString), responseModel: AllCoinResponse.self) + } +} diff --git a/Projects/NetworkService/Sources/NetworkService/WebServices/CoinNewsService.swift b/Projects/NetworkService/Sources/NetworkService/WebServices/CoinNewsService.swift new file mode 100644 index 0000000..1065155 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/WebServices/CoinNewsService.swift @@ -0,0 +1,27 @@ +// +// CoinNewsService.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +public protocol CoinNewsServiceProtocol { + func coinNewsRequest(requestModel: CoinNewsRequestModel) async throws -> CoinNewsResponse +} + +public final class CoinNewsService: CoinNewsServiceProtocol, BaseServiceProtocol { + typealias Endpoint = CoinNewsEndpoints + var networkLoader: NetworkLoaderProtocol + + public init(networkLoader: NetworkLoaderProtocol = NetworkLoader.shared){ + self.networkLoader = networkLoader + } + + public func coinNewsRequest(requestModel: CoinNewsRequestModel) async throws -> CoinNewsResponse { + let urlString = build(endpoint: .coinNews(coinCode: requestModel.coinCode)) + + return try await request(with: RequestObject(url: urlString), responseModel: CoinNewsResponse.self) + } +} diff --git a/Projects/NetworkService/Sources/NetworkService/WebServices/CoinPriceHistoryService.swift b/Projects/NetworkService/Sources/NetworkService/WebServices/CoinPriceHistoryService.swift new file mode 100644 index 0000000..4834b03 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/WebServices/CoinPriceHistoryService.swift @@ -0,0 +1,40 @@ +// +// CoinPriceHistoryService.swift +// NetworkService +// +// Created by Abay, Batuhan on 6.12.2023. +// + +import Foundation + +public protocol CoinPriceHistoryServiceProtocol { + func dailyPriceHistoryRequest(requestModel: CoinPriceHistoryRequestModel) async throws -> CoinPriceHistoryResponse + func hourlyPriceHistoryRequest(requestModel: CoinPriceHistoryRequestModel) async throws -> CoinPriceHistoryResponse +} + +public final class CoinPriceHistoryService: CoinPriceHistoryServiceProtocol, BaseServiceProtocol { + typealias Endpoint = CoinPriceHistoryEndpoints + var networkLoader: NetworkLoaderProtocol + + public init(networkLoader: NetworkLoaderProtocol = NetworkLoader.shared){ + self.networkLoader = networkLoader + } + + public func dailyPriceHistoryRequest(requestModel: CoinPriceHistoryRequestModel) async throws -> CoinPriceHistoryResponse { + let urlString = build(endpoint: .daily(coinCode: requestModel.coinCode, + unitToBeConverted: requestModel.unitToBeConverted, + dayLimit: requestModel.limit, + aggregate: requestModel.aggregate)) + + return try await request(with: RequestObject(url: urlString), responseModel: CoinPriceHistoryResponse.self) + } + + public func hourlyPriceHistoryRequest(requestModel: CoinPriceHistoryRequestModel) async throws -> CoinPriceHistoryResponse { + let urlString = build(endpoint: .hourly(coinCode: requestModel.coinCode, + unitToBeConverted: requestModel.unitToBeConverted, + hourLimit: requestModel.limit, + aggregate: requestModel.aggregate)) + + return try await request(with: RequestObject(url: urlString), responseModel: CoinPriceHistoryResponse.self) + } +} diff --git a/Projects/NetworkService/Sources/NetworkService/WebServices/ExampleService.swift b/Projects/NetworkService/Sources/NetworkService/WebServices/ExampleService.swift new file mode 100644 index 0000000..551abd8 --- /dev/null +++ b/Projects/NetworkService/Sources/NetworkService/WebServices/ExampleService.swift @@ -0,0 +1,29 @@ +// +// ExampleService.swift +// SampleAppSwiftUI +// +// Created by Saglam, Fatih on 16.01.2023. +// Copyright © 2023 Adesso Turkey. All rights reserved. +// + +import Foundation + +public protocol ExampleServiceProtocol { + func exampleRequest(requestModel: ExampleRequestModel) async throws -> ExampleResponse +} + +public final class ExampleService: ExampleServiceProtocol, BaseServiceProtocol { + typealias Endpoint = ExampleEndpoints + var networkLoader: NetworkLoaderProtocol + + public init(networkLoader: NetworkLoaderProtocol = NetworkLoader.shared){ + self.networkLoader = networkLoader + } + + public func exampleRequest(requestModel: ExampleRequestModel) async throws -> ExampleResponse { + let urlString = build(endpoint: .example(firstParameter: requestModel.firstParameter, + secondParameter: requestModel.secondParameter)) + + return try await request(with: RequestObject(url: urlString), responseModel: ExampleResponse.self) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/Base/WebSocketStream.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/Base/WebSocketStream.swift similarity index 89% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/Base/WebSocketStream.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/Base/WebSocketStream.swift index 7f2d644..7cec673 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/Base/WebSocketStream.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/Base/WebSocketStream.swift @@ -29,8 +29,11 @@ class WebSocketStream: NSObject, AsyncSequence { private var continuation: SocketStream.Continuation? private let task: URLSessionWebSocketTask - init(url: URL, session: URLSession = URLSession.shared) { + private var loggerManager: LoggerManagerProtocol? + + init(url: URL, session: URLSession = URLSession.shared, loggerManager: LoggerManagerProtocol?) { task = session.webSocketTask(with: url) + self.loggerManager = loggerManager } deinit { @@ -106,10 +109,10 @@ class WebSocketStream: NSObject, AsyncSequence { guard let self else { return } self.task.sendPing { error in if let error { - LoggerManager().setError(errorMessage: "Pong Error: \(error.localizedDescription)") + self.loggerManager?.setError("Pong Error: \(error.localizedDescription)") timer.invalidate() } else { - Logger().log(level: .info, message: "Connection is alive") + self.loggerManager?.setInfo("Connection is alive") } } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/RequestModels/FavoritesCoinRequest.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/RequestModels/FavoritesCoinRequest.swift similarity index 56% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/RequestModels/FavoritesCoinRequest.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/RequestModels/FavoritesCoinRequest.swift index 36dda4a..4d33887 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/RequestModels/FavoritesCoinRequest.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/RequestModels/FavoritesCoinRequest.swift @@ -7,13 +7,13 @@ import Foundation -struct FavoritesCoinRequest: Codable { - let action: String - var subs: [String] +public struct FavoritesCoinRequest: Codable { + public let action: String + public var subs: [String] } -extension FavoritesCoinRequest { - init(action: SubscriptionRequestAction, codeList: [CoinCode], toChange: String = "USD") { +public extension FavoritesCoinRequest { + init(action: SubscriptionRequestAction, codeList: [String], toChange: String = "USD") { self.action = action.rawValue self.subs = [] codeList.forEach({ self.subs.append(String(format: String(localized: "CoinPreRequest"), $0, toChange)) }) diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/RequestModels/SubscriptionRequest.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/RequestModels/SubscriptionRequest.swift similarity index 80% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/RequestModels/SubscriptionRequest.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/RequestModels/SubscriptionRequest.swift index 78ac7c1..c6f9e0c 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/RequestModels/SubscriptionRequest.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/RequestModels/SubscriptionRequest.swift @@ -7,7 +7,7 @@ import Foundation -struct SubscriptionRequest: Codable { +public struct SubscriptionRequest: Codable { let action: String let subs: [String] } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/RequestModels/SubscriptionRequestAction.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/RequestModels/SubscriptionRequestAction.swift similarity index 79% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/RequestModels/SubscriptionRequestAction.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/RequestModels/SubscriptionRequestAction.swift index 3d1b5d1..2806276 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/RequestModels/SubscriptionRequestAction.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/RequestModels/SubscriptionRequestAction.swift @@ -7,7 +7,7 @@ import Foundation -enum SubscriptionRequestAction: String { +public enum SubscriptionRequestAction: String { case add = "SubAdd" case remove = "SubRemove" } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/ResponseModels/FavoritesCoinResponse.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/ResponseModels/FavoritesCoinResponse.swift similarity index 65% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/ResponseModels/FavoritesCoinResponse.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/ResponseModels/FavoritesCoinResponse.swift index 203b9fd..dca8fe4 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/ResponseModels/FavoritesCoinResponse.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/ResponseModels/FavoritesCoinResponse.swift @@ -7,10 +7,10 @@ import Foundation -struct FavoritesCoinResponse: Codable { - let code: String? - let price: Double? - let lowestToday: Double? +public struct FavoritesCoinResponse: Codable { + public let code: String? + public let price: Double? + public let lowestToday: Double? enum CodingKeys: String, CodingKey { case code = "FROMSYMBOL" diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketEndpoint.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketEndpoint.swift similarity index 72% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketEndpoint.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketEndpoint.swift index 8afa565..05538fd 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketEndpoint.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketEndpoint.swift @@ -7,15 +7,15 @@ import Foundation -enum WebSocketEndpoint: TargetEndpointProtocol { +public enum WebSocketEndpoint: TargetEndpointProtocol { case baseCoinApi private struct Constants { static let webSocketEndpoint = Configuration.webSocketBaseUrl - static let apiKey = Configuration.coinApiKey + //static let apiKey = Configuration.coinApiKey } - var path: String { + public var path: String { switch self { case .baseCoinApi: return Constants.webSocketEndpoint diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketExcangeRatesMessage.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketExcangeRatesMessage.swift similarity index 60% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketExcangeRatesMessage.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketExcangeRatesMessage.swift index ee5de8e..f2fff37 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketExcangeRatesMessage.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketExcangeRatesMessage.swift @@ -7,14 +7,14 @@ import Foundation -struct WebSocketExcangeRatesMessage: WebSocketMessageProtocol { +public struct WebSocketExcangeRatesMessage: WebSocketMessageProtocol { - var type: WebSocketMessageType - var apiKey: String = Configuration.coinApiKey - var heartbeat: Bool - var dataType: [SubscribeDataType] - var filterAsset: [CoinAssetType] - var delayTime: Int = 3000 + public var type: WebSocketMessageType + public var apiKey: String = Configuration.coinApiKey + public var heartbeat: Bool + public var dataType: [SubscribeDataType] + public var filterAsset: [CoinAssetType] + public var delayTime: Int = 3000 init(type: WebSocketMessageType = .hello, heartbeat: Bool = false, @@ -36,13 +36,13 @@ struct WebSocketExcangeRatesMessage: WebSocketMessageProtocol { } } -struct ExcangeRatesResponseModel: Codable, Identifiable, ResponseModelProtocol { - var id: String { +public struct ExcangeRatesResponseModel: Codable, Identifiable, ResponseModelProtocol { + public var id: String { coinName() } - let time, assetIDBase, assetIDQuote, rateType: String - let rate: Double - let type: String + public let time, assetIDBase, assetIDQuote, rateType: String + public let rate: Double + public let type: String enum CodingKeys: String, CodingKey { case time @@ -52,12 +52,13 @@ struct ExcangeRatesResponseModel: Codable, Identifiable, ResponseModelProtocol { case rate, type } - func formattedPrice() -> String { + public func formattedPrice() -> String { "\(Int(rate))" } - func coinName() -> String { + public func coinName() -> String { "\(assetIDBase)/\(self.assetIDQuote)" } } -protocol ResponseModelProtocol {} + +public protocol ResponseModelProtocol {} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketMessageProtocol.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketMessageProtocol.swift similarity index 76% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketMessageProtocol.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketMessageProtocol.swift index c11f020..0228ed8 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketMessageProtocol.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketMessageProtocol.swift @@ -7,20 +7,20 @@ import Foundation -protocol WebSocketMessageProtocol: Codable { +public protocol WebSocketMessageProtocol: Codable { var type: WebSocketMessageType { get set } var apiKey: String { get set} var dataType: [SubscribeDataType] { get set} var filterAsset: [CoinAssetType] { get set} } -enum WebSocketMessageType: String, Codable { +public enum WebSocketMessageType: String, Codable { case hello case trade case exchange } -enum SubscribeDataType: String, Codable { +public enum SubscribeDataType: String, Codable { /// Exchange rate updates (VWAP-24H) case exrate @@ -34,7 +34,7 @@ enum SubscribeDataType: String, Codable { case exchange } -enum CoinAssetType: String, Codable { +public enum CoinAssetType: String, Codable { case BTC case ETH case BTCUSD = "BTC/USD" diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketProvider.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketProvider.swift similarity index 77% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketProvider.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketProvider.swift index ed70b08..98162ce 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketProvider.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketProvider.swift @@ -13,11 +13,11 @@ class WebSocketProvider: NSObject { var socket: WebSocketStream init? (endPoint: TargetEndpointProtocol = WebSocketEndpoint.baseCoinApi, - session: URLSession = URLSession.shared ) { + session: URLSession = URLSession.shared, loggerManager: LoggerManagerProtocol?) { guard let url = URL(string: WebSocketEndpoint.baseCoinApi.path) else { return nil } let query = URLQueryItem(name: "api_key", value: Configuration.coinApiKey) let finalURL = url.appending(queryItems: [query]) self.socket = WebSocketStream(url: finalURL, - session: session) + session: session, loggerManager: loggerManager) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketService.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketService.swift similarity index 59% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketService.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketService.swift index bdf72db..a4cf4f3 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketService.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketService.swift @@ -8,8 +8,8 @@ import Foundation import Combine -protocol WebSocketServiceProtocol: AnyObject, Publisher where Self.Output == URLSessionWebSocketTask.Message, Self.Failure == Error { - func connect(endPoint: WebSocketEndpoint) -> (any WebSocketServiceProtocol)? +public protocol WebSocketServiceProtocol: AnyObject, Publisher where Self.Output == URLSessionWebSocketTask.Message, Self.Failure == Error { + func connect(endPoint: WebSocketEndpoint, loggerManager: LoggerManagerProtocol?) -> (any WebSocketServiceProtocol)? func sendMessage(_ message: FavoritesCoinRequest) func disconnect() func connectionHandler(connected: @escaping (any WebSocketServiceProtocol) -> Void, @@ -17,69 +17,71 @@ protocol WebSocketServiceProtocol: AnyObject, Publisher where Self.Output == URL func setPing(time: TimeInterval) } -final class WebSocketService: NSObject, WebSocketServiceProtocol { +public final class WebSocketService: NSObject, WebSocketServiceProtocol { - typealias Output = URLSessionWebSocketTask.Message - typealias Input = URLSessionWebSocketTask.Message - typealias Failure = Error + public typealias Output = URLSessionWebSocketTask.Message + public typealias Input = URLSessionWebSocketTask.Message + public typealias Failure = Error - static let shared = WebSocketService() + public static let shared = WebSocketService() private var stream: WebSocketStream? private var disconnectionHandler: (URLSessionWebSocketTask.CloseCode) -> Void = { _ in } private var connectionHandler: (any WebSocketServiceProtocol) -> Void = { _ in } + private var loggerManager: LoggerManagerProtocol? private override init() {} - func connect(endPoint: WebSocketEndpoint) -> (any WebSocketServiceProtocol)? { + public func connect(endPoint: WebSocketEndpoint, loggerManager: LoggerManagerProtocol?) -> (any WebSocketServiceProtocol)? { let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil) - guard let provider = WebSocketProvider(endPoint: endPoint, session: session) else { + guard let provider = WebSocketProvider(endPoint: endPoint, session: session, loggerManager: loggerManager) else { return nil } + self.loggerManager = loggerManager stream = provider.socket return self } - func receive(subscriber: S) where WebSocketService.Failure == S.Failure, WebSocketService.Output == S.Input { + public func receive(subscriber: S) where WebSocketService.Failure == S.Failure, WebSocketService.Output == S.Input { guard let stream else { return } - let subscription = WebSocketSubscription(socket: stream, subscriber: subscriber) + let subscription = WebSocketSubscription(socket: stream, subscriber: subscriber, loggerManager: self.loggerManager) subscriber.receive(subscription: subscription) } - func connectionHandler(connected: @escaping (any WebSocketServiceProtocol) -> Void, + public func connectionHandler(connected: @escaping (any WebSocketServiceProtocol) -> Void, disconnected: @escaping (URLSessionWebSocketTask.CloseCode) -> Void) -> AnyPublisher { disconnectionHandler = disconnected connectionHandler = connected return self.eraseToAnyPublisher() } - func sendMessage(_ message: FavoritesCoinRequest) { + public func sendMessage(_ message: FavoritesCoinRequest) { guard let messageString = message.toJSONString() else { return } stream?.send(string: messageString) } - func disconnect() { + public func disconnect() { stream?.cancel(closeCode: .goingAway) stream = nil } - func setPing(time: TimeInterval = 10) { + public func setPing(time: TimeInterval = 10) { stream?.sentPingRegularly = true stream?.pingTimeInterval = time } } extension WebSocketService: URLSessionWebSocketDelegate { - func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) { + public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) { connectionHandler(self) } - func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { + public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { disconnectionHandler(closeCode) } } -extension URLSessionWebSocketTask.Message { +public extension URLSessionWebSocketTask.Message { func convert() -> T? { switch self { case .string(let json): diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketSubscription.swift b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketSubscription.swift similarity index 74% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketSubscription.swift rename to Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketSubscription.swift index f802b97..b1a8ef8 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebSocket/WebSocketSubscription.swift +++ b/Projects/NetworkService/Sources/NetworkService/WebSocket/WebSocketSubscription.swift @@ -12,8 +12,10 @@ class WebSocketSubscription: Subscription where S.Input == URLSes private var socket: WebSocketStream private var subscriber: S? + private var loggerManager: LoggerManagerProtocol? - init(socket: WebSocketStream, subscriber: S) { + init(socket: WebSocketStream, subscriber: S, loggerManager: LoggerManagerProtocol?) { + self.loggerManager = loggerManager self.subscriber = subscriber self.socket = socket listen() @@ -35,7 +37,7 @@ class WebSocketSubscription: Subscription where S.Input == URLSes private func listenSocket() async { guard let subscriber = subscriber else { - LoggerManager().setError(errorMessage: "no subscriber") + loggerManager?.setError("no subscriber") return } do { @@ -43,7 +45,7 @@ class WebSocketSubscription: Subscription where S.Input == URLSes _ = subscriber.receive(message) } } catch let error { - LoggerManager().setError(errorMessage: "Something went wrong \(error.localizedDescription)") + loggerManager?.setError("Something went wrong \(error.localizedDescription)") } } } diff --git a/Projects/NetworkService/Tests/NetworkServiceTests/NetworkServiceTests.swift b/Projects/NetworkService/Tests/NetworkServiceTests/NetworkServiceTests.swift new file mode 100644 index 0000000..0fd6c15 --- /dev/null +++ b/Projects/NetworkService/Tests/NetworkServiceTests/NetworkServiceTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import NetworkService + +final class NetworkServiceTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +} diff --git a/Projects/SampleAppSwiftUI/Project.swift b/Projects/SampleAppSwiftUI/Project.swift index 6c50820..189a120 100644 --- a/Projects/SampleAppSwiftUI/Project.swift +++ b/Projects/SampleAppSwiftUI/Project.swift @@ -6,7 +6,8 @@ let project = Project.createAppProject( projectPackages: [ Package.remote(url: "https://github.com/CocoaLumberjack/CocoaLumberjack", requirement: .upToNextMajor(from: "3.8.0")), Package.remote(url: "https://github.com/kean/Pulse", requirement: .upToNextMajor(from: "3.0.0")), - Package.remote(url: "https://github.com/apple/swift-log.git", requirement: .upToNextMajor(from: "1.5.2")) + Package.remote(url: "https://github.com/apple/swift-log.git", requirement: .upToNextMajor(from: "1.5.2")), + Package.local(path: .relativeToRoot("Projects/NetworkService")) ], projectSettings: .projectSettings, destinations: [.iPhone, .iPad, .macWithiPadDesign], @@ -19,7 +20,8 @@ let project = Project.createAppProject( .package(product: "CocoaLumberjack"), .package(product: "CocoaLumberjackSwift"), .package(product: "CocoaLumberjackSwiftLogBackend"), - .package(product: "PulseUI") + .package(product: "PulseUI"), + .package(product: "NetworkService") ], hasUnitTestTarget: true, hasUITestTarget: true diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Application/AppDelegate.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Application/AppDelegate.swift index 9ef5a0a..0b92eb9 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Application/AppDelegate.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Application/AppDelegate.swift @@ -15,6 +15,6 @@ class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { // Handle remote notification failures here - LoggerManager().setError(errorMessage: error.localizedDescription) + LoggerManager.shared.setError(errorMessage: error.localizedDescription) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Application/Services/LoggingService.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Application/Services/LoggingService.swift index e381e9a..f11bb84 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Application/Services/LoggingService.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Application/Services/LoggingService.swift @@ -11,7 +11,7 @@ import UIKit class LoggingService { init() { - LoggerManager.instance.setup(level: .debug) + LoggerManager.shared.setup(level: .debug) logApplicationAndDeviceInfo() } @@ -21,6 +21,6 @@ class LoggingService { let deviceModel = UIDevice.modelName let osVersion = UIDevice.osVersion - LoggerManager.instance.setInfo(version: version, build: build, deviceModel: deviceModel, osVersion: osVersion) + LoggerManager.shared.setInfo(version: version, build: build, deviceModel: deviceModel, osVersion: osVersion) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Constants/URLs.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Constants/URLs.swift index dddcb40..e79d4fb 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Constants/URLs.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Constants/URLs.swift @@ -13,7 +13,7 @@ enum URLs { static let iconPath = "/assets/icons/" static let scaleURL = "@2x.png" - static func getURL(from coinCode: CoinCode) -> URL? { + static func getURL(from coinCode: String) -> URL? { URL(string: "\(baseURL)\(iconPath)\(coinCode.lowercased())\(scaleURL)") } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Managers/LoggerManager.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Managers/LoggerManager.swift index 75d6964..32c54ca 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Managers/LoggerManager.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Managers/LoggerManager.swift @@ -9,9 +9,12 @@ import CocoaLumberjack import CocoaLumberjackSwiftLogBackend import Logging +import NetworkService class LoggerManager { + static let shared = LoggerManager() + private enum Constants { static let secondsInOneDay: TimeInterval = 60 * 60 * 24 static let bytesInTenMegabytes: UInt64 = 1024 * 10 @@ -20,7 +23,8 @@ class LoggerManager { private let logger = Logger() private var fileLogger: DDFileLogger? - static let instance = LoggerManager() + + private init() {} func setLogLevel(_ logLevel: LogLevel) { logger.setLogLevel(logLevel) @@ -68,3 +72,26 @@ class LoggerManager { logger.error(errorMessage) } } + +// MARK: - NetworkService - LoggerManagerProtocol Implementation +extension LoggerManager: LoggerManagerProtocol { + func setVerbose(_ message: String) { + logger.verbose(message) + } + + func setDebug(_ message: String) { + logger.debug(message) + } + + func setInfo(_ message: String) { + logger.info(message) + } + + func setWarn(_ message: String) { + logger.warn(message) + } + + func setError(_ message: String) { + logger.error(message) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/NetworkLoader.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/NetworkLoader.swift deleted file mode 100644 index 77c32c0..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/NetworkLoader.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// NetworkLoader.swift -// SampleAppSwiftUI -// -// Created by Saglam, Fatih on 10.01.2023. -// Copyright © 2023 Adesso Turkey. All rights reserved. -// - -import Foundation - -class NetworkLoader: NetworkLoaderProtocol { - var session: URLSessionProtocol = URLSession.shared - var decoder: JSONDecoder = JSONDecoder() -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/NetworkLoaderProvider.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/NetworkLoaderProvider.swift deleted file mode 100644 index 20ccaaa..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/Base/NetworkLoaderProvider.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// NetworkLoaderProvider.swift -// SampleAppSwiftUI -// -// Created by Saglam, Fatih on 11.01.2023. -// Copyright © 2023 Adesso Turkey. All rights reserved. -// - -import Foundation - -class NetworkLoaderProvider { - - static let shared: NetworkLoaderProvider = NetworkLoaderProvider() - - let networkLoader: NetworkLoaderProtocol - - private init() { - networkLoader = NetworkLoader() - } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/AllCoinRemoteDataSource.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/AllCoinRemoteDataSource.swift index 36b25a1..39d8d77 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/AllCoinRemoteDataSource.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/AllCoinRemoteDataSource.swift @@ -6,6 +6,7 @@ // import Foundation +import NetworkService protocol AllCoinRemoteDataSourceProtocol { func getAllCoin(limit: Int, unitToBeConverted: String, page: Int) async throws -> AllCoinResponse @@ -20,6 +21,8 @@ class AllCoinRemoteDataSource: AllCoinRemoteDataSourceProtocol { } func getAllCoin(limit: Int, unitToBeConverted: String, page: Int) async throws -> AllCoinResponse { - try await allCoinService.allCoinRequest(limit: limit, unitToBeConverted: unitToBeConverted, page: page) + try await allCoinService.allCoinRequest(requestModel: AllCoinRequestModel(limit: limit, + unitToBeConverted: unitToBeConverted, + page: page)) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/CoinNewsDataSource.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/CoinNewsDataSource.swift index 8cce873..771e0ea 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/CoinNewsDataSource.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/CoinNewsDataSource.swift @@ -6,6 +6,7 @@ // import Foundation +import NetworkService protocol CoinNewsRemoteDataSourceProtocol { func getCoinNews(coinCode: String) async throws -> CoinNewsResponse @@ -19,6 +20,6 @@ class CoinNewsRemoteDataSource: CoinNewsRemoteDataSourceProtocol { } func getCoinNews(coinCode: String) async throws -> CoinNewsResponse { - try await coinNewsService.coinNewsRequest(coinCode: coinCode) + try await coinNewsService.coinNewsRequest(requestModel: CoinNewsRequestModel(coinCode: coinCode)) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/CoinPriceHistoryRemoteDataSource.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/CoinPriceHistoryRemoteDataSource.swift index 20372a1..41b667b 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/CoinPriceHistoryRemoteDataSource.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/CoinPriceHistoryRemoteDataSource.swift @@ -6,6 +6,7 @@ // import Foundation +import NetworkService protocol CoinPriceHistoryRemoteDataSourceProtocol { func getDailyPriceHistory(coinCode: String, unitToBeConverted: String, dayLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryResponse @@ -20,11 +21,31 @@ class CoinPriceHistoryRemoteDataSource: CoinPriceHistoryRemoteDataSourceProtocol self.coinPriceHistoryService = coinPriceHistoryService } - func getDailyPriceHistory(coinCode: String, unitToBeConverted: String, dayLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryResponse { - try await coinPriceHistoryService.dailyPriceHistoryRequest(coinCode: coinCode, unitToBeConverted: unitToBeConverted, dayLimit: dayLimit, aggregate: aggregate) + func getDailyPriceHistory(coinCode: String, + unitToBeConverted: String, + dayLimit: Int, + aggregate: Int) async throws -> CoinPriceHistoryResponse { + try await coinPriceHistoryService.dailyPriceHistoryRequest( + requestModel: CoinPriceHistoryRequestModel( + coinCode: coinCode, + unitToBeConverted: unitToBeConverted, + limit: dayLimit, + aggregate: aggregate + ) + ) } - func getHourlyPriceHistory(coinCode: String, unitToBeConverted: String, hourLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryResponse { - try await coinPriceHistoryService.hourlyPriceHistoryRequest(coinCode: coinCode, unitToBeConverted: unitToBeConverted, hourLimit: hourLimit, aggregate: aggregate) + func getHourlyPriceHistory(coinCode: String, + unitToBeConverted: String, + hourLimit: Int, + aggregate: Int) async throws -> CoinPriceHistoryResponse { + try await coinPriceHistoryService.hourlyPriceHistoryRequest( + requestModel: CoinPriceHistoryRequestModel( + coinCode: coinCode, + unitToBeConverted: unitToBeConverted, + limit: hourLimit, + aggregate: aggregate + ) + ) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/ExampleRemoteDataSource.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/ExampleRemoteDataSource.swift index adafa07..55135e1 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/ExampleRemoteDataSource.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/ExampleRemoteDataSource.swift @@ -7,6 +7,7 @@ // import Foundation +import NetworkService protocol ExampleRemoteDataSourceProtocol { func getExample() async throws -> ExampleResponse @@ -21,6 +22,7 @@ class ExampleRemoteDataSource: ExampleRemoteDataSourceProtocol { } func getExample() async throws -> ExampleResponse { - try await exampleService.exampleRequest() + try await exampleService.exampleRequest(requestModel: ExampleRequestModel(firstParameter: "firstParameter", + secondParameter: "secondParameter")) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/WebServiceProvider.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/WebServiceProvider.swift similarity index 96% rename from Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/WebServiceProvider.swift rename to Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/WebServiceProvider.swift index e82bd49..6d7e665 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/WebServiceProvider.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/RemoteDataSources/WebServiceProvider.swift @@ -7,6 +7,7 @@ // import Foundation +import NetworkService class WebServiceProvider { static let shared: WebServiceProvider = WebServiceProvider() diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/CoinNewsResponse.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/CoinNewsResponse.swift deleted file mode 100644 index 06d6717..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/ResponseModels/CoinNewsResponse.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// CoinNewsResponse.swift -// SampleAppSwiftUI -// -// Created by Meryem Şahin on 20.07.2023. -// - -import Foundation - -struct CoinNewsResponse: Codable, Hashable, Identifiable { - var id = UUID() - let type: Int - let message: String - let data: [CoinNewData]? - let hasWarning: Bool - - enum CodingKeys: String, CodingKey { - case type = "Type" - case message = "Message" - case data = "Data" - case hasWarning = "HasWarning" - } -} - -// MARK: - Datum -struct CoinNewData: Codable, Hashable, Identifiable { - let id: String - let imageurl: String - let title: String - let url: String - let body: String - let source: String - - enum CodingKeys: String, CodingKey { - case id - case imageurl, title, url, body - case source - } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/URLBuilder/EndpointBuilder.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/URLBuilder/EndpointBuilder.swift deleted file mode 100644 index 66cb8e7..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/URLBuilder/EndpointBuilder.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// EndpointBuilder.swift -// SampleAppSwiftUI -// -// Created by Saglam, Fatih on 11.01.2023. -// Copyright © 2023 Adesso Turkey. All rights reserved. -// - -import Foundation - -class EndpointBuilder { - - init() { } - - func build(with targetEndpoint: T) -> String { - targetEndpoint.path - } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/URLBuilder/TargetEndpointProtocol.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/URLBuilder/TargetEndpointProtocol.swift deleted file mode 100644 index d65ff1a..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/URLBuilder/TargetEndpointProtocol.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// TargetEndpointProtocol.swift -// SampleAppSwiftUI -// -// Created by Saglam, Fatih on 11.01.2023. -// Copyright © 2023 Adesso Turkey. All rights reserved. -// - -import Foundation - -protocol TargetEndpointProtocol { - var path: String { get } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/AllCoinUseCase.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/AllCoinUseCase.swift index 398be53..1136a50 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/AllCoinUseCase.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/AllCoinUseCase.swift @@ -8,7 +8,7 @@ import Foundation protocol AllCoinUseCaseProtocol { - func fetchAllCoin(limit: Int, unitToBeConverted: String, page: Int) async throws -> AllCoinResponse + func fetchAllCoin(limit: Int, unitToBeConverted: String, page: Int) async throws -> AllCoinUIModel } class AllCoinUseCase: AllCoinUseCaseProtocol { @@ -18,7 +18,8 @@ class AllCoinUseCase: AllCoinUseCaseProtocol { self.allCoinRepository = allCoinRepository } - func fetchAllCoin(limit: Int, unitToBeConverted: String, page: Int) async throws -> AllCoinResponse { - try await allCoinRepository.getAllCoin(limit: limit, unitToBeConverted: unitToBeConverted, page: page) + func fetchAllCoin(limit: Int, unitToBeConverted: String, page: Int) async throws -> AllCoinUIModel { + let responseModel = try await allCoinRepository.getAllCoin(limit: limit, unitToBeConverted: unitToBeConverted, page: page) + return AllCoinUIModel(from: responseModel) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/CoinNewsUseCase.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/CoinNewsUseCase.swift index 122fa19..790e99d 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/CoinNewsUseCase.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/CoinNewsUseCase.swift @@ -8,7 +8,7 @@ import Foundation protocol CoinNewsUseCaseProtocol { - func getCoinNews(coinCode: String) async throws -> CoinNewsResponse + func getCoinNews(coinCode: String) async throws -> CoinNewsUIModel } class CoinNewsUseCase: CoinNewsUseCaseProtocol { @@ -18,7 +18,8 @@ class CoinNewsUseCase: CoinNewsUseCaseProtocol { self.coinNewsRepository = coinNewsRepository } - func getCoinNews(coinCode: String) async throws -> CoinNewsResponse { - try await coinNewsRepository.getCoinNews(coinCode: coinCode) + func getCoinNews(coinCode: String) async throws -> CoinNewsUIModel { + let responseModel = try await coinNewsRepository.getCoinNews(coinCode: coinCode) + return CoinNewsUIModel(from: responseModel) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/CoinPriceHistoryUseCase.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/CoinPriceHistoryUseCase.swift index 1f59327..a7fcfc0 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/CoinPriceHistoryUseCase.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/CoinPriceHistoryUseCase.swift @@ -8,8 +8,8 @@ import Foundation protocol CoinPriceHistoryUseCaseProtocol { - func getDailyPriceHistory(coinCode: String, unitToBeConverted: String, dayLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryResponse - func getHourlyPriceHistory(coinCode: String, unitToBeConverted: String, hourLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryResponse + func getDailyPriceHistory(coinCode: String, unitToBeConverted: String, dayLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryUIModel + func getHourlyPriceHistory(coinCode: String, unitToBeConverted: String, hourLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryUIModel } class CoinPriceHistoryUseCase: CoinPriceHistoryUseCaseProtocol { @@ -20,11 +20,21 @@ class CoinPriceHistoryUseCase: CoinPriceHistoryUseCaseProtocol { self.coinPriceHistoryRepository = coinPriceHistoryRepository } - func getDailyPriceHistory(coinCode: String, unitToBeConverted: String, dayLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryResponse { - try await coinPriceHistoryRepository.getDailyPriceHistory(coinCode: coinCode, unitToBeConverted: unitToBeConverted, dayLimit: dayLimit, aggregate: aggregate) + func getDailyPriceHistory(coinCode: String, unitToBeConverted: String, dayLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryUIModel { + let responseModel = try await coinPriceHistoryRepository.getDailyPriceHistory(coinCode: coinCode, + unitToBeConverted: unitToBeConverted, + dayLimit: dayLimit, + aggregate: aggregate) + + return CoinPriceHistoryUIModel(from: responseModel) } - func getHourlyPriceHistory(coinCode: String, unitToBeConverted: String, hourLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryResponse { - try await coinPriceHistoryRepository.getHourlyPriceHistory(coinCode: coinCode, unitToBeConverted: unitToBeConverted, hourLimit: hourLimit, aggregate: aggregate) + func getHourlyPriceHistory(coinCode: String, unitToBeConverted: String, hourLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryUIModel { + let responseModel = try await coinPriceHistoryRepository.getHourlyPriceHistory(coinCode: coinCode, + unitToBeConverted: unitToBeConverted, + hourLimit: hourLimit, + aggregate: aggregate) + + return CoinPriceHistoryUIModel(from: responseModel) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/ExampleUseCase.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/ExampleUseCase.swift index c089759..1844676 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/ExampleUseCase.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/UseCases/ExampleUseCase.swift @@ -9,7 +9,7 @@ import Foundation protocol ExampleUseCaseProtocol { - func fetchExample() async throws -> ExampleResponse + func fetchExample() async throws -> ExampleUIModel } class ExampleUseCase: ExampleUseCaseProtocol { @@ -19,7 +19,8 @@ class ExampleUseCase: ExampleUseCaseProtocol { self.exampleRepository = exampleRepository } - func fetchExample() async throws -> ExampleResponse { - try await exampleRepository.getExample() + func fetchExample() async throws -> ExampleUIModel { + let responseModel = try await exampleRepository.getExample() + return ExampleUIModel(from: responseModel) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/AllCoinRepository.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/AllCoinRepository.swift index 0d9a0de..2f4f793 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/AllCoinRepository.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/AllCoinRepository.swift @@ -6,6 +6,7 @@ // import Foundation +import NetworkService protocol AllCoinRepositoryProtocol { func getAllCoin(limit: Int, unitToBeConverted: String, page: Int) async throws -> AllCoinResponse diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/CoinNewsRepository.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/CoinNewsRepository.swift index 2ec3425..4262226 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/CoinNewsRepository.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/CoinNewsRepository.swift @@ -6,6 +6,7 @@ // import Foundation +import NetworkService protocol CoinNewsRepositoryProtocol { func getCoinNews(coinCode: String) async throws -> CoinNewsResponse diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/CoinPriceHistoryRepository.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/CoinPriceHistoryRepository.swift index b77d8fd..afd6603 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/CoinPriceHistoryRepository.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/CoinPriceHistoryRepository.swift @@ -6,6 +6,7 @@ // import Foundation +import NetworkService protocol CoinPriceHistoryRepositoryProtocol { func getDailyPriceHistory(coinCode: String, unitToBeConverted: String, dayLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryResponse diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/ExampleRepository.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/ExampleRepository.swift index 64aa9b3..1bdbbc0 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/ExampleRepository.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServiceRepositories/ExampleRepository.swift @@ -7,6 +7,7 @@ // import Foundation +import NetworkService protocol ExampleRepositoryProtocol { func getExample() async throws -> ExampleResponse diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/AllCoinService/AllCoinService.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/AllCoinService/AllCoinService.swift deleted file mode 100644 index 6c03799..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/AllCoinService/AllCoinService.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// AllCoinService.swift -// SampleAppSwiftUI -// -// Created by Uslu, Teyhan on 24.04.2023. -// - -import Foundation - -protocol AllCoinServiceProtocol { - func allCoinRequest(limit: Int, unitToBeConverted: String, page: Int) async throws -> AllCoinResponse -} - -final class AllCoinService: AllCoinServiceProtocol, BaseServiceProtocol { - - typealias Endpoint = AllCoinServiceEndpoint - - let networkLoader: NetworkLoaderProtocol - - init(networkLoader: NetworkLoaderProtocol = NetworkLoaderProvider.shared.networkLoader) { - self.networkLoader = networkLoader - } - - func allCoinRequest(limit: Int = 3, unitToBeConverted: String = "USD", page: Int = 1) async throws -> AllCoinResponse { - try await request(with: RequestObject(url: build(endpoint: .allCoin(limit: limit, unitToBeConverted: unitToBeConverted, page: page))), - responseModel: AllCoinResponse.self) - } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/AllCoinService/AllCoinServiceEndpoint.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/AllCoinService/AllCoinServiceEndpoint.swift deleted file mode 100644 index 87b5e5c..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/AllCoinService/AllCoinServiceEndpoint.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// AllCoinServiceEndpoint.swift -// SampleAppSwiftUI -// -// Created by Uslu, Teyhan on 24.04.2023. -// - -import Foundation - -enum AllCoinServiceEndpoint: TargetEndpointProtocol { - case allCoin(limit: Int = 3, unitToBeConverted: String = "USD", page: Int = 1) - - private struct Constants { - static let allCoinEndpoint = "top/mktcapfull?limit=%d&tsym=%@&page=%d&api_key=%@" - } - - var path: String { - switch self { - case .allCoin(let limit, let toConvert, let page): - return BaseEndpoint.base.path + String(format: Constants.allCoinEndpoint, limit, toConvert, page, Configuration.coinApiKey) - } - } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinNewsService/CoinNewsService.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinNewsService/CoinNewsService.swift deleted file mode 100644 index 1cfbcf5..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinNewsService/CoinNewsService.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// CoinNewsService.swift -// SampleAppSwiftUI -// -// Created by Meryem Şahin on 20.07.2023. -// - -import Foundation - -protocol CoinNewsServiceProtocol { - func coinNewsRequest(coinCode: String) async throws -> CoinNewsResponse -} - -final class CoinNewsService: CoinNewsServiceProtocol, BaseServiceProtocol { - typealias Endpoint = CoinNewsServiceEndpoint - - var networkLoader: NetworkLoaderProtocol - - init(networkLoader: NetworkLoaderProtocol = NetworkLoaderProvider.shared.networkLoader) { - self.networkLoader = networkLoader - } - - func coinNewsRequest(coinCode: String) async throws -> CoinNewsResponse { - let urlString = build(endpoint: .coinNews(coinCode: coinCode)) - return try await request(with: RequestObject(url: urlString), responseModel: CoinNewsResponse.self) - } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinPriceHistoryService/CoinPriceHistoryService.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinPriceHistoryService/CoinPriceHistoryService.swift deleted file mode 100644 index 90e83d6..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinPriceHistoryService/CoinPriceHistoryService.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// CoinPriceHistoryService.swift -// SampleAppSwiftUI -// -// Created by Abay, Batuhan on 29.05.2023. -// - -import Foundation - -protocol CoinPriceHistoryServiceProtocol { - func dailyPriceHistoryRequest(coinCode: String, unitToBeConverted: String, dayLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryResponse - func hourlyPriceHistoryRequest(coinCode: String, unitToBeConverted: String, hourLimit: Int, aggregate: Int) async throws -> CoinPriceHistoryResponse -} - -final class CoinPriceHistoryService: CoinPriceHistoryServiceProtocol, BaseServiceProtocol { - typealias Endpoint = CoinPriceHistoryServiceEndpoint - - let networkLoader: NetworkLoaderProtocol - - init(networkLoader: NetworkLoaderProtocol = NetworkLoaderProvider.shared.networkLoader) { - self.networkLoader = networkLoader - } - - func dailyPriceHistoryRequest(coinCode: String, unitToBeConverted: String = "USD", dayLimit: Int = 30, aggregate: Int = 1) async throws -> CoinPriceHistoryResponse { - let urlString = build(endpoint: .daily(coinCode: coinCode, unitToBeConverted: unitToBeConverted, dayLimit: dayLimit, aggregate: aggregate)) - return try await request(with: RequestObject(url: urlString), responseModel: CoinPriceHistoryResponse.self) - } - - func hourlyPriceHistoryRequest(coinCode: String, unitToBeConverted: String = "USD", hourLimit: Int = 30, aggregate: Int = 1) async throws -> CoinPriceHistoryResponse { - let urlString = build(endpoint: .hourly(coinCode: coinCode, unitToBeConverted: unitToBeConverted, hourLimit: hourLimit, aggregate: aggregate)) - return try await request(with: RequestObject(url: urlString), responseModel: CoinPriceHistoryResponse.self) - } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinPriceHistoryService/CoinPriceHistoryServiceEndpoint.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinPriceHistoryService/CoinPriceHistoryServiceEndpoint.swift deleted file mode 100644 index 44081e3..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/CoinPriceHistoryService/CoinPriceHistoryServiceEndpoint.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// CoinPriceHistoryServiceEndpoint.swift -// SampleAppSwiftUI -// -// Created by Abay, Batuhan on 29.05.2023. -// - -import Foundation - -enum CoinPriceHistoryServiceEndpoint: TargetEndpointProtocol { - case daily(coinCode: String, unitToBeConverted: String = "USD", dayLimit: Int = 30, aggregate: Int = 1) - case hourly(coinCode: String, unitToBeConverted: String = "USD", hourLimit: Int = 30, aggregate: Int = 1) - - private struct Constants { - static let dailyEndpoint = "v2/histoday?fsym=%@&tsym=%@&limit=%d&aggregate=%d&api_key=%@" - static let hourlyEndpoint = "v2/histohour?fsym=%@&tsym=%@&limit=%d&aggregate=%d&api_key=%@" - } - - var path: String { - switch self { - case .daily(let coinCode, let unitToBeConverted, let dayLimit, let aggregate): - return BaseEndpoint.base.path + String(format: Constants.dailyEndpoint, coinCode, unitToBeConverted, dayLimit, aggregate, Configuration.coinApiKey) - case .hourly(let coinCode, let unitToBeConverted, let hourLimit, let aggregate): - return BaseEndpoint.base.path + String(format: Constants.hourlyEndpoint, coinCode, unitToBeConverted, hourLimit, aggregate, Configuration.coinApiKey) - } - } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/ExampleService/ExampleService.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/ExampleService/ExampleService.swift deleted file mode 100644 index c5d1581..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Network/WebServices/ExampleService/ExampleService.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// ExampleService.swift -// SampleAppSwiftUI -// -// Created by Saglam, Fatih on 16.01.2023. -// Copyright © 2023 Adesso Turkey. All rights reserved. -// - -import Foundation - -protocol ExampleServiceProtocol { - func exampleRequest() async throws -> ExampleResponse -} - -final class ExampleService: ExampleServiceProtocol, BaseServiceProtocol { - - typealias Endpoint = ExampleServiceEndpoint - - let networkLoader: NetworkLoaderProtocol - - init(networkLoader: NetworkLoaderProtocol = NetworkLoaderProvider.shared.networkLoader) { - self.networkLoader = networkLoader - } - - func exampleRequest() async throws -> ExampleResponse { - try await request(with: RequestObject(url: build(endpoint: .example(firstParameter: "firstParameter", - secondParameter: "secondParameter"))), - responseModel: ExampleResponse.self) - } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Favorites/FavoritesView.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Favorites/FavoritesView.swift index 1d55f1f..60a3238 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Favorites/FavoritesView.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Favorites/FavoritesView.swift @@ -22,7 +22,7 @@ struct FavoritesView: View { CoinListView(viewModel: viewModel, filteredCoins: $viewModel.filteredCoins, favoriteChanged: viewModel.fetchFavorites) } .navigationDestination(for: Screen.self) { screen in - if screen.type == .detail, let data = screen.data as? CoinData { + if screen.type == .detail, let data = screen.data as? CoinUIModel { CoinDetailView(coinData: data) } } @@ -46,7 +46,7 @@ struct FavoritesView: View { }) } - private func fetchFavorites(codes: [CoinData]) { + private func fetchFavorites(codes: [CoinUIModel]) { viewModel.fetchFavorites() } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Favorites/FavoritesViewModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Favorites/FavoritesViewModel.swift index a6f5b62..2a2edaa 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Favorites/FavoritesViewModel.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Favorites/FavoritesViewModel.swift @@ -8,6 +8,7 @@ import Foundation import SwiftUI import Combine +import NetworkService class FavoritesViewModel: ObservableObject { private let checkWebSocket = true @@ -17,9 +18,9 @@ class FavoritesViewModel: ObservableObject { private var reconnectionCount: Int = 0 private var maxReconnectionCount: Int = 3 - @Published var coinList: [CoinData] = [] - @Published var filteredCoins: [CoinData] = [] - @Published var coinInfo: CoinData? + @Published var coinList: [CoinUIModel] = [] + @Published var filteredCoins: [CoinUIModel] = [] + @Published var coinInfo: CoinUIModel? @Published var filterTitle = SortOptions.defaultList.rawValue @Published var selectedSortOption: SortOptions = .defaultList @@ -61,20 +62,21 @@ class FavoritesViewModel: ObservableObject { private func connect() { guard reconnectionCount < maxReconnectionCount, - let service = webSocketService.connect(endPoint: .baseCoinApi) else { + let service = webSocketService.connect(endPoint: .baseCoinApi, loggerManager: LoggerManager.shared) else { NSLog("Service connection error.") return } service.setPing(time: 10) service.connectionHandler {[weak self] webservice in guard let self else { return } - var subs: [CoinCode] = [] + var subs: [String] = [] for coin in self.filteredCoins { if let coinInfo = coin.coinInfo, let code = coinInfo.code { subs.append(code) } } + let request = FavoritesCoinRequest(action: .add, codeList: subs) webservice.sendMessage(request) } disconnected: { [weak self] closeCode in diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinListView.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinListView.swift index caadf6f..622b53c 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinListView.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinListView.swift @@ -9,7 +9,7 @@ import SwiftUI struct CoinListView: View { @ObservedObject var viewModel: ViewModel - @Binding var filteredCoins: [CoinData] + @Binding var filteredCoins: [CoinUIModel] @State private var showingAlert = false @State private var alertTitle = "" @EnvironmentObject var router: Router @@ -59,11 +59,11 @@ struct CoinListView: View { } } - private func navigateCoinDetail(coinData: CoinData) { + private func navigateCoinDetail(coinData: CoinUIModel) { router.navigateCoinDetail(coinData: coinData) } - func checkFavorite(coinData: CoinData) { + func checkFavorite(coinData: CoinUIModel) { self.alertTitle = StorageManager.shared.manageFavorites(coinData: coinData) showingAlert.toggle() favoriteChanged() diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinNews/CoinNewsListView.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinNews/CoinNewsListView.swift index d83a459..1e72f9c 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinNews/CoinNewsListView.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinNews/CoinNewsListView.swift @@ -10,7 +10,7 @@ import SwiftUI struct CoinNewsListView: View { @StateObject private var viewModel: CoinDetailViewModel - init(coinData: CoinData) { + init(coinData: CoinUIModel) { _viewModel = StateObject( wrappedValue: CoinDetailViewModel(coinData: coinData) ) @@ -55,6 +55,6 @@ struct CoinNewsListView: View { struct CoinNewsListView_Previews: PreviewProvider { static var previews: some View { - CoinNewsListView(coinData: CoinData.demo) + CoinNewsListView(coinData: CoinUIModel.demo) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinView.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinView.swift index 77bef41..52704e0 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinView.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/CoinView.swift @@ -8,7 +8,7 @@ import SwiftUI struct CoinView: View { - var coinInfo: CoinData + var coinInfo: CoinUIModel var body: some View { ZStack { @@ -72,7 +72,7 @@ struct CoinView: View { } } - func configureTextColor(_ rawData: RawUsd) -> Color { + func configureTextColor(_ rawData: RawUsdUIModel) -> Color { rawData.changePercentage ?? Numbers.absoluteZero < Numbers.absoluteZero ? .red : .green } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/ChangePercentageView.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/ChangePercentageView.swift index 731c61c..9a77ef9 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/ChangePercentageView.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/ChangePercentageView.swift @@ -8,7 +8,7 @@ import SwiftUI struct ChangePercentageView: View { - let changeRate: RawUsd + let changeRate: RawUsdUIModel private var isRised: Bool { changeRate.changePercentage ?? 0 >= 0 @@ -28,5 +28,5 @@ struct ChangePercentageView: View { } #Preview { - ChangePercentageView(changeRate: CoinData.demo.detail?.usd ?? .init()) + ChangePercentageView(changeRate: CoinUIModel.demo.detail?.usd ?? .init()) } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinDetailView.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinDetailView.swift index c8edfed..e25bce4 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinDetailView.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinDetailView.swift @@ -17,7 +17,7 @@ struct CoinDetailView: View { @StateObject private var viewModel: CoinDetailViewModel - init(coinData: CoinData) { + init(coinData: CoinUIModel) { _viewModel = StateObject( wrappedValue: CoinDetailViewModel( coinData: coinData @@ -170,7 +170,7 @@ struct CoinDetailView: View { #Preview { Group { NavigationView { - CoinDetailView(coinData: CoinData.demo) + CoinDetailView(coinData: CoinUIModel.demo) .previewLayout(.sizeThatFits) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinDetailViewModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinDetailViewModel.swift index 88c5435..27eb5a8 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinDetailViewModel.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinDetailViewModel.swift @@ -13,19 +13,19 @@ class CoinDetailViewModel: ObservableObject { @Published var chartHistoryRangeSelection: CoinChartHistoryRange = .sixMonth private(set) var coinPriceHistoryChartDataModel: CoinPriceHistoryChartDataModel? @Published var isLoading = false - @Published var coinNewsDataModel: [CoinNewData]? + @Published var coinNewsDataModel: [CoinNewDataUIModel]? @Published var priceChartSelectedXDateText = "" - let coinData: CoinData + let coinData: CoinUIModel init( isFavorite: Bool = false, chartHistoryRangeSelection: CoinChartHistoryRange = .sixMonth, coinPriceHistoryChartDataModel: CoinPriceHistoryChartDataModel? = nil, isLoading: Bool = false, - coinNewsDataModel: [CoinNewData]? = nil, + coinNewsDataModel: [CoinNewDataUIModel]? = nil, priceChartSelectedXDateText: String = "", - coinData: CoinData + coinData: CoinUIModel ) { self.isFavorite = isFavorite self.chartHistoryRangeSelection = chartHistoryRangeSelection @@ -82,7 +82,7 @@ class CoinDetailViewModel: ObservableObject { isLoading = true } do { - var response: CoinPriceHistoryResponse? + var response: CoinPriceHistoryUIModel? let limitAndAggregate = range.limitAndAggregateValue switch range { case .oneDay: @@ -111,7 +111,7 @@ class CoinDetailViewModel: ObservableObject { } } } catch let error { - LoggerManager().setError(errorMessage: error.localizedDescription) + LoggerManager.shared.setError(errorMessage: error.localizedDescription) } } @@ -125,7 +125,7 @@ class CoinDetailViewModel: ObservableObject { } } } catch let error { - LoggerManager().setError(errorMessage: error.localizedDescription) + LoggerManager.shared.setError(errorMessage: error.localizedDescription) } } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinPriceInfoChartDataModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinPriceInfoChartDataModel.swift index e811930..0e8c1de 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinPriceInfoChartDataModel.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/Coin/Detail/CoinPriceInfoChartDataModel.swift @@ -14,7 +14,7 @@ struct CoinPriceHistoryChartDataModel { let prices: [CoinPriceInfoChartDataModel] let lineColor: Color - init(from coinPriceHistoryData: CoinPriceHistoryData) { + init(from coinPriceHistoryData: CoinPriceHistoryDataUIModel) { self.aggregated = coinPriceHistoryData.aggregated self.timeFrom = coinPriceHistoryData.timeFrom self.timeTo = coinPriceHistoryData.timeTo @@ -27,31 +27,6 @@ struct CoinPriceHistoryChartDataModel { self.lineColor = .blue } } - - static let demo = CoinPriceHistoryChartDataModel( - from: CoinPriceHistoryData( - aggregated: false, - timeFrom: 1684972800, - timeTo: 1685404800, - data: [ - CoinPriceInfo(time: 1684972800, high: 3.592, low: 3.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685059200, high: 4.592, low: 3.544, open: 4.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685145600, high: 5.592, low: 2.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685613085, high: 6.592, low: 3.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685633085, high: 7.592, low: 3.544, open: 4.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685653085, high: 8.592, low: 2.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685673085, high: 9.592, low: 3.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685693085, high: 17.592, low: 0.544, open: 4.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685713085, high: 2.592, low: 2.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685733085, high: 6.592, low: 3.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685753085, high: 3.592, low: 3.544, open: 4.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685773085, high: 2.592, low: 2.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685793085, high: 7.592, low: 3.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685813085, high: 11.592, low: 3.544, open: 4.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), - CoinPriceInfo(time: 1685833085, high: 15.592, low: 2.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563) - ] - ) - ) } struct CoinPriceInfoChartDataModel: Identifiable, Hashable { @@ -66,7 +41,7 @@ struct CoinPriceInfoChartDataModel: Identifiable, Hashable { let volumeTo: Float? let close: Float? - init?(from coinPriceInfo: CoinPriceInfo) { + init?(from coinPriceInfo: CoinPriceInfoUIModel) { guard let time = coinPriceInfo.time, let high = coinPriceInfo.high, let low = coinPriceInfo.low else { return nil } @@ -87,3 +62,33 @@ struct CoinPriceInfoChartDataModel: Identifiable, Hashable { lhs.timeStamp == rhs.timeStamp } } + +// MARK: - DEMO +extension CoinPriceHistoryChartDataModel { + // swiftlint:disable line_length + static let demo = CoinPriceHistoryChartDataModel( + from: CoinPriceHistoryDataUIModel( + aggregated: false, + timeFrom: 1684972800, + timeTo: 1685404800, + data: [ + CoinPriceInfoUIModel(time: 1684972800, high: 3.592, low: 3.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685059200, high: 4.592, low: 3.544, open: 4.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685145600, high: 5.592, low: 2.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685613085, high: 6.592, low: 3.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685633085, high: 7.592, low: 3.544, open: 4.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685653085, high: 8.592, low: 2.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685673085, high: 9.592, low: 3.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685693085, high: 17.592, low: 0.544, open: 4.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685713085, high: 2.592, low: 2.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685733085, high: 6.592, low: 3.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685753085, high: 3.592, low: 3.544, open: 4.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685773085, high: 2.592, low: 2.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685793085, high: 7.592, low: 3.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685813085, high: 11.592, low: 3.544, open: 4.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563), + CoinPriceInfoUIModel(time: 1685833085, high: 15.592, low: 2.544, open: 3.562, volumeFrom: 15886.3, volumeTo: 56872.31, close: 3.563) + ] + ) + ) + // swiftlint:enable line_length +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/HomeView.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/HomeView.swift index 4829f47..a00e85e 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/HomeView.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/HomeView.swift @@ -30,7 +30,7 @@ struct HomeView: View { .navigationTitle("Home") .navigationBarTitleDisplayMode(.inline) .navigationDestination(for: Screen.self) { screen in - if screen.type == .detail, let data = screen.data as? CoinData { + if screen.type == .detail, let data = screen.data as? CoinUIModel { CoinDetailView(coinData: data) } } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/HomeViewModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/HomeViewModel.swift index e8bbdb6..d523bbb 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/HomeViewModel.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Home/HomeViewModel.swift @@ -8,16 +8,18 @@ import Foundation import SwiftUI import Combine +import NetworkService class HomeViewModel: ObservableObject { @Published var coinInfo: ExcangeRatesResponseModel? - @Published var coinList: [CoinData] = [] - @Published var filteredCoins: [CoinData] = [] + @Published var coinList: [CoinUIModel] = [] + @Published var filteredCoins: [CoinUIModel] = [] @Published var filterTitle = SortOptions.defaultList.rawValue let listPageLimit = 10 @Published var isLoading: Bool = false @Published var selectedSortOption: SortOptions = .defaultList + private var allCoinsUseCase: AllCoinUseCaseProtocol = AllCoinUseCase() func fillModels(demo: Bool = false) async { if demo { @@ -27,10 +29,11 @@ class HomeViewModel: ObservableObject { } private func fetchAllCoins(page: Int = 1) async { - guard let dataSource = try? await AllCoinRemoteDataSource().getAllCoin(limit: self.listPageLimit, unitToBeConverted: "USD", page: page) else { + guard let dataSource = try? await allCoinsUseCase.fetchAllCoin(limit: self.listPageLimit, unitToBeConverted: "USD", page: page) else { Logger().error("Problem on the convert") return } + DispatchQueue.main.async { if let data = dataSource.data { self.coinList.append(contentsOf: data) @@ -61,7 +64,7 @@ class HomeViewModel: ObservableObject { extension HomeViewModel: ViewModelProtocol { - func checkLastItem(_ item: CoinData) { + func checkLastItem(_ item: CoinUIModel) { guard !isLoading else { return } let offset = 2 @@ -85,11 +88,11 @@ extension HomeViewModel: ViewModelProtocol { // MARK: Demo Mode extension HomeViewModel { private func fetchDemoModel() { - guard let coinList = JsonHelper.make([CoinData].self, .coinList) else { return } + guard let coinList = JsonHelper.make([CoinUIModel].self, .coinList) else { return } self.fillDemoData(coinList: coinList) } - private func fillDemoData(coinList: [CoinData]) { + private func fillDemoData(coinList: [CoinUIModel]) { self.coinList = coinList self.filteredCoins = coinList } diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Router.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Router.swift index d20d980..3b9543e 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Router.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/Router.swift @@ -15,7 +15,7 @@ final public class Router: ObservableObject { @Published var selectedTab: TabIndex = .home var tabbarNames: [TabIndex] = [.home, .favorites, .settings] - func navigateCoinDetail(coinData: CoinData) { + func navigateCoinDetail(coinData: CoinUIModel) { if selectedTab == .home { homeNavigationPath.append(Screen(type: .detail, data: coinData)) } else if selectedTab == .favorites { diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/ViewModelProtocol.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/ViewModelProtocol.swift index 2e2a090..fb49b76 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/ViewModelProtocol.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Scenes/ViewModelProtocol.swift @@ -9,15 +9,15 @@ import Foundation protocol ViewModelProtocol: ObservableObject { var isLoading: Bool { get set } - var filteredCoins: [CoinData] { get set } - var coinList: [CoinData] { get } + var filteredCoins: [CoinUIModel] { get set } + var coinList: [CoinUIModel] { get } var selectedSortOption: SortOptions { get set } - func checkLastItem(_ item: CoinData) + func checkLastItem(_ item: CoinUIModel) } extension ViewModelProtocol { - func checkLastItem(_ item: CoinData) {} + func checkLastItem(_ item: CoinUIModel) {} func sortOptions(sort: SortOptions) { selectedSortOption = sort diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/AllCoinUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/AllCoinUIModel.swift new file mode 100644 index 0000000..901d94f --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/AllCoinUIModel.swift @@ -0,0 +1,32 @@ +// +// AllCoinUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct AllCoinUIModel: Hashable, Identifiable { + var id: String + let data: [CoinUIModel]? + + init(id: String, data: [CoinUIModel]?) { + self.id = id + self.data = data + } +} + +// MARK: - UIModelProtocol +extension AllCoinUIModel: UIModelProtocol { + init(from responseModel: AllCoinResponse) { + self.id = responseModel.id + self.data = responseModel.data?.compactMap({ CoinUIModel(from: $0) }) + } + + init?(from responseModel: AllCoinResponse?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinMarketCapsCoinInfoUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinMarketCapsCoinInfoUIModel.swift new file mode 100644 index 0000000..ae50b0e --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinMarketCapsCoinInfoUIModel.swift @@ -0,0 +1,36 @@ +// +// CoinMarketCapsCoinInfoUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct CoinMarketCapsCoinInfoUIModel: Codable, Hashable { + let code: String? + var title: String? + + init(code: String?, title: String? = nil) { + self.code = code + self.title = title + } + + static func == (lhs: CoinMarketCapsCoinInfoUIModel, rhs: CoinMarketCapsCoinInfoUIModel) -> Bool { + lhs.code == rhs.code + } +} + +// MARK: - UIModelProtocol +extension CoinMarketCapsCoinInfoUIModel: UIModelProtocol { + init(from responseModel: CoinMarketCapsCoinInfo) { + self.code = responseModel.code + self.title = responseModel.title + } + + init?(from responseModel: CoinMarketCapsCoinInfo?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinNewDataUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinNewDataUIModel.swift new file mode 100644 index 0000000..d366096 --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinNewDataUIModel.swift @@ -0,0 +1,44 @@ +// +// CoinNewDataUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct CoinNewDataUIModel: Hashable, Identifiable { + let id: String + let imageurl: String + let title: String + let url: String + let body: String + let source: String + + init(id: String, imageurl: String, title: String, url: String, body: String, source: String) { + self.id = id + self.imageurl = imageurl + self.title = title + self.url = url + self.body = body + self.source = source + } +} + +// MARK: - UIModelProtocol +extension CoinNewDataUIModel: UIModelProtocol { + init(from responseModel: CoinNewData) { + self.id = responseModel.id + self.imageurl = responseModel.imageurl + self.title = responseModel.title + self.url = responseModel.url + self.body = responseModel.body + self.source = responseModel.source + } + + init?(from responseModel: CoinNewData?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinNewsUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinNewsUIModel.swift new file mode 100644 index 0000000..4763f42 --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinNewsUIModel.swift @@ -0,0 +1,41 @@ +// +// CoinNewsUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct CoinNewsUIModel: Hashable, Identifiable { + var id: UUID + let type: Int + let message: String + let data: [CoinNewDataUIModel]? + let hasWarning: Bool + + init(id: UUID, type: Int, message: String, data: [CoinNewDataUIModel]?, hasWarning: Bool) { + self.id = id + self.type = type + self.message = message + self.data = data + self.hasWarning = hasWarning + } +} + +// MARK: - UIModelProtocol +extension CoinNewsUIModel: UIModelProtocol { + init(from responseModel: CoinNewsResponse) { + self.id = responseModel.id + self.type = responseModel.type + self.message = responseModel.message + self.data = responseModel.data?.compactMap({ CoinNewDataUIModel(from: $0) }) + self.hasWarning = responseModel.hasWarning + } + + init?(from responseModel: CoinNewsResponse?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinPriceHistoryDataUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinPriceHistoryDataUIModel.swift new file mode 100644 index 0000000..166ece9 --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinPriceHistoryDataUIModel.swift @@ -0,0 +1,38 @@ +// +// CoinPriceHistoryDataUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct CoinPriceHistoryDataUIModel { + let aggregated: Bool? + let timeFrom: TimeInterval? + let timeTo: TimeInterval? + let data: [CoinPriceInfoUIModel]? + + init(aggregated: Bool?, timeFrom: TimeInterval?, timeTo: TimeInterval?, data: [CoinPriceInfoUIModel]?) { + self.aggregated = aggregated + self.timeFrom = timeFrom + self.timeTo = timeTo + self.data = data + } +} + +// MARK: - UIModelProtocol +extension CoinPriceHistoryDataUIModel: UIModelProtocol { + init(from responseModel: CoinPriceHistoryData) { + self.aggregated = responseModel.aggregated + self.timeFrom = responseModel.timeFrom + self.timeTo = responseModel.timeTo + self.data = responseModel.data?.compactMap({ CoinPriceInfoUIModel(from: $0) }) + } + + init?(from responseModel: CoinPriceHistoryData?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinPriceHistoryUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinPriceHistoryUIModel.swift new file mode 100644 index 0000000..3630cd6 --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinPriceHistoryUIModel.swift @@ -0,0 +1,29 @@ +// +// CoinPriceHistoryUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct CoinPriceHistoryUIModel { + let data: CoinPriceHistoryDataUIModel? + + init(data: CoinPriceHistoryDataUIModel?) { + self.data = data + } +} + +// MARK: - UIModelProtocol +extension CoinPriceHistoryUIModel: UIModelProtocol { + init(from responseModel: CoinPriceHistoryResponse) { + self.data = CoinPriceHistoryDataUIModel(from: responseModel.data) + } + + init?(from responseModel: CoinPriceHistoryResponse?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinPriceInfoUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinPriceInfoUIModel.swift new file mode 100644 index 0000000..efada16 --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinPriceInfoUIModel.swift @@ -0,0 +1,55 @@ +// +// CoinPriceInfoUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct CoinPriceInfoUIModel { + let time: TimeInterval? + let high: Float? + let low: Float? + let open: Float? + let volumeFrom: Float? + let volumeTo: Float? + let close: Float? + + init( + time: TimeInterval?, + high: Float?, + low: Float?, + open: Float?, + volumeFrom: Float?, + volumeTo: Float?, + close: Float? + ) { + self.time = time + self.high = high + self.low = low + self.open = open + self.volumeFrom = volumeFrom + self.volumeTo = volumeTo + self.close = close + } +} + +// MARK: - UIModelProtocol +extension CoinPriceInfoUIModel: UIModelProtocol { + init(from responseModel: CoinPriceInfo) { + self.time = responseModel.time + self.high = responseModel.high + self.low = responseModel.low + self.open = responseModel.open + self.volumeFrom = responseModel.volumeFrom + self.volumeTo = responseModel.volumeTo + self.close = responseModel.close + } + + init?(from responseModel: CoinPriceInfo?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinRawUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinRawUIModel.swift new file mode 100644 index 0000000..cd73342 --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinRawUIModel.swift @@ -0,0 +1,33 @@ +// +// CoinRawUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct CoinRawUIModel: Codable, Hashable { + var usd: RawUsdUIModel? + + init(usd: RawUsdUIModel? = nil) { + self.usd = usd + } + + static func == (lhs: CoinRawUIModel, rhs: CoinRawUIModel) -> Bool { + lhs.usd == rhs.usd + } +} + +// MARK: - UIModelProtocol +extension CoinRawUIModel: UIModelProtocol { + init(from responseModel: CoinRaw) { + self.usd = RawUsdUIModel(from: responseModel.usd) + } + + init?(from responseModel: CoinRaw?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinUIModel.swift new file mode 100644 index 0000000..5c38087 --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/CoinUIModel.swift @@ -0,0 +1,49 @@ +// +// CoinUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct CoinUIModel: Codable, Hashable, Identifiable { + var id: String + var coinInfo: CoinMarketCapsCoinInfoUIModel? + var detail: CoinRawUIModel? + + init(id: String, coinInfo: CoinMarketCapsCoinInfoUIModel? = nil, detail: CoinRawUIModel? = nil) { + self.id = id + self.coinInfo = coinInfo + self.detail = detail + } + + static func == (lhs: CoinUIModel, rhs: CoinUIModel) -> Bool { + lhs.coinInfo == rhs.coinInfo && + lhs.detail == rhs.detail + } +} + +// MARK: - UIModelProtocol +extension CoinUIModel: UIModelProtocol { + init(from responseModel: CoinData) { + self.id = responseModel.id + self.coinInfo = CoinMarketCapsCoinInfoUIModel(from: responseModel.coinInfo) + self.detail = CoinRawUIModel(from: responseModel.detail) + } + + init?(from responseModel: CoinData?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} + +// MARK: - DEMO +extension CoinUIModel { + static let demo = CoinUIModel(id: UUID().uuidString, + coinInfo: CoinMarketCapsCoinInfoUIModel(code: "BTC", title: "Demo"), + detail: CoinRawUIModel(usd: RawUsdUIModel(price: 29467.560, + changeAmount: 28.015, + changePercentage: 29.74))) +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/ExampleUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/ExampleUIModel.swift new file mode 100644 index 0000000..e8514a9 --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/ExampleUIModel.swift @@ -0,0 +1,29 @@ +// +// ExampleUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct ExampleUIModel { + let value: String? + + init(value: String?) { + self.value = value + } +} + +// MARK: - UIModelProtocol +extension ExampleUIModel: UIModelProtocol { + init(from responseModel: ExampleResponse) { + self.value = responseModel.value + } + + init?(from responseModel: ExampleResponse?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/Protocols/UIModelProtocol.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/Protocols/UIModelProtocol.swift new file mode 100644 index 0000000..d2bb9e2 --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/Protocols/UIModelProtocol.swift @@ -0,0 +1,15 @@ +// +// UIModelProtocol.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation + +protocol UIModelProtocol { + associatedtype ResponseModel + + init(from responseModel: ResponseModel) + init?(from responseModel: ResponseModel?) +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/RawUsdUIModel.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/RawUsdUIModel.swift new file mode 100644 index 0000000..621816a --- /dev/null +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/UIModels/RawUsdUIModel.swift @@ -0,0 +1,63 @@ +// +// RawUsdUIModel.swift +// SampleAppSwiftUI +// +// Created by Abay, Batuhan on 19.12.2023. +// + +import Foundation +import NetworkService + +struct RawUsdUIModel: Codable, Hashable { + var price: Double? + var changeAmount: Double? + var changePercentage: Double? + + init(price: Double? = nil, changeAmount: Double? = nil, changePercentage: Double? = nil) { + self.price = price + self.changeAmount = changeAmount + self.changePercentage = changePercentage + } + + static func == (lhs: RawUsdUIModel, rhs: RawUsdUIModel) -> Bool { + lhs.price == rhs.price && + lhs.changeAmount == rhs.changeAmount && + lhs.changePercentage == rhs.changePercentage + } +} + +// MARK: - UIModelProtocol +extension RawUsdUIModel: UIModelProtocol { + init(from responseModel: RawUsd) { + self.price = responseModel.price + self.changeAmount = responseModel.changeAmount + self.changePercentage = responseModel.changePercentage + } + + init?(from responseModel: RawUsd?) { + guard let responseModel else { return nil } + self.init(from: responseModel) + } +} + +// MARK: - Utilities +extension RawUsdUIModel { + func createPriceString() -> String { + self.price?.formatted(.currency(code: "USD").precision(.fractionLength(Range.currency))) ?? "" + } + + func createChangeText() -> String { + "\(createPercentageText()) (\(createAmountText()))" + } + + func createPercentageText() -> String { + ((self.changePercentage ?? 0) / 100) + .formatted(.percent.precision(.fractionLength(Range.currency))) + } + + func createAmountText() -> String { + self.changeAmount? + .formatted(.currency(code: "USD") + .precision(.fractionLength(Range.currency))) ?? "" + } +} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Utility/Extensions/RawUsdExtensions.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Utility/Extensions/RawUsdExtensions.swift deleted file mode 100644 index 9b87a50..0000000 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Utility/Extensions/RawUsdExtensions.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// RawUsd+Extension.swift -// SampleAppSwiftUI -// -// Created by Abay, Batuhan on 31.05.2023. -// - -import Foundation - -extension RawUsd { - func createPriceString() -> String { - self.price?.formatted(.currency(code: "USD").precision(.fractionLength(Range.currency))) ?? "" - } - - func createChangeText() -> String { - "\(createPercentageText()) (\(createAmountText()))" - } - - func createPercentageText() -> String { - ((self.changePercentage ?? 0) / 100) - .formatted(.percent.precision(.fractionLength(Range.currency))) - } - - func createAmountText() -> String { - self.changeAmount? - .formatted(.currency(code: "USD") - .precision(.fractionLength(Range.currency))) ?? "" - } -} diff --git a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Utility/Managers/StorageManager.swift b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Utility/Managers/StorageManager.swift index 06ca8d0..355ee13 100644 --- a/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Utility/Managers/StorageManager.swift +++ b/Projects/SampleAppSwiftUI/SampleAppSwiftUI/Utility/Managers/StorageManager.swift @@ -11,11 +11,11 @@ final class StorageManager { static let shared = StorageManager() - @AppStorage("favoriteCoins") var favoriteCoins: [CoinData] = [] + @AppStorage("favoriteCoins") var favoriteCoins: [CoinUIModel] = [] private init() { } - func isCoinFavorite(_ coinCode: CoinCode) -> Bool { + func isCoinFavorite(_ coinCode: String) -> Bool { favoriteCoins.contains { coinData in if let coinInfo = coinData.coinInfo, let code = coinInfo.code { return code == coinCode @@ -24,7 +24,7 @@ final class StorageManager { } } - func toggleFavoriteCoin(coinData: CoinData) { + func toggleFavoriteCoin(coinData: CoinUIModel) { if favoriteCoins.isEmpty { addFavoriteCoin(coinData: coinData) } else { @@ -36,13 +36,13 @@ final class StorageManager { } } - private func addFavoriteCoin(coinData: CoinData) { + private func addFavoriteCoin(coinData: CoinUIModel) { DispatchQueue.main.async { self.favoriteCoins.append(coinData) } } - private func removeFavoriteCoin(_ coinCode: CoinCode) { + private func removeFavoriteCoin(_ coinCode: String) { favoriteCoins.removeAll { coinData in if let coinInfo = coinData.coinInfo, let code = coinInfo.code { return code == coinCode @@ -52,7 +52,7 @@ final class StorageManager { } @discardableResult - func manageFavorites(coinData: CoinData) -> String { + func manageFavorites(coinData: CoinUIModel) -> String { let output = isCoinFavorite(coinData.coinInfo?.code ?? "") ? "Removed from Favorites" : "Added to Favorites" toggleFavoriteCoin(coinData: coinData) return output diff --git a/Tuist/Dependencies.swift b/Tuist/Dependencies.swift deleted file mode 100644 index 4947e27..0000000 --- a/Tuist/Dependencies.swift +++ /dev/null @@ -1,13 +0,0 @@ -// swift-tools-version: 5.9 - -import ProjectDescription -import ProjectDescriptionHelpers - -let dependencies = Dependencies( - swiftPackageManager: .init( - projectOptions: [ - "LocalSwiftPackage": .options(disableSynthesizedResourceAccessors: false), - ] - ), - platforms: [.iOS] -) diff --git a/Tuist/Package.swift b/Tuist/Package.swift deleted file mode 100644 index 6ee6010..0000000 --- a/Tuist/Package.swift +++ /dev/null @@ -1,8 +0,0 @@ -// swift-tools-version: 5.9 - -import PackageDescription - -let package = Package( - name: "PackageName", - dependencies: [] -)