From fe34747c7c32720461ac5c3c0120719be3721c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Fern=C3=A1ndez?= Date: Fri, 18 Jun 2021 15:42:09 +0200 Subject: [PATCH 1/3] feat: add async alternatives --- Package.swift | 2 +- README.md | 4 ++ Sources/REESwift/Network.swift | 11 ++++ Sources/REESwift/REESwift.swift | 32 +++++++++++ Tests/REESwiftTests/REESwiftTests.swift | 70 +++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index f4fb341..8a730fb 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/README.md b/README.md index 4dd61c3..92fe1e4 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ func consumerPrices(startDate: Date, endDate: Date, geo: GEO, completion: @escap func consumerPrices(date: Date, geo: GEO, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) func consumerPrices(startDate: Date, endDate: Date, geo: GEO) -> AnyPublisher<[PrecioLuzValue], Error> func consumerPrices(date: Date, geo: GEO) -> AnyPublisher<[PrecioLuzValue], Error> +func consumerPrices(startDate: Date, endDate: Date, geo: GEO) async throws -> [PrecioLuzValue] +func consumerPrices(date: Date, geo: GEO) async throws -> [PrecioLuzValue] ``` ### Obtener precios mercado spot @@ -42,6 +44,8 @@ func spotPrices(startDate: Date, endDate: Date, geo: GEO, completion: @escaping func spotPrices(date: Date, geo: GEO, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) func spotPrices(startDate: Date, endDate: Date, geo: GEO) -> AnyPublisher<[PrecioLuzValue], Error> func spotPrices(date: Date, geo: GEO) -> AnyPublisher<[PrecioLuzValue], Error> +func spotPrices(startDate: Date, endDate: Date) async throws -> [PrecioLuzValue] +func spotPrices(date: Date) async throws -> [PrecioLuzValue] ``` ## Licencia de uso y contribuciĆ³n con el proyecto diff --git a/Sources/REESwift/Network.swift b/Sources/REESwift/Network.swift index 31c5305..4a6287b 100644 --- a/Sources/REESwift/Network.swift +++ b/Sources/REESwift/Network.swift @@ -17,6 +17,17 @@ class Network { } } + @available(iOS 15.0, *) + @available(tvOS 15.0, *) + @available(watchOS 8.0, *) + @available(macOS 12, *) + func request(_ endpoint: Endpoint) async throws -> T { + let (data, response) = try await URLSession.shared.data(from: endpoint.url) + guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw URLError(.badServerResponse) } + let decodedData = try decoder.decode(T.self, from: data) + return decodedData + } + func request(_ endpoint: Endpoint) -> AnyPublisher { URLSession.shared .dataTaskPublisher(for: endpoint.url) diff --git a/Sources/REESwift/REESwift.swift b/Sources/REESwift/REESwift.swift index b98d328..8077b64 100644 --- a/Sources/REESwift/REESwift.swift +++ b/Sources/REESwift/REESwift.swift @@ -67,3 +67,35 @@ public extension REESwift { } } + +@available(iOS 15.0, *) +@available(tvOS 15.0, *) +@available(watchOS 8.0, *) +@available(macOS 12, *) +public extension REESwift { + + func consumerPrices(startDate: Date, endDate: Date, geo: GEO) async throws -> [PrecioLuzValue] { + return try await prices(id: "1001", startDate: startDate, endDate: endDate, geo: geo) + } + + func consumerPrices(date: Date, geo: GEO) async throws -> [PrecioLuzValue] { + return try await prices(id: "1001", startDate: date.start, endDate: date.end, geo: geo) + } + + func spotPrices(startDate: Date, endDate: Date) async throws -> [PrecioLuzValue] { + return try await prices(id: "600", startDate: startDate, endDate: endDate, geo: nil) + } + + func spotPrices(date: Date) async throws -> [PrecioLuzValue] { + return try await prices(id: "600", startDate: date.start, endDate: date.end, geo: nil) + } + + private func prices(id: String, startDate: Date, endDate: Date, geo: GEO?) async throws -> [PrecioLuzValue] { + let endpoint = Endpoint.prices(startDate: startDate, endDate: endDate, geo: geo) + let apiResponse: APIResponse = try await Network.shared.request(endpoint) + let prices = apiResponse.included.first { $0.id == id } + guard let values = prices?.attributes?.values, !values.isEmpty else { throw URLError(.badServerResponse) } + return values + } + +} diff --git a/Tests/REESwiftTests/REESwiftTests.swift b/Tests/REESwiftTests/REESwiftTests.swift index 6517906..31b1e64 100644 --- a/Tests/REESwiftTests/REESwiftTests.swift +++ b/Tests/REESwiftTests/REESwiftTests.swift @@ -8,6 +8,53 @@ final class REESwiftTests: XCTestCase { // MARK: - Test consumer prices + @available(iOS 15.0, *) + @available(tvOS 15.0, *) + @available(watchOS 8.0, *) + @available(macOS 12, *) + func testConsumerPricesAsync() async { + + let now = Date.now + + let todayPrices = try? await ree.consumerPrices(date: now, geo: .peninsula) + XCTAssertNotNil(todayPrices) + XCTAssert(todayPrices?.count == 24) + + let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: now)! + let otherPrices = try? await ree.consumerPrices(startDate: yesterday.start, endDate: now.end, geo: .peninsula) + XCTAssertNotNil(otherPrices) + XCTAssert(otherPrices?.count == 48) + + let futureDate = Calendar.current.date(byAdding: .month, value: 1, to: now)! + let futurePrices = try? await ree.consumerPrices(date: futureDate, geo: .peninsula) + XCTAssertNil(futurePrices) + + } + + @available(iOS 15.0, *) + @available(tvOS 15.0, *) + @available(watchOS 8.0, *) + @available(macOS 12, *) + func testConsumerPricesAsyncOtherGEOs() async { + + let now = Date.now + + let peninsulaPrices = try? await ree.consumerPrices(date: now, geo: .peninsula) + let ceutaPrices = try? await ree.consumerPrices(date: now, geo: .ceuta) + XCTAssertNotNil(peninsulaPrices) + XCTAssertNotNil(ceutaPrices) + XCTAssert(peninsulaPrices?.count == ceutaPrices?.count) + var priceEquals = true + for i in 0.. Date: Fri, 18 Jun 2021 19:41:33 +0200 Subject: [PATCH 2/3] rename output --- README.md | 24 ++++++++--------- Sources/REESwift/Model/PrecioLuzValue.swift | 8 ------ Sources/REESwift/Model/Value.swift | 8 ++++++ Sources/REESwift/REESwift.swift | 30 ++++++++++----------- 4 files changed, 35 insertions(+), 35 deletions(-) delete mode 100644 Sources/REESwift/Model/PrecioLuzValue.swift create mode 100644 Sources/REESwift/Model/Value.swift diff --git a/README.md b/README.md index 92fe1e4..6b2b1ab 100644 --- a/README.md +++ b/README.md @@ -29,23 +29,23 @@ import REESwift ### Obtener precios consumidor ```swift -func consumerPrices(startDate: Date, endDate: Date, geo: GEO, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) -func consumerPrices(date: Date, geo: GEO, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) -func consumerPrices(startDate: Date, endDate: Date, geo: GEO) -> AnyPublisher<[PrecioLuzValue], Error> -func consumerPrices(date: Date, geo: GEO) -> AnyPublisher<[PrecioLuzValue], Error> -func consumerPrices(startDate: Date, endDate: Date, geo: GEO) async throws -> [PrecioLuzValue] -func consumerPrices(date: Date, geo: GEO) async throws -> [PrecioLuzValue] +func consumerPrices(startDate: Date, endDate: Date, geo: GEO, completion: @escaping (Result<[Value], Error>) -> Void) +func consumerPrices(date: Date, geo: GEO, completion: @escaping (Result<[Value], Error>) -> Void) +func consumerPrices(startDate: Date, endDate: Date, geo: GEO) -> AnyPublisher<[Value], Error> +func consumerPrices(date: Date, geo: GEO) -> AnyPublisher<[Value], Error> +func consumerPrices(startDate: Date, endDate: Date, geo: GEO) async throws -> [Value] +func consumerPrices(date: Date, geo: GEO) async throws -> [Value] ``` ### Obtener precios mercado spot ```swift -func spotPrices(startDate: Date, endDate: Date, geo: GEO, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) -func spotPrices(date: Date, geo: GEO, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) -func spotPrices(startDate: Date, endDate: Date, geo: GEO) -> AnyPublisher<[PrecioLuzValue], Error> -func spotPrices(date: Date, geo: GEO) -> AnyPublisher<[PrecioLuzValue], Error> -func spotPrices(startDate: Date, endDate: Date) async throws -> [PrecioLuzValue] -func spotPrices(date: Date) async throws -> [PrecioLuzValue] +func spotPrices(startDate: Date, endDate: Date, geo: GEO, completion: @escaping (Result<[Value], Error>) -> Void) +func spotPrices(date: Date, geo: GEO, completion: @escaping (Result<[Value], Error>) -> Void) +func spotPrices(startDate: Date, endDate: Date, geo: GEO) -> AnyPublisher<[Value], Error> +func spotPrices(date: Date, geo: GEO) -> AnyPublisher<[Value], Error> +func spotPrices(startDate: Date, endDate: Date) async throws -> [Value] +func spotPrices(date: Date) async throws -> [Value] ``` ## Licencia de uso y contribuciĆ³n con el proyecto diff --git a/Sources/REESwift/Model/PrecioLuzValue.swift b/Sources/REESwift/Model/PrecioLuzValue.swift deleted file mode 100644 index 86919b5..0000000 --- a/Sources/REESwift/Model/PrecioLuzValue.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation - -public protocol PrecioLuzValue { - var datetime: Date { get } - var value: Float { get } -} - -extension APIResponse.Attributes.Value: PrecioLuzValue { } diff --git a/Sources/REESwift/Model/Value.swift b/Sources/REESwift/Model/Value.swift new file mode 100644 index 0000000..8be5a6a --- /dev/null +++ b/Sources/REESwift/Model/Value.swift @@ -0,0 +1,8 @@ +import Foundation + +public protocol Value { + var datetime: Date { get } + var value: Float { get } +} + +extension APIResponse.Attributes.Value: Value { } diff --git a/Sources/REESwift/REESwift.swift b/Sources/REESwift/REESwift.swift index 8077b64..24f1847 100644 --- a/Sources/REESwift/REESwift.swift +++ b/Sources/REESwift/REESwift.swift @@ -9,39 +9,39 @@ public struct REESwift { public extension REESwift { - func consumerPrices(startDate: Date, endDate: Date, geo: GEO, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) { + func consumerPrices(startDate: Date, endDate: Date, geo: GEO, completion: @escaping (Result<[Value], Error>) -> Void) { prices(id: "1001", startDate: startDate, endDate: endDate, geo: geo, completion: completion) } - func consumerPrices(date: Date, geo: GEO, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) { + func consumerPrices(date: Date, geo: GEO, completion: @escaping (Result<[Value], Error>) -> Void) { prices(id: "1001", startDate: date.start, endDate: date.end, geo: geo, completion: completion) } - func consumerPrices(startDate: Date, endDate: Date, geo: GEO) -> AnyPublisher<[PrecioLuzValue], Error> { + func consumerPrices(startDate: Date, endDate: Date, geo: GEO) -> AnyPublisher<[Value], Error> { return prices(id: "1001", startDate: startDate, endDate: endDate, geo: geo) } - func consumerPrices(date: Date, geo: GEO) -> AnyPublisher<[PrecioLuzValue], Error> { + func consumerPrices(date: Date, geo: GEO) -> AnyPublisher<[Value], Error> { return prices(id: "1001", startDate: date.start, endDate: date.end, geo: geo) } - func spotPrices(startDate: Date, endDate: Date, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) { + func spotPrices(startDate: Date, endDate: Date, completion: @escaping (Result<[Value], Error>) -> Void) { prices(id: "600", startDate: startDate, endDate: endDate, geo: nil, completion: completion) } - func spotPrices(date: Date, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) { + func spotPrices(date: Date, completion: @escaping (Result<[Value], Error>) -> Void) { prices(id: "600", startDate: date.start, endDate: date.end, geo: nil, completion: completion) } - func spotPrices(startDate: Date, endDate: Date) -> AnyPublisher<[PrecioLuzValue], Error> { + func spotPrices(startDate: Date, endDate: Date) -> AnyPublisher<[Value], Error> { return prices(id: "600", startDate: startDate, endDate: endDate, geo: nil) } - func spotPrices(date: Date) -> AnyPublisher<[PrecioLuzValue], Error> { + func spotPrices(date: Date) -> AnyPublisher<[Value], Error> { return prices(id: "600", startDate: date.start, endDate: date.end, geo: nil) } - private func prices(id: String, startDate: Date, endDate: Date, geo: GEO?, completion: @escaping (Result<[PrecioLuzValue], Error>) -> Void) { + private func prices(id: String, startDate: Date, endDate: Date, geo: GEO?, completion: @escaping (Result<[Value], Error>) -> Void) { let endpoint = Endpoint.prices(startDate: startDate, endDate: endDate, geo: geo) Network.shared.request(endpoint) { (result: Result) in switch result { @@ -55,7 +55,7 @@ public extension REESwift { } } - private func prices(id: String, startDate: Date, endDate: Date, geo: GEO?) -> AnyPublisher<[PrecioLuzValue], Error> { + private func prices(id: String, startDate: Date, endDate: Date, geo: GEO?) -> AnyPublisher<[Value], Error> { let endpoint = Endpoint.prices(startDate: startDate, endDate: endDate, geo: geo) return Network.shared.request(endpoint) .tryMap { (apiResponse: APIResponse) -> [APIResponse.Attributes.Value] in @@ -74,23 +74,23 @@ public extension REESwift { @available(macOS 12, *) public extension REESwift { - func consumerPrices(startDate: Date, endDate: Date, geo: GEO) async throws -> [PrecioLuzValue] { + func consumerPrices(startDate: Date, endDate: Date, geo: GEO) async throws -> [Value] { return try await prices(id: "1001", startDate: startDate, endDate: endDate, geo: geo) } - func consumerPrices(date: Date, geo: GEO) async throws -> [PrecioLuzValue] { + func consumerPrices(date: Date, geo: GEO) async throws -> [Value] { return try await prices(id: "1001", startDate: date.start, endDate: date.end, geo: geo) } - func spotPrices(startDate: Date, endDate: Date) async throws -> [PrecioLuzValue] { + func spotPrices(startDate: Date, endDate: Date) async throws -> [Value] { return try await prices(id: "600", startDate: startDate, endDate: endDate, geo: nil) } - func spotPrices(date: Date) async throws -> [PrecioLuzValue] { + func spotPrices(date: Date) async throws -> [Value] { return try await prices(id: "600", startDate: date.start, endDate: date.end, geo: nil) } - private func prices(id: String, startDate: Date, endDate: Date, geo: GEO?) async throws -> [PrecioLuzValue] { + private func prices(id: String, startDate: Date, endDate: Date, geo: GEO?) async throws -> [Value] { let endpoint = Endpoint.prices(startDate: startDate, endDate: endDate, geo: geo) let apiResponse: APIResponse = try await Network.shared.request(endpoint) let prices = apiResponse.included.first { $0.id == id } From e5e318de5162bfb6beb1494b4dc1882ed8d7ad7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Fern=C3=A1ndez?= Date: Thu, 23 Dec 2021 11:39:15 +0100 Subject: [PATCH 3/3] feat: iOS 13+ async methods --- Sources/REESwift/Extensions/URLSession.swift | 17 +++++++++ Sources/REESwift/Network.swift | 4 -- Sources/REESwift/REESwift.swift | 40 ++++++++++---------- Tests/REESwiftTests/REESwiftTests.swift | 18 ++------- 4 files changed, 40 insertions(+), 39 deletions(-) create mode 100644 Sources/REESwift/Extensions/URLSession.swift diff --git a/Sources/REESwift/Extensions/URLSession.swift b/Sources/REESwift/Extensions/URLSession.swift new file mode 100644 index 0000000..3bf48f6 --- /dev/null +++ b/Sources/REESwift/Extensions/URLSession.swift @@ -0,0 +1,17 @@ +import Foundation + +@available(iOS, deprecated: 15.0) +extension URLSession { + func data(from url: URL) async throws -> (Data, URLResponse) { + try await withCheckedThrowingContinuation { continuation in + let task = self.dataTask(with: url) { data, response, error in + guard let data = data, let response = response else { + let error = error ?? URLError(.badServerResponse) + return continuation.resume(throwing: error) + } + continuation.resume(returning: (data, response)) + } + task.resume() + } + } +} diff --git a/Sources/REESwift/Network.swift b/Sources/REESwift/Network.swift index 4a6287b..934d07d 100644 --- a/Sources/REESwift/Network.swift +++ b/Sources/REESwift/Network.swift @@ -17,10 +17,6 @@ class Network { } } - @available(iOS 15.0, *) - @available(tvOS 15.0, *) - @available(watchOS 8.0, *) - @available(macOS 12, *) func request(_ endpoint: Endpoint) async throws -> T { let (data, response) = try await URLSession.shared.data(from: endpoint.url) guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw URLError(.badServerResponse) } diff --git a/Sources/REESwift/REESwift.swift b/Sources/REESwift/REESwift.swift index 24f1847..22d934f 100644 --- a/Sources/REESwift/REESwift.swift +++ b/Sources/REESwift/REESwift.swift @@ -17,14 +17,6 @@ public extension REESwift { prices(id: "1001", startDate: date.start, endDate: date.end, geo: geo, completion: completion) } - func consumerPrices(startDate: Date, endDate: Date, geo: GEO) -> AnyPublisher<[Value], Error> { - return prices(id: "1001", startDate: startDate, endDate: endDate, geo: geo) - } - - func consumerPrices(date: Date, geo: GEO) -> AnyPublisher<[Value], Error> { - return prices(id: "1001", startDate: date.start, endDate: date.end, geo: geo) - } - func spotPrices(startDate: Date, endDate: Date, completion: @escaping (Result<[Value], Error>) -> Void) { prices(id: "600", startDate: startDate, endDate: endDate, geo: nil, completion: completion) } @@ -33,14 +25,6 @@ public extension REESwift { prices(id: "600", startDate: date.start, endDate: date.end, geo: nil, completion: completion) } - func spotPrices(startDate: Date, endDate: Date) -> AnyPublisher<[Value], Error> { - return prices(id: "600", startDate: startDate, endDate: endDate, geo: nil) - } - - func spotPrices(date: Date) -> AnyPublisher<[Value], Error> { - return prices(id: "600", startDate: date.start, endDate: date.end, geo: nil) - } - private func prices(id: String, startDate: Date, endDate: Date, geo: GEO?, completion: @escaping (Result<[Value], Error>) -> Void) { let endpoint = Endpoint.prices(startDate: startDate, endDate: endDate, geo: geo) Network.shared.request(endpoint) { (result: Result) in @@ -55,6 +39,26 @@ public extension REESwift { } } +} + +public extension REESwift { + + func consumerPrices(startDate: Date, endDate: Date, geo: GEO) -> AnyPublisher<[Value], Error> { + return prices(id: "1001", startDate: startDate, endDate: endDate, geo: geo) + } + + func consumerPrices(date: Date, geo: GEO) -> AnyPublisher<[Value], Error> { + return prices(id: "1001", startDate: date.start, endDate: date.end, geo: geo) + } + + func spotPrices(startDate: Date, endDate: Date) -> AnyPublisher<[Value], Error> { + return prices(id: "600", startDate: startDate, endDate: endDate, geo: nil) + } + + func spotPrices(date: Date) -> AnyPublisher<[Value], Error> { + return prices(id: "600", startDate: date.start, endDate: date.end, geo: nil) + } + private func prices(id: String, startDate: Date, endDate: Date, geo: GEO?) -> AnyPublisher<[Value], Error> { let endpoint = Endpoint.prices(startDate: startDate, endDate: endDate, geo: geo) return Network.shared.request(endpoint) @@ -68,10 +72,6 @@ public extension REESwift { } -@available(iOS 15.0, *) -@available(tvOS 15.0, *) -@available(watchOS 8.0, *) -@available(macOS 12, *) public extension REESwift { func consumerPrices(startDate: Date, endDate: Date, geo: GEO) async throws -> [Value] { diff --git a/Tests/REESwiftTests/REESwiftTests.swift b/Tests/REESwiftTests/REESwiftTests.swift index 31b1e64..6066809 100644 --- a/Tests/REESwiftTests/REESwiftTests.swift +++ b/Tests/REESwiftTests/REESwiftTests.swift @@ -8,13 +8,9 @@ final class REESwiftTests: XCTestCase { // MARK: - Test consumer prices - @available(iOS 15.0, *) - @available(tvOS 15.0, *) - @available(watchOS 8.0, *) - @available(macOS 12, *) func testConsumerPricesAsync() async { - let now = Date.now + let now = Date() let todayPrices = try? await ree.consumerPrices(date: now, geo: .peninsula) XCTAssertNotNil(todayPrices) @@ -31,13 +27,9 @@ final class REESwiftTests: XCTestCase { } - @available(iOS 15.0, *) - @available(tvOS 15.0, *) - @available(watchOS 8.0, *) - @available(macOS 12, *) func testConsumerPricesAsyncOtherGEOs() async { - let now = Date.now + let now = Date() let peninsulaPrices = try? await ree.consumerPrices(date: now, geo: .peninsula) let ceutaPrices = try? await ree.consumerPrices(date: now, geo: .ceuta) @@ -132,13 +124,9 @@ final class REESwiftTests: XCTestCase { // MARK: - Test spot prices - @available(iOS 15.0, *) - @available(tvOS 15.0, *) - @available(watchOS 8.0, *) - @available(macOS 12, *) func testSpotPricesAsync() async { - let now = Date.now + let now = Date() let todayPrices = try? await ree.spotPrices(date: now) XCTAssertNotNil(todayPrices)