Skip to content

Commit

Permalink
Preparing for Swift 6 (#1)
Browse files Browse the repository at this point in the history
Bumping swift-tools-version to 5.9 and enabling StrictConcurrency=complete in preparation for Swift 6.
  • Loading branch information
mikwat authored Mar 14, 2024
1 parent b8e38ec commit 924f03e
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 184 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ on:

jobs:
build:

runs-on: macos-latest
runs-on: macos-14

steps:
- uses: swift-actions/setup-swift@v2
with:
swift-version: "5.9"
- uses: actions/checkout@v3
- name: Build
run: swift build -v
Expand Down
9 changes: 7 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.7.1
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand All @@ -18,7 +18,12 @@ let package = Package(
// 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: "MailerLiteAPIClient"),
name: "MailerLiteAPIClient",
swiftSettings: [
/// Remove `=targeted` to use the default `complete`.
.enableExperimentalFeature("StrictConcurrency=complete")
]
),
.testTarget(
name: "MailerLiteAPIClientTests",
dependencies: ["MailerLiteAPIClient"]),
Expand Down
71 changes: 33 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ import MailerLiteAPIClient

let mailerLite = MailerLiteAPIClient(apiKey: "your-api-key")

mailerLite.send(ListSubscribers()) { result in
switch result {
case .success(let response):
// TODO: handle response
let data = response.data {
print("Found \(data.count) subscribers")
}
case .failure(let error):
// TODO: handle error
do {
let response = mailerLite.send(ListSubscribers())
// TODO: handle response
let data = response.data {
print("Found \(data.count) subscribers")
}
} catch {
// TODO: handle error
}
```

Expand All @@ -32,16 +30,15 @@ import MailerLiteAPIClient

let mailerLite = MailerLiteAPIClient(apiKey: "your-api-key")

mailerLite.send(UpsertSubscriber(email: "dummy@example.com", fields: Subscriber.Fields(lastName: "Testerson"))) { result in
switch result {
case .success(let response):
// TODO: handle response
let data = response.data {
print("Subscriber \(data.email) upserted")
}
case .failure(let error):
// TODO: handle error
do {
let response = mailerLite.send(UpsertSubscriber(email: "dummy@example.com", fields: Subscriber.Fields(lastName: "Testerson")))
// TODO: handle response
let data = response.data {
print("Subscriber \(data.email) upserted")
}
} catch {
// TODO: handle error
switch result {
}
```

Expand All @@ -51,27 +48,25 @@ import MailerLiteAPIClient

let mailerLite = MailerLiteAPIClient(apiKey: "your-api-key")

mailerLite.send(UpdateSubscriber(
id: "31897397363737859",
fields: Subscriber.Fields(
lastName: nil,
name: "Dummy"
),
groups: [
"4243829086487936",
"14133878422767533",
"31985378335392975"
]
))) { result in
switch result {
case .success(let response):
// TODO: handle response
let data = response.data {
print("Subscriber \(data.id) updated")
}
case .failure(let error):
// TODO: handle error
do {
let response = mailerLite.send(UpdateSubscriber(
id: "31897397363737859",
fields: Subscriber.Fields(
lastName: nil,
name: "Dummy"
),
groups: [
"4243829086487936",
"14133878422767533",
"31985378335392975"
]
)))
// TODO: handle response
let data = response.data {
print("Subscriber \(data.id) updated")
}
} catch {
// TODO: handle error
}
```

Expand Down
2 changes: 1 addition & 1 deletion Sources/MailerLiteAPIClient/APIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ import Foundation
public typealias ResultCallback<Value> = (Result<Value, Error>) -> Void

protocol APIClient {
func send<T: APIRequest>(_ request: T, completion: @escaping ResultCallback<T.Response>) async
func send<T: APIRequest>(_ request: T) async throws -> T.Response
}
2 changes: 1 addition & 1 deletion Sources/MailerLiteAPIClient/APIClientError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

enum APIClientError: Error, Equatable {
enum APIClientError: Error, Equatable, Sendable {
case decoding, encoding, unknownResponse
case parsing(Error)
case response(APIErrorResponse)
Expand Down
2 changes: 1 addition & 1 deletion Sources/MailerLiteAPIClient/APIErrorResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

public struct APIErrorResponse: Decodable {
public struct APIErrorResponse: Decodable, Sendable {
let message: String?
let errors: [String: [String]]?
}
38 changes: 17 additions & 21 deletions Sources/MailerLiteAPIClient/MailerLiteAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class MailerLiteAPIClient: APIClient {
self.urlSession = urlSession
}

public func send<T: APIRequest>(_ request: T, completion: @escaping ResultCallback<T.Response>) -> Void {
public func send<T: APIRequest>(_ request: T) async throws -> T.Response {
let endpoint = self.endpoint(for: request)
var urlRequest = URLRequest(url: endpoint)
urlRequest.httpMethod = request.method.rawValue.uppercased()
Expand All @@ -30,32 +30,28 @@ public class MailerLiteAPIClient: APIClient {
do {
urlRequest.httpBody = try JSONEncoder().encode(request)
} catch {
return completion(.failure(APIClientError.encoding))
throw APIClientError.encoding
}
}

let task = urlSession.dataTask(with: urlRequest) { data, response, error in
if let error = error {
completion(.failure(error))
} else if let data = data {
do {
let httpResponse = response as! HTTPURLResponse
switch httpResponse.statusCode {
case 200...299:
let apiResponse = try JSONDecoder().decode(T.Response.self, from: data)
completion(.success(apiResponse))
default:
let apiErrorResponse = try JSONDecoder().decode(APIErrorResponse.self, from: data)
completion(.failure(APIClientError.response(apiErrorResponse)))
}
} catch {
completion(.failure(APIClientError.parsing(error)))
}
let (data, response) = try await urlSession.data(for: urlRequest)
do {
let httpResponse = response as! HTTPURLResponse
switch httpResponse.statusCode {
case 200...299:
let apiResponse = try JSONDecoder().decode(T.Response.self, from: data)
return apiResponse
default:
let apiErrorResponse = try JSONDecoder().decode(APIErrorResponse.self, from: data)
throw APIClientError.response(apiErrorResponse)
}
} catch {
if let error = error as? APIClientError {
throw error
} else {
completion(.failure(APIClientError.unknownResponse))
throw APIClientError.parsing(error)
}
}
task.resume()
}

private func endpoint<T: APIRequest>(for request: T) -> URL {
Expand Down
62 changes: 30 additions & 32 deletions Tests/MailerLiteAPIClientTests/Requests/ListSubscribersTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ final class ListSubscribersTest: XCTestCase {
expectation = expectation(description: "Expectation")
}

func testSuccess() {
func testSuccess() async {
let jsonString = """
{
"data": [
Expand Down Expand Up @@ -82,48 +82,46 @@ final class ListSubscribersTest: XCTestCase {
return (response, data)
}

apiClient.send(ListSubscribers()) { result in
switch result {
case .success(let response):
XCTAssertEqual(response.data?.count, 1)
XCTAssertEqual(response.data?.first?.email, "dummy@example.com")
XCTAssertNotNil(response.meta?.nextCursor)
case .failure(let error):
XCTFail("Error was not expected: \(error)")
}
self.expectation.fulfill()
do {
let response = try await apiClient.send(ListSubscribers())
XCTAssertEqual(response.data?.count, 1)
XCTAssertEqual(response.data?.first?.email, "dummy@example.com")
XCTAssertNotNil(response.meta?.nextCursor)
} catch {
XCTFail("Error was not expected: \(error)")
}
wait(for: [expectation], timeout: 1.0)

self.expectation.fulfill()
await fulfillment(of: [expectation], timeout: 1.0)
}

func testFailure() {
func testFailure() async {
let data = Data()
let apiURL = URL(string: "https://connect.mailerlite.com/api/subscribers?")!
MockURLProtocol.requestHandler = { request in
let response = HTTPURLResponse(url: apiURL, statusCode: 500, httpVersion: nil, headerFields: nil)!
return (response, data)
}

apiClient.send(ListSubscribers()) { result in
switch result {
case .success(_):
XCTFail("Success was not expected.")
case .failure(let error):
guard let apiError = error as? APIClientError else {
XCTFail("Incorrect error received.")
self.expectation.fulfill()
return
}

switch apiError {
case .parsing(let error):
XCTAssertEqual(error.localizedDescription, "The data couldn’t be read because it isn’t in the correct format.")
default:
XCTFail("Incorrect error received.")
}
do {
let _ = try await apiClient.send(ListSubscribers())
XCTFail("Success was not expected.")
} catch {
guard let apiError = error as? APIClientError else {
XCTFail("Incorrect error received.")
self.expectation.fulfill()
return
}

switch apiError {
case .parsing(let error):
XCTAssertEqual(error.localizedDescription, "The data couldn’t be read because it isn’t in the correct format.")
default:
XCTFail("Incorrect error received.")
}
self.expectation.fulfill()
}
wait(for: [expectation], timeout: 1.0)

self.expectation.fulfill()
await fulfillment(of: [expectation], timeout: 1.0)
}
}
Loading

0 comments on commit 924f03e

Please sign in to comment.