From 2451bfabbe052d9ff2d70e0ce67f0c0912aaf71c Mon Sep 17 00:00:00 2001 From: Petr Pavlik Date: Wed, 18 Dec 2024 19:58:57 -0400 Subject: [PATCH] swift format --- .../IndiePitcherSwift/IndiePitcherSwift.swift | 199 +++++++++++------- Sources/IndiePitcherSwift/dtos.swift | 147 +++++++------ Sources/IndiePitcherSwift/errors.swift | 2 +- Sources/IndiePitcherSwift/responses.swift | 29 +-- 4 files changed, 217 insertions(+), 160 deletions(-) diff --git a/Sources/IndiePitcherSwift/IndiePitcherSwift.swift b/Sources/IndiePitcherSwift/IndiePitcherSwift.swift index ba993da..be24deb 100644 --- a/Sources/IndiePitcherSwift/IndiePitcherSwift.swift +++ b/Sources/IndiePitcherSwift/IndiePitcherSwift.swift @@ -1,9 +1,9 @@ -import Foundation import AsyncHTTPClient +import Foundation import NIO import NIOCore -import NIOHTTP1 import NIOFoundationCompat +import NIOHTTP1 extension HTTPClientResponse { var isOk: Bool { @@ -14,11 +14,11 @@ extension HTTPClientResponse { /// IndiePitcher SDK. /// This SDK is only intended for server-side Swift use. Do not embed the secret API key in client-side code for security reasons. public struct IndiePitcher: Sendable { - private let client: HTTPClient // is sendable / thread-safe + private let client: HTTPClient // is sendable / thread-safe private let apiKey: String private let requestTimeout: TimeAmount = .seconds(30) private let maxResponseSize = 1024 * 1024 * 100 - + /// Creates a new instance of IndiePitcher SDK /// - Parameters: /// - client: Vapor's client instance to use to perform network requests. Uses the shared client by default. @@ -27,196 +27,235 @@ public struct IndiePitcher: Sendable { self.client = client self.apiKey = apiKey } - + // MARK: networking - + private var commonHeaders: HTTPHeaders { - get { - var headers = HTTPHeaders() - headers.add(name: "Authorization", value: "Bearer \(apiKey)") - headers.add(name: "User-Agent", value: "IndiePitcherSwift") - return headers - } + var headers = HTTPHeaders() + headers.add(name: "Authorization", value: "Bearer \(apiKey)") + headers.add(name: "User-Agent", value: "IndiePitcherSwift") + return headers } - + private var jsonEncoder: JSONEncoder { let encoder = JSONEncoder() encoder.dateEncodingStrategy = .iso8601 return encoder } - + private var jsonDecoder: JSONDecoder { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 return decoder } - + private func buildUri(path: String) -> String { "https://api.indiepitcher.com/v1" + path } - - private func post(path: String, body: Codable) async throws -> T { - + + private func post(path: String, body: Codable) async throws -> T + { + var headers = commonHeaders headers.add(name: "Content-Type", value: "application/json") - + var request = HTTPClientRequest(url: buildUri(path: path)) request.method = .POST request.headers = headers request.body = .bytes(.init(data: try jsonEncoder.encode(body))) - - let response = try await client.execute(request, timeout: requestTimeout) - let responseData = try await response.body.collect(upTo: maxResponseSize) - + + let response = try await client.execute( + request, timeout: requestTimeout) + let responseData = try await response.body.collect( + upTo: maxResponseSize) + guard response.isOk else { - let error = try? jsonDecoder.decode(ErrorResponse.self, from: responseData) - throw IndiePitcherRequestError(statusCode: response.status.code, reason: error?.reason ?? "Unknown reason") + let error = try? jsonDecoder.decode( + ErrorResponse.self, from: responseData) + throw IndiePitcherRequestError( + statusCode: response.status.code, + reason: error?.reason ?? "Unknown reason") } - + return try self.jsonDecoder.decode(T.self, from: responseData) } - - private func patch(path: String, body: Codable) async throws -> T { - + + private func patch(path: String, body: Codable) async throws + -> T + { + var headers = commonHeaders headers.add(name: "Content-Type", value: "application/json") - + var request = HTTPClientRequest(url: buildUri(path: path)) request.method = .PATCH request.headers = headers request.body = .bytes(.init(data: try jsonEncoder.encode(body))) - - let response = try await client.execute(request, timeout: requestTimeout) - let responseData = try await response.body.collect(upTo: maxResponseSize) - + + let response = try await client.execute( + request, timeout: requestTimeout) + let responseData = try await response.body.collect( + upTo: maxResponseSize) + guard response.isOk else { - let error = try? jsonDecoder.decode(ErrorResponse.self, from: responseData) - throw IndiePitcherRequestError(statusCode: response.status.code, reason: error?.reason ?? "Unknown reason") + let error = try? jsonDecoder.decode( + ErrorResponse.self, from: responseData) + throw IndiePitcherRequestError( + statusCode: response.status.code, + reason: error?.reason ?? "Unknown reason") } - + return try self.jsonDecoder.decode(T.self, from: responseData) } - + private func get(path: String) async throws -> T { - + let headers = commonHeaders - + var request = HTTPClientRequest(url: buildUri(path: path)) request.method = .GET request.headers = headers - - let response = try await client.execute(request, timeout: requestTimeout) - let responseData = try await response.body.collect(upTo: maxResponseSize) - + + let response = try await client.execute( + request, timeout: requestTimeout) + let responseData = try await response.body.collect( + upTo: maxResponseSize) + guard response.isOk else { - let error = try? jsonDecoder.decode(ErrorResponse.self, from: responseData) - throw IndiePitcherRequestError(statusCode: response.status.code, reason: error?.reason ?? "Unknown reason") + let error = try? jsonDecoder.decode( + ErrorResponse.self, from: responseData) + throw IndiePitcherRequestError( + statusCode: response.status.code, + reason: error?.reason ?? "Unknown reason") } - + return try self.jsonDecoder.decode(T.self, from: responseData) } - + // MARK: API calls - + /// Add a new contact to the mailing list, or update an existing one if `updateIfExists` is set to `true`. /// - Parameter contact: Contact properties. /// - Returns: Created contact. - @discardableResult public func addContact(contact: CreateContact) async throws -> DataResponse { + @discardableResult public func addContact(contact: CreateContact) + async throws -> DataResponse + { try await post(path: "/contacts/create", body: contact) } - + /// Add miultiple contacts (up to 100) using a single API call to avoid being rate limited. Payloads with `updateIfExists` is set to `true` will be updated if a contact with given email already exists. /// - Parameter contacts: Contact properties /// - Returns: A generic empty response. - @discardableResult public func addContacts(contacts: [CreateContact]) async throws -> EmptyResposne { - + @discardableResult public func addContacts(contacts: [CreateContact]) + async throws -> EmptyResposne + { + struct Payload: Codable { let contacts: [CreateContact] } - - return try await post(path: "/contacts/create_many", body: Payload(contacts: contacts)) + + return try await post( + path: "/contacts/create_many", body: Payload(contacts: contacts)) } - + /// Updates a contact with given email address. This call will fail if a contact with provided email does not exist, use `addContact` instead in such case. /// - Parameter contact: Contact properties to update /// - Returns: Updated contact. - @discardableResult public func updateContact(contact: UpdateContact) async throws -> DataResponse { + @discardableResult public func updateContact(contact: UpdateContact) + async throws -> DataResponse + { try await patch(path: "/contacts/update", body: contact) } - + /// Deletes a contact with provided email from the mailing list /// - Parameter email: The email address of the contact you wish to remove from the mailing list /// - Returns: A generic empty response. - @discardableResult public func deleteContact(email: String) async throws -> EmptyResposne { - + @discardableResult public func deleteContact(email: String) async throws + -> EmptyResposne + { + struct Payload: Codable { var email: String } - - return try await post(path: "/contacts/delete", body: Payload(email: email)) + + return try await post( + path: "/contacts/delete", body: Payload(email: email)) } - + /// Returns a paginated list of stored contacts in the mailing list. /// - Parameters: /// - page: Page to fetch, the first page has index 1. /// - perPage: How many contacts to return per page. /// - Returns: A paginated array of contacts - public func listContacts(page: Int = 1, perPage: Int = 10) async throws -> PagedDataResponse { + public func listContacts(page: Int = 1, perPage: Int = 10) async throws + -> PagedDataResponse + { try await get(path: "/contacts?page=\(page)&per=\(perPage)") } - + /// Sends an email to specified email address. /// The email is not required to belong to a contact in your contact lsit. Use this API to send emails such as that a user who is not signed up for your product was invited to a team. /// - Parameter data: Input params. /// - Returns: A genereic response with no return data. - @discardableResult public func sendEmail(data: SendEmail) async throws -> EmptyResposne { + @discardableResult public func sendEmail(data: SendEmail) async throws + -> EmptyResposne + { try await post(path: "/email/transactional", body: data) } - + /// Send a personalized email to one more (up to 100 using 1 API call) contacts subscribed to a proviced mailing list. This is the recommended way to send an email to members of a team of your product. /// All provided emails must belong to your mailing list and must be members of provided mailing list. All contacts are automatically subscribed to `important` default mailing list. You can use peronalization tags such as `Hi {{firstName}}` to peronalize individual sent emails, and scheduled it to be sent with a delay. /// - Parameter data: Input params. /// - Returns: A genereic response with no return data. - @discardableResult public func sendEmailToContact(data: SendEmailToContact) async throws -> EmptyResposne { + @discardableResult public func sendEmailToContact(data: SendEmailToContact) + async throws -> EmptyResposne + { try await post(path: "/email/contact", body: data) } - + /// Send a personalized email to all contacts subscribed to a provided mailing list. This is the recommendat way to send a newsletter, by creating a list called something like `Newsletter`. /// All contacts are automatically subscribed to `important` default mailing list. You can use peronalization tags such as `Hi {{firstName}}` to peronalize individual sent emails, and scheduled it to be sent with a delay. /// - Parameter data: Input params. /// - Returns: A genereic response with no return data. - @discardableResult public func sendEmailToMailingList(data: SendEmailToMailingList) async throws -> EmptyResposne { + @discardableResult public func sendEmailToMailingList( + data: SendEmailToMailingList + ) async throws -> EmptyResposne { try await post(path: "/email/list", body: data) } - + /// Returns mailing lists contacts can subscribe to. /// - Parameters: /// - page: Page to fetch, the first page has index 1. /// - perPage: How many contacts to return per page. /// - Returns: A paginated array of mailing lists - public func listMailingLists(page: Int = 1, perPage: Int = 10) async throws -> PagedDataResponse { - + public func listMailingLists(page: Int = 1, perPage: Int = 10) async throws + -> PagedDataResponse + { + struct Payload: Codable { let page: Int let per: Int } - + return try await get(path: "/lists?page=\(page)&per=\(perPage)") } - - + /// Generates a new public URL for a contact with provided email to manage their mailing list subscriptions. /// - Parameters: /// - contactEmail: The email of a contact in your project's contact list, who to create the portal session for. /// - returnURL: The URL to redirect to when the user is done editing their mailing list, or when the session has expired. /// - Returns: The URL to redirect your user to, and the expiration date of the session. - public func createMailingListsPortalSession(contactEmail: String, returnURL: URL) async throws -> DataResponse { - + public func createMailingListsPortalSession( + contactEmail: String, returnURL: URL + ) async throws -> DataResponse { + struct Payload: Codable { let contactEmail: String let returnURL: URL } - - return try await post(path: "/lists/portal_session", body: Payload(contactEmail: contactEmail, returnURL: returnURL)) + + return try await post( + path: "/lists/portal_session", + body: Payload(contactEmail: contactEmail, returnURL: returnURL)) } } diff --git a/Sources/IndiePitcherSwift/dtos.swift b/Sources/IndiePitcherSwift/dtos.swift index c1ecf51..3dc0c37 100644 --- a/Sources/IndiePitcherSwift/dtos.swift +++ b/Sources/IndiePitcherSwift/dtos.swift @@ -1,10 +1,3 @@ -// -// File.swift -// -// -// Created by Petr Pavlik on 17.08.2024. -// - import Foundation /// The format of the email body @@ -25,7 +18,7 @@ public enum CustomContactPropertyValue: Codable, Equatable, Sendable { case bool(Bool) /// A date property case date(Date) - + // Coding keys to differentiate between the cases private enum CodingKeys: String, CodingKey { case string @@ -33,7 +26,7 @@ public enum CustomContactPropertyValue: Codable, Equatable, Sendable { case bool case date } - + // Encoding function public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() @@ -48,38 +41,47 @@ public enum CustomContactPropertyValue: Codable, Equatable, Sendable { try container.encode(value) } } - + // Decoding function public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() - + if let value = try? container.decode(Date.self) { self = .date(value) return } - + if let value = try? container.decode(Bool.self) { self = .bool(value) return } - + if let value = try? container.decode(Double.self) { self = .number(value) return } - + if let value = try? container.decode(String.self) { self = .string(value) return } - - throw DecodingError.dataCorruptedError(in: container, debugDescription: "Data doesn't match either string, number, bool, or date.") + + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: + "Data doesn't match either string, number, bool, or date.") } } /// A contact in the mailing list public struct Contact: Codable, Sendable { - public init(email: String, userId: String? = nil, avatarUrl: String? = nil, name: String? = nil, hardBouncedAt: Date? = nil, subscribedToLists: [String], customProperties: [String : CustomContactPropertyValue], languageCode: String? = nil) { + public init( + email: String, userId: String? = nil, avatarUrl: String? = nil, + name: String? = nil, hardBouncedAt: Date? = nil, + subscribedToLists: [String], + customProperties: [String: CustomContactPropertyValue], + languageCode: String? = nil + ) { self.email = email self.userId = userId self.avatarUrl = avatarUrl @@ -89,7 +91,7 @@ public struct Contact: Codable, Sendable { self.customProperties = customProperties self.languageCode = languageCode } - + /// The email of the contact public var email: String /// The user id of the contact @@ -110,7 +112,7 @@ public struct Contact: Codable, Sendable { /// The payload to create a new contact public struct CreateContact: Codable, Sendable { - + /// Initializer /// - Parameters: /// - email: The email of the contact. @@ -122,7 +124,13 @@ public struct CreateContact: Codable, Sendable { /// - subscribedToLists: The list of mailing lists the contact should be subscribed to. Use the `name` field of the lists. /// - customProperties: The custom properties of the contact. Custom properties must be first defined in the IndiePitcher dashboard. /// - ignoreListSubscriptionsWhenUpdating: Whether to ignore subscribedToLists field if the contact already exists and `updateIfExists` is set to `true`. Useful to avoid accidentally resubscribing a contact to lists they unsubscribed before. Default value is `true`. - public init(email: String, userId: String? = nil, avatarUrl: String? = nil, name: String? = nil, languageCode: String? = nil, updateIfExists: Bool? = nil, subscribedToLists: Set? = nil, customProperties: [String : CustomContactPropertyValue]? = nil, ignoreListSubscriptionsWhenUpdating: Bool? = nil) { + public init( + email: String, userId: String? = nil, avatarUrl: String? = nil, + name: String? = nil, languageCode: String? = nil, + updateIfExists: Bool? = nil, subscribedToLists: Set? = nil, + customProperties: [String: CustomContactPropertyValue]? = nil, + ignoreListSubscriptionsWhenUpdating: Bool? = nil + ) { self.email = email self.userId = userId self.avatarUrl = avatarUrl @@ -131,9 +139,10 @@ public struct CreateContact: Codable, Sendable { self.updateIfExists = updateIfExists self.subscribedToLists = subscribedToLists self.customProperties = customProperties - self.ignoreListSubscriptionsWhenUpdating = ignoreListSubscriptionsWhenUpdating + self.ignoreListSubscriptionsWhenUpdating = + ignoreListSubscriptionsWhenUpdating } - + /// The email of the contact. public var email: String /// The user id of the contact. @@ -156,20 +165,20 @@ public struct CreateContact: Codable, Sendable { /// The payload to create multiple contacts using a single API call public struct CreateMultipleContacts: Codable, Sendable { - + /// Initializer /// - Parameter contacts: The list of contacts to create public init(contacts: [CreateContact]) { self.contacts = contacts } - + /// The list of contacts to create public var contacts: [CreateContact] } /// The payload to update a contact in the mailing list. The email is required to identify the contact. public struct UpdateContact: Codable, Sendable { - + /// Initializer /// - Parameters: /// - email: The email of the contact. @@ -180,7 +189,13 @@ public struct UpdateContact: Codable, Sendable { /// - addedListSubscripitons: The list of mailing lists to subscribe the contact to. Use the `name` field of the lists. /// - removedListSubscripitons: The list of mailing lists unsubscribe the contact from. Use the `name` field of the lists. /// - customProperties: The custom properties of the contact. Custom properties must be first defined in the IndiePitcher dashboard. Pass 'nil' to remove a custom property. - public init(email: String, userId: String? = nil, avatarUrl: String? = nil, name: String? = nil, languageCode: String? = nil, addedListSubscripitons: Set? = nil, removedListSubscripitons: Set? = nil, customProperties: [String : CustomContactPropertyValue?]? = nil) { + public init( + email: String, userId: String? = nil, avatarUrl: String? = nil, + name: String? = nil, languageCode: String? = nil, + addedListSubscripitons: Set? = nil, + removedListSubscripitons: Set? = nil, + customProperties: [String: CustomContactPropertyValue?]? = nil + ) { self.email = email self.userId = userId self.avatarUrl = avatarUrl @@ -190,7 +205,7 @@ public struct UpdateContact: Codable, Sendable { self.removedListSubscripitons = removedListSubscripitons self.customProperties = customProperties } - + /// The email of the contact. public var email: String /// The user id of the contact. @@ -211,36 +226,38 @@ public struct UpdateContact: Codable, Sendable { /// Payload of send transactional email request. public struct SendEmail: Codable, Sendable { - + /// Initializer /// - Parameters: /// - to: Can be just an email "john@example.com", or an email with a neme "John Doe " /// - subject: The subject of the email. /// - body: The body of the email. /// - bodyFormat: The format of the body of the email. Can be `markdown` or `html`. - public init(to: String, subject: String, body: String, bodyFormat: EmailBodyFormat) { + public init( + to: String, subject: String, body: String, bodyFormat: EmailBodyFormat + ) { self.to = to self.subject = subject self.body = body self.bodyFormat = bodyFormat } - + /// Can be just an email "john@example.com", or an email with a neme "John Doe " public var to: String - + /// The subject of the email. public var subject: String - + /// The body of the email. public var body: String - + /// The format of the body of the email. Can be `markdown` or `html`. public var bodyFormat: EmailBodyFormat } /// Send an email to one of more registered contacts. public struct SendEmailToContact: Codable, Sendable { - + /// Initializer /// - Parameters: /// - contactEmail: The email of the contact to send. @@ -251,7 +268,12 @@ public struct SendEmailToContact: Codable, Sendable { /// - list: Specify a list the contact(s) can unsubscribe from if they don't wish to receive further emails like this. The contact(s) must be subscribed to this list. Pass "important" to provide a list the contact(s) cannot unsubscribe from. /// - delaySeconds: Delay sending of this email by the amount of seconds you provide. /// - delayUntilDate: Delay sending of this email until specified date. - public init(contactEmail: String? = nil, contactEmails: [String]? = nil, subject: String, body: String, bodyFormat: EmailBodyFormat, list: String = "important", delaySeconds: TimeInterval? = nil, delayUntilDate: Date? = nil) { + public init( + contactEmail: String? = nil, contactEmails: [String]? = nil, + subject: String, body: String, bodyFormat: EmailBodyFormat, + list: String = "important", delaySeconds: TimeInterval? = nil, + delayUntilDate: Date? = nil + ) { self.contactEmail = contactEmail self.contactEmails = contactEmails self.subject = subject @@ -261,34 +283,34 @@ public struct SendEmailToContact: Codable, Sendable { self.delaySeconds = delaySeconds self.delayUntilDate = delayUntilDate } - + /// The email of the contact to send. public var contactEmail: String? - + /// Allows you to send an email to multiple contacts using a single request. public var contactEmails: [String]? - + /// The subject of the email. Supports personalization. public var subject: String - + /// The body of the email. Both HTML and markdown body do support personalization. public var body: String - + /// The format of the body of the email. Can be `markdown` or `html`. public var bodyFormat: EmailBodyFormat - + /// Specify a list the contact(s) can unsubscribe from if they don't wish to receive further emails like this. The contact(s) must be subscribed to this list. Pass "important" to provide a list the contact(s) cannot unsubscribe from. public var list: String - + /// Delay sending of this email by the amount of seconds you provide. public var delaySeconds: TimeInterval? - + /// Delay sending of this email until specified date. public var delayUntilDate: Date? } public struct SendEmailToMailingList: Codable, Sendable { - + /// Initializer /// - Parameters: /// - subject: The subject of the email. Supports personalization. @@ -299,7 +321,12 @@ public struct SendEmailToMailingList: Codable, Sendable { /// - delayUntilDate: Delay sending of this email by the amount of seconds you provide. /// - trackEmailOpens: Whether to track email opens. Allow you to overwrite the project's global setting. /// - trackEmailLinkClicks: Whether to track email opens. Allow you to overwrite the project's global setting. - public init(subject: String, body: String, bodyFormat: EmailBodyFormat, list: String = "important", delaySeconds: TimeInterval? = nil, delayUntilDate: Date? = nil, trackEmailOpens: Bool? = nil, trackEmailLinkClicks: Bool? = nil) { + public init( + subject: String, body: String, bodyFormat: EmailBodyFormat, + list: String = "important", delaySeconds: TimeInterval? = nil, + delayUntilDate: Date? = nil, trackEmailOpens: Bool? = nil, + trackEmailLinkClicks: Bool? = nil + ) { self.subject = subject self.body = body self.bodyFormat = bodyFormat @@ -309,31 +336,31 @@ public struct SendEmailToMailingList: Codable, Sendable { self.trackEmailOpens = trackEmailOpens self.trackEmailLinkClicks = trackEmailLinkClicks } - + /// The subject of the email. Supports personalization. public var subject: String - + /// The body of the email. Both HTML and markdown body do support personalization. public var body: String - + /// The format of the body of the email. Can be `markdown` or `html`. public var bodyFormat: EmailBodyFormat - + /// The email will be sent to contacts subscribed to this list. Pass "important" to send the email to all of your contacts. public var list: String - + /// Delay sending of this email by the amount of seconds you provide. public var delaySeconds: TimeInterval? - + /// Delay sending of this email until specified date. public var delayUntilDate: Date? - + /// Whether to track email opens. /// /// Allow you to overwrite the project's global setting. /// - Default: `nil`- Uses the project's global setting. var trackEmailOpens: Bool? - + /// Whether to track email opens. /// /// Allow you to overwrite the project's global setting. @@ -343,19 +370,19 @@ public struct SendEmailToMailingList: Codable, Sendable { /// Represents a mailing list contacts can subscribe to, such as `Monthly newsletter` or `Onboarding`. public struct MailingList: Codable, Sendable { - + public init(name: String, title: String, numSubscribers: Int) { self.name = name self.title = title self.numSubscribers = numSubscribers } - + /// The unique name of the mailing list meant to be used by the public API. Not intended to be be shown to the end users, that's what `title` is for. public var name: String - + /// A human readable name of the mailing list. public var title: String - + /// The number of contacts subscribed to this list. public var numSubscribers: Int } @@ -367,13 +394,13 @@ public struct MailingListPortalSession: Codable, Sendable { self.expiresAt = expiresAt self.returnURL = returnURL } - + /// The URL under which the user can manage their list subscriptions. public var url: URL - + /// Specified until when will the URL be valid public var expiresAt: Date - + /// URL to redirect the user to when they tap on that they're cone editing their lists, or when the session is expired. public var returnURL: URL } diff --git a/Sources/IndiePitcherSwift/errors.swift b/Sources/IndiePitcherSwift/errors.swift index 8ee74e9..44db6af 100644 --- a/Sources/IndiePitcherSwift/errors.swift +++ b/Sources/IndiePitcherSwift/errors.swift @@ -1,7 +1,7 @@ public struct IndiePitcherRequestError: Error, Equatable, Sendable { var statusCode: UInt var reason: String - + public init(statusCode: UInt, reason: String) { self.statusCode = statusCode self.reason = reason diff --git a/Sources/IndiePitcherSwift/responses.swift b/Sources/IndiePitcherSwift/responses.swift index c0de445..bee7728 100644 --- a/Sources/IndiePitcherSwift/responses.swift +++ b/Sources/IndiePitcherSwift/responses.swift @@ -1,34 +1,25 @@ -// -// File.swift -// -// -// Created by Petr Pavlik on 17.08.2024. -// - -import Foundation - /// Represents a response returning data. public struct DataResponse: Codable, Sendable { - + public init(data: T) { self.success = true self.data = data } - + /// Always true public var success: Bool - + /// Returned data public var data: T } /// Represents a response returning no useful data. public struct EmptyResposne: Codable, Sendable { - + public init() { self.success = true } - + /// Always true public var success: Bool } @@ -40,7 +31,7 @@ public struct PagedDataResponse: Codable, Sendable { self.data = data self.metadata = metadata } - + /// Paging metadata public struct PageMetadata: Codable, Sendable { public init(page: Int, per: Int, total: Int) { @@ -48,7 +39,7 @@ public struct PagedDataResponse: Codable, Sendable { self.per = per self.total = total } - + /// Page index, indexed from 1. public let page: Int /// Number of results per page @@ -56,13 +47,13 @@ public struct PagedDataResponse: Codable, Sendable { /// Total number of results. public let total: Int } - + /// Always true public var success: Bool - + /// Returned results public var data: [T] - + /// Paging metadata public var metadata: PageMetadata }