From 8c15b7838933c565f71ad4ddb04735b662077178 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 11 Sep 2025 09:24:23 -0500 Subject: [PATCH 1/5] remove support for non-SwiftPM package managers. require Swift 6.0+. --- .github/workflows/tests.yml | 19 +---- JSONAPI.podspec | 141 ------------------------------------ Package.resolved | 25 +++---- Package.swift | 10 +-- README.md | 28 +------ 5 files changed, 25 insertions(+), 198 deletions(-) delete mode 100644 JSONAPI.podspec diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9644cf8..df8f649 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,23 +13,12 @@ jobs: fail-fast: false matrix: image: - - swift:5.2-focal - - swift:5.2-centos8 - - swift:5.3-focal - - swift:5.3-centos8 - - swift:5.4-focal - - swift:5.4-centos8 - - swift:5.5-focal - - swift:5.5-centos8 - - swift:5.6-focal -# - swift:5.7-focal -# - swift:5.8-focal -# - swift:5.9-focal -# - swift:5.10-focal + - swift:6.0 + - swift:6.1 container: ${{ matrix.image }} steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v5 - name: Run tests run: swift test --enable-test-discovery osx: @@ -39,6 +28,6 @@ jobs: uses: maxim-lobanov/setup-xcode@v1 with: { 'xcode-version': 'latest' } - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v5 - name: Run tests run: swift test --enable-test-discovery diff --git a/JSONAPI.podspec b/JSONAPI.podspec deleted file mode 100644 index 4a22764..0000000 --- a/JSONAPI.podspec +++ /dev/null @@ -1,141 +0,0 @@ -# -# Be sure to run `pod spec lint JSONAPI.podspec' to ensure this is a -# valid spec and to remove all comments including this before submitting the spec. -# -# To learn more about Podspec attributes see https://docs.cocoapods.org/specification.html -# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ -# - -Pod::Spec.new do |spec| - - # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # These will help people to find your library, and whilst it - # can feel like a chore to fill in it's definitely to your advantage. The - # summary should be tweet-length, and the description more in depth. - # - - spec.name = "MP-JSONAPI" - spec.version = "5.1.0" - spec.summary = "Swift Codable JSON API framework." - - # This description is used to generate tags and improve search results. - # * Think: What does it do? Why did you write it? What is the focus? - # * Try to keep it short, snappy and to the point. - # * Write the description between the DESC delimiters below. - # * Finally, don't worry about the indent, CocoaPods strips it! - spec.description = <<-DESC - A Swift package for encoding to- and decoding from JSON API compliant requests and responses. - -See the JSON API Spec here: https://jsonapi.org/format/ - DESC - - spec.homepage = "https://github.com/mattpolzin/JSONAPI" - # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" - - - # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Licensing your code is important. See https://choosealicense.com for more info. - # CocoaPods will detect a license file if there is a named LICENSE* - # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. - # - - # spec.license = "MIT" - spec.license = { :type => "MIT", :file => "LICENSE.txt" } - - - # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Specify the authors of the library, with email addresses. Email addresses - # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also - # accepts just a name if you'd rather not provide an email address. - # - # Specify a social_media_url where others can refer to, for example a twitter - # profile URL. - # - - spec.author = { "Mathew Polzin" => "matt.polzin@gmail.com" } - # Or just: spec.author = "Mathew Polzin" - # spec.authors = { "Mathew Polzin" => "matt.polzin@gmail.com" } - # spec.social_media_url = "https://twitter.com/Mathew Polzin" - - # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # If this Pod runs only on iOS or OS X, then specify the platform and - # the deployment target. You can optionally include the target after the platform. - # - - # spec.platform = :ios - # spec.platform = :ios, "5.0" - - # When using multiple platforms - spec.ios.deployment_target = "8.0" - spec.osx.deployment_target = "10.9" - # spec.watchos.deployment_target = "2.0" - # spec.tvos.deployment_target = "9.0" - - - # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Specify the location from where the source should be retrieved. - # Supports git, hg, bzr, svn and HTTP. - # - - spec.source = { :git => "https://github.com/mattpolzin/JSONAPI.git", :tag => "#{spec.version}" } - - - # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # CocoaPods is smart about how it includes source code. For source files - # giving a folder will include any swift, h, m, mm, c & cpp files. - # For header files it will include any header in the folder. - # Not including the public_header_files will make all headers public. - # - - spec.source_files = "Sources/JSONAPI/**/*.{swift}" - # spec.exclude_files = "Classes/Exclude" - - # spec.public_header_files = "Classes/**/*.h" - - - # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # A list of resources included with the Pod. These are copied into the - # target bundle with a build phase script. Anything else will be cleaned. - # You can preserve files from being cleaned, please don't preserve - # non-essential files like tests, examples and documentation. - # - - # spec.resource = "icon.png" - # spec.resources = "Resources/*.png" - - # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" - - - # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Link your library with frameworks, or libraries. Libraries do not include - # the lib prefix of their name. - # - - # spec.frameworks = "SomeFramework", "AnotherFramework" - - # spec.library = "iconv" - # spec.libraries = "iconv", "xml2" - - - # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # If your library depends on compiler flags you can set them in the xcconfig hash - # where they will only apply to your library. If you depend on other Podspecs - # you can include multiple dependencies to ensure it works. - - spec.swift_version = "5.2" - spec.module_name = "JSONAPI" - # spec.requires_arc = true - - # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } - spec.dependency "Poly", "~> 2.6" - -end diff --git a/Package.resolved b/Package.resolved index f7c521e..f0e1825 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,16 +1,15 @@ { - "object": { - "pins": [ - { - "package": "Poly", - "repositoryURL": "https://github.com/mattpolzin/Poly.git", - "state": { - "branch": null, - "revision": "99e2e8b575620369be52fe348c0dd72028e3674c", - "version": "2.8.0" - } + "originHash" : "54aae326cbf0090b80ab7860957be502f5396744f5579cf522be7cb3ffa67caf", + "pins" : [ + { + "identity" : "poly", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mattpolzin/Poly.git", + "state" : { + "revision" : "37c942daa23ab373ce05ef87f42103342e59cf3a", + "version" : "3.0.0" } - ] - }, - "version": 1 + } + ], + "version" : 3 } diff --git a/Package.swift b/Package.swift index 81b978c..642091f 100644 --- a/Package.swift +++ b/Package.swift @@ -1,12 +1,12 @@ -// swift-tools-version:5.2 +// swift-tools-version:6.0 import PackageDescription let package = Package( name: "JSONAPI", platforms: [ - .macOS(.v10_10), - .iOS(.v10) + .macOS(.v10_15), + .iOS(.v13) ], products: [ .library( @@ -17,7 +17,7 @@ let package = Package( targets: ["JSONAPITesting"]) ], dependencies: [ - .package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.8.0")), + .package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "3.0.0")), ], targets: [ .target( @@ -33,5 +33,5 @@ let package = Package( name: "JSONAPITestingTests", dependencies: ["JSONAPI", "JSONAPITesting"]) ], - swiftLanguageVersions: [.v5] + swiftLanguageModes: [.v5, .v6] ) diff --git a/README.md b/README.md index 67946cd..873a186 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # JSONAPI -[![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) [![Swift 5.2+](http://img.shields.io/badge/Swift-5.2+-blue.svg)](https://swift.org) [![Tests](https://github.com/mattpolzin/JSONAPI/actions/workflows/tests.yml/badge.svg)](https://github.com/mattpolzin/JSONAPI/actions/workflows/tests.yml) +[![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) [![Swift 6.0+](http://img.shields.io/badge/Swift-6.0+-blue.svg)](https://swift.org) [![Tests](https://github.com/mattpolzin/JSONAPI/actions/workflows/tests.yml/badge.svg)](https://github.com/mattpolzin/JSONAPI/actions/workflows/tests.yml) A Swift package for encoding to- and decoding from **JSON API** compliant requests and responses. @@ -58,35 +58,15 @@ If you find something wrong with this library and it isn't already mentioned und ## Dev Environment ### Prerequisites -1. Swift 5.2+ -2. Swift Package Manager, Xcode 11+, or Cocoapods +1. Swift 6.0+ +2. Swift Package Manager ### Swift Package Manager Just include the following in your package's dependencies and add `JSONAPI` to the dependencies for any of your targets. ```swift -.package(url: "https://github.com/mattpolzin/JSONAPI.git", from: "5.1.0") +.package(url: "https://github.com/mattpolzin/JSONAPI.git", from: "6.0.0") ``` -### Xcode project -With Xcode 11+, you can open the folder containing this repository. There is no need for an Xcode project, but you can generate one with `swift package generate-xcodeproj`. - -### CocoaPods -To use this framework in your project via Cocoapods, add the following dependencies to your Podfile. -```ruby -pod 'Poly', :git => 'https://github.com/mattpolzin/Poly.git' -pod 'MP-JSONAPI', :git => 'https://github.com/mattpolzin/JSONAPI.git' -``` - -### Carthage -This library does not support the Carthage package manager. This is intentional to avoid an additional dependency on Xcode and the Xcode's project files as their format changes throughout versions (in addition to the complexity of maintaining different shared schemes for each supported operating system). - -The difference between supporting and not supporting Carthage is the difference between maintaining an Xcode project with at least one shared build scheme; I encourage those that need Carthage support to fork this repository and add support to their fork by committing an Xcode project (you can generate one as described in the [Xcode project](#xcode-project) section above). Once an Xcode project is generated, you need to mark at least one scheme as [shared](https://github.com/Carthage/Carthage#share-your-xcode-schemes). - -### Running the Playground -To run the included Playground files, create an Xcode project using Swift Package Manager, then create an Xcode Workspace in the root of the repository and add both the generated Xcode project and the playground to the Workspace. - -Note that Playground support for importing non-system Frameworks is still a bit touchy as of Swift 4.2. Sometimes building, cleaning and building, or commenting out and then uncommenting import statements (especially in the` Entities.swift` Playground Source file) can get things working for me when I am getting an error about `JSONAPI` not being found. - ## Deeper Dive - [Project Status](./documentation/project-status.md) - [Server & Client Example](./documentation/examples/client-server-example.md) From 07503f207408a9604f59bf7ce759cbb72c58dc59 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 13 Sep 2025 17:10:28 -0500 Subject: [PATCH 2/5] a few less controversial changes for starters --- Sources/JSONAPI/Error/BasicJSONAPIError.swift | 6 ++++-- Sources/JSONAPI/Error/GenericJSONAPIError.swift | 2 +- Sources/JSONAPI/JSONAPICodingError.swift | 2 +- Sources/JSONAPI/Resource/Id.swift | 2 +- Sources/JSONAPI/Resource/Poly+PrimaryResource.swift | 6 ++++-- .../Resource Object/ResourceObjectDecodingError.swift | 4 ++-- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Sources/JSONAPI/Error/BasicJSONAPIError.swift b/Sources/JSONAPI/Error/BasicJSONAPIError.swift index 2c18e69..b966229 100644 --- a/Sources/JSONAPI/Error/BasicJSONAPIError.swift +++ b/Sources/JSONAPI/Error/BasicJSONAPIError.swift @@ -39,7 +39,7 @@ public struct BasicJSONAPIErrorPayload: Codable, Eq self.source = source } - public struct Source: Codable, Equatable { + public struct Source: Codable, Equatable, Sendable { /// a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. "/data" for a primary data object, or "/data/attributes/title" for a specific attribute]. public let pointer: String? /// which URI query parameter caused the error @@ -70,6 +70,8 @@ public struct BasicJSONAPIErrorPayload: Codable, Eq } } +extension BasicJSONAPIErrorPayload: Sendable where IdType: Sendable {} + /// `BasicJSONAPIError` optionally decodes many possible fields /// specified by the JSON:API 1.0 Spec. It gives no type-guarantees of what /// will be non-nil, but could provide good diagnostic information when @@ -100,4 +102,4 @@ public struct BasicJSONAPIErrorPayload: Codable, Eq /// with non-nil values in a flattened way. There will be no `source` key /// but there will be `pointer` and `parameter` keys (if those values /// are non-nil). -public typealias BasicJSONAPIError = GenericJSONAPIError> +public typealias BasicJSONAPIError = GenericJSONAPIError> diff --git a/Sources/JSONAPI/Error/GenericJSONAPIError.swift b/Sources/JSONAPI/Error/GenericJSONAPIError.swift index 473caac..457e4f7 100644 --- a/Sources/JSONAPI/Error/GenericJSONAPIError.swift +++ b/Sources/JSONAPI/Error/GenericJSONAPIError.swift @@ -8,7 +8,7 @@ /// `GenericJSONAPIError` can be used to specify whatever error /// payload you expect to need to parse in responses and handle any /// other payload structure as `.unknownError`. -public enum GenericJSONAPIError: JSONAPIError, CustomStringConvertible { +public enum GenericJSONAPIError: JSONAPIError, CustomStringConvertible { case unknownError case error(ErrorPayload) diff --git a/Sources/JSONAPI/JSONAPICodingError.swift b/Sources/JSONAPI/JSONAPICodingError.swift index da0705d..8cf889b 100644 --- a/Sources/JSONAPI/JSONAPICodingError.swift +++ b/Sources/JSONAPI/JSONAPICodingError.swift @@ -13,7 +13,7 @@ public enum JSONAPICodingError: Swift.Error { case missingOrMalformedMetadata(path: [CodingKey]) case missingOrMalformedLinks(path: [CodingKey]) - public enum Quantity: String, Equatable { + public enum Quantity: String, Equatable, Sendable { case one case many diff --git a/Sources/JSONAPI/Resource/Id.swift b/Sources/JSONAPI/Resource/Id.swift index 2fc4bac..fd05123 100644 --- a/Sources/JSONAPI/Resource/Id.swift +++ b/Sources/JSONAPI/Resource/Id.swift @@ -31,7 +31,7 @@ extension String: RawIdType {} /// A type that can be used as the `MaybeRawId` for a `ResourceObject` that does not /// have an Id (most likely because it was created by a client and the server will be responsible /// for assigning it an Id). -public struct Unidentified: MaybeRawId, CustomStringConvertible { +public struct Unidentified: MaybeRawId, CustomStringConvertible, Sendable { public init() {} public var description: String { return "Unidentified" } diff --git a/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift b/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift index 1443610..95f04a7 100644 --- a/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift +++ b/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift @@ -15,12 +15,12 @@ import Poly /// disparate types under one roof for /// the purposes of JSON API compliant /// encoding or decoding. -public typealias EncodableJSONPoly = Poly & EncodablePrimaryResource +public typealias EncodableJSONPoly = Poly & EncodablePrimaryResource & Sendable public typealias EncodablePolyWrapped = Encodable & Equatable public typealias CodablePolyWrapped = EncodablePolyWrapped & Decodable -extension Poly0: CodablePrimaryResource { +extension Poly0: @retroactive Encodable, @retroactive Decodable { public init(from decoder: Decoder) throws { throw JSONAPICodingError.illegalDecoding("Attempted to decode Poly0, which should represent a thing that is not expected to be found in a document.", path: decoder.codingPath) } @@ -30,6 +30,8 @@ extension Poly0: CodablePrimaryResource { } } +extension Poly0: CodablePrimaryResource {} + // MARK: - 1 type extension Poly1: EncodablePrimaryResource, OptionalEncodablePrimaryResource where A: EncodablePolyWrapped {} diff --git a/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift b/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift index 004e03c..d458605 100644 --- a/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift +++ b/Sources/JSONAPI/Resource/Resource Object/ResourceObjectDecodingError.swift @@ -13,7 +13,7 @@ public struct ResourceObjectDecodingError: Swift.Error, Equatable { static let entireObject = "entire object" - public enum Cause: Equatable { + public enum Cause: Equatable, Sendable { case keyNotFound case valueNotFound case typeMismatch(expectedTypeName: String) @@ -26,7 +26,7 @@ public struct ResourceObjectDecodingError: Swift.Error, Equatable { } } - public enum Location: String, Equatable { + public enum Location: String, Equatable, Sendable { case attributes case relationships case relationshipType From 8f66c9247dff207b0bdee1bece897140b304e50a Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 13 Sep 2025 17:17:07 -0500 Subject: [PATCH 3/5] silence some retroactive conformance warnings where they are acceptable risks --- Sources/JSONAPITesting/Attribute+Literal.swift | 6 +++--- Sources/JSONAPITesting/Optional+Literal.swift | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/JSONAPITesting/Attribute+Literal.swift b/Sources/JSONAPITesting/Attribute+Literal.swift index b631bab..f817a32 100644 --- a/Sources/JSONAPITesting/Attribute+Literal.swift +++ b/Sources/JSONAPITesting/Attribute+Literal.swift @@ -39,7 +39,7 @@ extension Attribute: ExpressibleByFloatLiteral where RawValue: ExpressibleByFloa } } -extension Optional: ExpressibleByFloatLiteral where Wrapped: ExpressibleByFloatLiteral { +extension Optional: @retroactive ExpressibleByFloatLiteral where Wrapped: ExpressibleByFloatLiteral { public typealias FloatLiteralType = Wrapped.FloatLiteralType public init(floatLiteral value: FloatLiteralType) { @@ -55,7 +55,7 @@ extension Attribute: ExpressibleByBooleanLiteral where RawValue: ExpressibleByBo } } -extension Optional: ExpressibleByBooleanLiteral where Wrapped: ExpressibleByBooleanLiteral { +extension Optional: @retroactive ExpressibleByBooleanLiteral where Wrapped: ExpressibleByBooleanLiteral { public typealias BooleanLiteralType = Wrapped.BooleanLiteralType public init(booleanLiteral value: BooleanLiteralType) { @@ -71,7 +71,7 @@ extension Attribute: ExpressibleByIntegerLiteral where RawValue: ExpressibleByIn } } -extension Optional: ExpressibleByIntegerLiteral where Wrapped: ExpressibleByIntegerLiteral { +extension Optional: @retroactive ExpressibleByIntegerLiteral where Wrapped: ExpressibleByIntegerLiteral { public typealias IntegerLiteralType = Wrapped.IntegerLiteralType public init(integerLiteral value: IntegerLiteralType) { diff --git a/Sources/JSONAPITesting/Optional+Literal.swift b/Sources/JSONAPITesting/Optional+Literal.swift index 8c87fab..f0e03d6 100644 --- a/Sources/JSONAPITesting/Optional+Literal.swift +++ b/Sources/JSONAPITesting/Optional+Literal.swift @@ -5,7 +5,7 @@ // Created by Mathew Polzin on 11/29/18. // -extension Optional: ExpressibleByUnicodeScalarLiteral where Wrapped: ExpressibleByUnicodeScalarLiteral { +extension Optional: @retroactive ExpressibleByUnicodeScalarLiteral where Wrapped: ExpressibleByUnicodeScalarLiteral { public typealias UnicodeScalarLiteralType = Wrapped.UnicodeScalarLiteralType public init(unicodeScalarLiteral value: UnicodeScalarLiteralType) { @@ -13,7 +13,7 @@ extension Optional: ExpressibleByUnicodeScalarLiteral where Wrapped: Expressible } } -extension Optional: ExpressibleByExtendedGraphemeClusterLiteral where Wrapped: ExpressibleByExtendedGraphemeClusterLiteral { +extension Optional: @retroactive ExpressibleByExtendedGraphemeClusterLiteral where Wrapped: ExpressibleByExtendedGraphemeClusterLiteral { public typealias ExtendedGraphemeClusterLiteralType = Wrapped.ExtendedGraphemeClusterLiteralType public init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) { @@ -21,7 +21,7 @@ extension Optional: ExpressibleByExtendedGraphemeClusterLiteral where Wrapped: E } } -extension Optional: ExpressibleByStringLiteral where Wrapped: ExpressibleByStringLiteral { +extension Optional: @retroactive ExpressibleByStringLiteral where Wrapped: ExpressibleByStringLiteral { public typealias StringLiteralType = Wrapped.StringLiteralType public init(stringLiteral value: StringLiteralType) { From f2e3317242eb53c822f284995f4f8d8b0f03fbe2 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 13 Sep 2025 17:41:03 -0500 Subject: [PATCH 4/5] and now to conditionally apply sendable more broadly --- Sources/JSONAPI/Document/APIDescription.swift | 4 ++++ Sources/JSONAPI/Document/Document.swift | 15 +++++++++++++++ Sources/JSONAPI/Document/Includes.swift | 2 ++ Sources/JSONAPI/Document/ResourceBody.swift | 6 ++++++ Sources/JSONAPI/Meta/Links.swift | 4 +++- Sources/JSONAPI/Meta/Meta.swift | 2 +- Sources/JSONAPI/Resource/Attribute.swift | 4 ++++ Sources/JSONAPI/Resource/Id.swift | 2 ++ .../JSONAPI/Resource/Poly+PrimaryResource.swift | 2 +- Sources/JSONAPI/Resource/Relationship.swift | 15 +++++++++++++++ .../Resource/Resource Object/ResourceObject.swift | 11 +++++++++-- .../Comparisons/IncludesCompareTests.swift | 4 ++-- 12 files changed, 64 insertions(+), 7 deletions(-) diff --git a/Sources/JSONAPI/Document/APIDescription.swift b/Sources/JSONAPI/Document/APIDescription.swift index cb417a6..123d056 100644 --- a/Sources/JSONAPI/Document/APIDescription.swift +++ b/Sources/JSONAPI/Document/APIDescription.swift @@ -21,6 +21,8 @@ public struct APIDescription: APIDescriptionType { } } +extension APIDescription: Sendable where Meta: Sendable {} + /// Can be used as `APIDescriptionType` for Documents that do not /// have any API Description (a.k.a. "JSON:API Object"). public struct NoAPIDescription: APIDescriptionType, CustomStringConvertible { @@ -33,6 +35,8 @@ public struct NoAPIDescription: APIDescriptionType, CustomStringConvertible { public var description: String { return "No JSON:API Object" } } +extension NoAPIDescription: Sendable {} + extension APIDescription { private enum CodingKeys: String, CodingKey { case version diff --git a/Sources/JSONAPI/Document/Document.swift b/Sources/JSONAPI/Document/Document.swift index 3673493..402cc6e 100644 --- a/Sources/JSONAPI/Document/Document.swift +++ b/Sources/JSONAPI/Document/Document.swift @@ -186,6 +186,10 @@ public struct Document(_ other: Document.Body.Data, combiningMetaWith metaMerge: (MetaType, MetaType) -> MetaType, diff --git a/Sources/JSONAPI/Document/Includes.swift b/Sources/JSONAPI/Document/Includes.swift index b76225e..8eb5000 100644 --- a/Sources/JSONAPI/Document/Includes.swift +++ b/Sources/JSONAPI/Document/Includes.swift @@ -45,6 +45,8 @@ public struct Includes: Encodable, Equatable { } } +extension Includes: Sendable where I: Sendable {} + extension Includes: Decodable where I: Decodable { public init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() diff --git a/Sources/JSONAPI/Document/ResourceBody.swift b/Sources/JSONAPI/Document/ResourceBody.swift index 0986a4f..88b8d44 100644 --- a/Sources/JSONAPI/Document/ResourceBody.swift +++ b/Sources/JSONAPI/Document/ResourceBody.swift @@ -65,6 +65,8 @@ public struct SingleResourceBody: Equatable, Coda } } +extension Link: Sendable where Meta: Sendable, URL: Sendable {} + extension Link where Meta == NoMetadata { public init(url: URL) { self.init(url: url, meta: .none) diff --git a/Sources/JSONAPI/Meta/Meta.swift b/Sources/JSONAPI/Meta/Meta.swift index 7018cd6..51aec42 100644 --- a/Sources/JSONAPI/Meta/Meta.swift +++ b/Sources/JSONAPI/Meta/Meta.swift @@ -21,7 +21,7 @@ extension Optional: Meta where Wrapped: Meta {} /// Use this type when you want to specify not to encode or decode any metadata /// for a type. -public struct NoMetadata: Meta, CustomStringConvertible { +public struct NoMetadata: Meta, CustomStringConvertible, Sendable { public static var none: NoMetadata { return NoMetadata() } public init() { } diff --git a/Sources/JSONAPI/Resource/Attribute.swift b/Sources/JSONAPI/Resource/Attribute.swift index 5b0ed41..e39b884 100644 --- a/Sources/JSONAPI/Resource/Attribute.swift +++ b/Sources/JSONAPI/Resource/Attribute.swift @@ -34,6 +34,8 @@ public struct TransformedAttribute { // If we are using the identity transform, we can skip the transform and guarantee no // error is thrown. @@ -82,6 +84,8 @@ public struct Attribute: AttributeType { } } +extension Attribute: Sendable where RawValue: Sendable {} + extension Attribute: CustomStringConvertible { public var description: String { return "Attribute<\(String(describing: RawValue.self))>(\(String(describing: value)))" diff --git a/Sources/JSONAPI/Resource/Id.swift b/Sources/JSONAPI/Resource/Id.swift index fd05123..d8a727c 100644 --- a/Sources/JSONAPI/Resource/Id.swift +++ b/Sources/JSONAPI/Resource/Id.swift @@ -97,6 +97,8 @@ public struct Id: Equa } } +extension Id: Sendable where RawType: Sendable {} + extension Id: Hashable where RawType: RawIdType { public func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(Self.self)) diff --git a/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift b/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift index 95f04a7..ef2a340 100644 --- a/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift +++ b/Sources/JSONAPI/Resource/Poly+PrimaryResource.swift @@ -15,7 +15,7 @@ import Poly /// disparate types under one roof for /// the purposes of JSON API compliant /// encoding or decoding. -public typealias EncodableJSONPoly = Poly & EncodablePrimaryResource & Sendable +public typealias EncodableJSONPoly = Poly & EncodablePrimaryResource public typealias EncodablePolyWrapped = Encodable & Equatable public typealias CodablePolyWrapped = EncodablePolyWrapped & Decodable diff --git a/Sources/JSONAPI/Resource/Relationship.swift b/Sources/JSONAPI/Resource/Relationship.swift index 4a2c3fa..bccf453 100644 --- a/Sources/JSONAPI/Resource/Relationship.swift +++ b/Sources/JSONAPI/Resource/Relationship.swift @@ -60,6 +60,12 @@ public struct ToOneRelationship let age: Attribute let favoriteColor: Attribute } - struct Relationships: JSONAPI.Relationships { + struct Relationships: JSONAPI.Relationships & Sendable { let bestFriend: ToOneRelationship let parents: ToManyRelationship } From 1e6b5312f323b0247570e7126c9ae7e76194b400 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 13 Sep 2025 17:51:22 -0500 Subject: [PATCH 5/5] fixing test types --- .../Comparisons/DocumentCompareTests.swift | 4 ++-- .../Test Helpers/String+CreatableRawIdType.swift | 2 +- Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift | 2 +- Tests/JSONAPITests/Error/GenericJSONAPIErrorTests.swift | 2 +- .../JSONAPITests/Test Helpers/String+CreatableRawIdType.swift | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift b/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift index dd05fe3..a06c7d8 100644 --- a/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift +++ b/Tests/JSONAPITestingTests/Comparisons/DocumentCompareTests.swift @@ -143,7 +143,7 @@ fileprivate typealias TestType2 = ResourceObject, NoMetadata, NoLinks, Include2, NoAPIDescription, BasicJSONAPIError> -fileprivate struct TestMetadata: JSONAPI.Meta, CustomStringConvertible { +fileprivate struct TestMetadata: JSONAPI.Meta, CustomStringConvertible, Sendable { let total: Int var description: String { @@ -151,7 +151,7 @@ fileprivate struct TestMetadata: JSONAPI.Meta, CustomStringConvertible { } } -fileprivate struct TestLinks: JSONAPI.Links { +fileprivate struct TestLinks: JSONAPI.Links, Sendable { let link: Link } diff --git a/Tests/JSONAPITestingTests/Test Helpers/String+CreatableRawIdType.swift b/Tests/JSONAPITestingTests/Test Helpers/String+CreatableRawIdType.swift index dd3c8f7..282325a 100644 --- a/Tests/JSONAPITestingTests/Test Helpers/String+CreatableRawIdType.swift +++ b/Tests/JSONAPITestingTests/Test Helpers/String+CreatableRawIdType.swift @@ -7,7 +7,7 @@ import JSONAPI -private var uniqueStringCounter = 0 +nonisolated(unsafe) private var uniqueStringCounter = 0 extension String: CreatableRawIdType { public static func unique() -> String { diff --git a/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift b/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift index 0100a07..8783794 100644 --- a/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift +++ b/Tests/JSONAPITests/Document/DocumentDecodingErrorTests.swift @@ -224,7 +224,7 @@ extension DocumentDecodingErrorTests { case unknownError case basic(BasicError) - struct BasicError: Codable, Equatable { + struct BasicError: Codable, Equatable, Sendable { let code: Int let description: String } diff --git a/Tests/JSONAPITests/Error/GenericJSONAPIErrorTests.swift b/Tests/JSONAPITests/Error/GenericJSONAPIErrorTests.swift index d645c23..6f095cc 100644 --- a/Tests/JSONAPITests/Error/GenericJSONAPIErrorTests.swift +++ b/Tests/JSONAPITests/Error/GenericJSONAPIErrorTests.swift @@ -132,7 +132,7 @@ final class GenericJSONAPIErrorTests: XCTestCase { } } -private struct TestPayload: Codable, Equatable, ErrorDictType { +private struct TestPayload: Codable, Equatable, ErrorDictType, Sendable { let hello: String let world: Int? diff --git a/Tests/JSONAPITests/Test Helpers/String+CreatableRawIdType.swift b/Tests/JSONAPITests/Test Helpers/String+CreatableRawIdType.swift index dd3c8f7..282325a 100644 --- a/Tests/JSONAPITests/Test Helpers/String+CreatableRawIdType.swift +++ b/Tests/JSONAPITests/Test Helpers/String+CreatableRawIdType.swift @@ -7,7 +7,7 @@ import JSONAPI -private var uniqueStringCounter = 0 +nonisolated(unsafe) private var uniqueStringCounter = 0 extension String: CreatableRawIdType { public static func unique() -> String {