Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
mattpolzin/JSONAPI
JSONAPI
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/
Quick Start
:warning: The following Google Colab examples have correct code, but from time to time the Google Colab Swift compiler may be buggy and claim it cannot build the JSONAPI library.
Clientside
- Basic Example
- Compound Example
- Metadata Example
- Custom Errors Example
- PATCH Example
- Resource Storage Example (using JSONAPI-ResourceStorage)
Serverside
Client+Server
This library works well when used by both the server responsible for serialization and the client responsible for deserialization. Check out the example.
Table of Contents
Primary Goals
The primary goals of this framework are:
- Allow creation of Swift types that are easy to use in code but also can be encoded to- or decoded from JSON API v1.0 Spec compliant payloads without lots of boilerplate code.
- Leverage
Codable
to avoid additional outside dependencies and get operability with non-JSON encoders/decoders for free. - Do not sacrifice type safety.
- Be platform agnostic so that Swift code can be written once and used by both the client and the server.
- Provide human readable error output. The errors thrown when decoding an API response and the results of the
JSONAPITesting
framework'scompare(to:)
functions all have digestible human readable descriptions (just useString(describing:)
).
Caveat
The big caveat is that, although the aim is to support the JSON API spec, this framework ends up being naturally opinionated about certain things that the API Spec does not specify. These caveats are largely a side effect of attempting to write the library in a "Swifty" way.
If you find something wrong with this library and it isn't already mentioned under Project Status, let me know! I want to keep working towards a library implementation that is useful in any application.
Dev Environment
Prerequisites
- Swift 5.2+
- Swift Package Manager, Xcode 11+, or Cocoapods
Swift Package Manager
Just include the following in your package's dependencies and add JSONAPI
to the dependencies for any of your targets.
.package(url: "https://github.com/mattpolzin/JSONAPI.git", from: "5.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.
pod 'Poly', :git => 'https://github.com/mattpolzin/Poly.git'
pod 'MP-JSONAPI', :git => 'https://github.com/mattpolzin/JSONAPI.git'
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
JSONAPI+Testing
The JSONAPI
framework is packaged with a test library to help you test your JSONAPI
integration. The test library is called JSONAPITesting
. You can see JSONAPITesting
in action in the Playground included with the JSONAPI
repository.
Literal Expressibility
Literal expressibility for Attribute
, ToOneRelationship
, and Id
are provided so that you can easily write test ResourceObject
values into your unit tests.
For example, you could create a mock Author
(from the above example) as follows
let author = Author(
id: "1234", // You can just use a String directly as an Id
attributes: .init(name: "Janice Bluff"), // The name Attribute does not need to be initialized, you just use a String directly.
relationships: .none,
meta: .none,
links: .none
)
Resource Object check()
The ResourceObject
gets a check()
function that can be used to catch problems with your JSONAPI
structures that are not caught by Swift's type system.
To catch malformed JSONAPI.Attributes
and JSONAPI.Relationships
, just call check()
in your unit test functions:
func test_initAuthor() {
let author = Author(...)
Author.check(author)
}
Comparisons
You can compare Documents
, ResourceObjects
, Attributes
, etc. and get human-readable output using the compare(to:)
methods included with JSONAPITesting
.
func test_articleResponse() {
let endToEndAPITestResponse: SingleArticleDocumentWithIncludes = ...
let expectedResponse: SingleArticleDocumentWithIncludes = ...
let comparison = endToEndAPITestResponse.compare(to: expectedResponse)
XCTAssert(comparison.isSame, String(describing: comparison))
}
JSONAPI-Arbitrary
The JSONAPI-Arbitrary
library provides SwiftCheck
Arbitrary
conformance for many of the JSONAPI
types.
See https://github.com/mattpolzin/JSONAPI-Arbitrary for more information.
JSONAPI-OpenAPI
The JSONAPI-OpenAPI
library generates OpenAPI compliant JSON Schema for models built with the JSONAPI
library. If your Swift code is your preferred source of truth for API information, this is an easy way to document the response schemas of your API.
JSONAPI-OpenAPI
also has experimental support for generating JSONAPI
Swift code from Open API documentation (this currently lives on the feature/gen-swift
branch).
See https://github.com/mattpolzin/JSONAPI-OpenAPI for more information.
JSONAPI-ResourceStorage
The JSONAPI-ResourceStorage
package has two very early stage modules supporting storage and retrieval of JSONAPI.ResourceObjects
. Please consider these modules to be more of examples of two directions you could head in than anything else.
Github
link |
Stars: 52 |
Last commit: 1 week ago |
Related Packages
You may find interesting
Dependencies
Releases
5.0 - The More Meta The Merrier - 2020-09-26T20:21:05
See the release candidate notes for more information on the following: https://github.com/mattpolzin/JSONAPI/releases/tag/5.0.0-rc.1
Features & Improvements
Better error reporting
When there is a failure to decode includes
in a JSON:API document, version 5 will give you a much more concise error message. Where before it would always tell you all the possible ways the include might have failed to decode, it will now attempt to determine which failure is relevant using a couple of simple heuristics about very common cases.
If an include does not have a JSON:API type matching any of the types allowed to be included, the error will just tell you what type it found and what types it was looking for.
If an include that failed to decode matches one of the expected JSON:API types, the error will report specifically the reason why that type of resource could not be decoded.
Resource Identifier Object metadata
Previously, you could decode metadata that was found in the Relationship Object but not metadata that was found in the Resource Identifier Object nested within a Relationship Object. This gap in what the JSONAPI library can express has been fixed. See the release candidate notes or the JSON:API specification for more information on the distinction.
You can see more on using this new metadata location in this library's relationship metadata documentation.
⚠️ Breaking Changes ⚠️
ToOneRelationship
and ToManyRelationship
both gained an additional generic type parameter named IdMetaType
. This means that any code that uses them directly or typealiases them will need to be updated.
// before
ToOneRelationship<Identifiable: JSONAPI.JSONAPIIdentifiable, MetaType: JSONAPI.Meta, LinksType: JSONAPI.Links>
// after
ToOneRelationship<Identifiable: JSONAPI.JSONAPIIdentifiable, IdMetaType: JSONAPI.Meta, MetaType: JSONAPI.Meta, LinksType: JSONAPI.Links>
//before
ToManyRelationship<Relatable: JSONAPI.Relatable, MetaType: JSONAPI.Meta, LinksType: JSONAPI.Links>
//after
ToManyRelationship<Relatable: JSONAPI.Relatable, IdMetaType: JSONAPI.Meta, MetaType: JSONAPI.Meta, LinksType: JSONAPI.Links>
if you don't need to take advantage of this new metadata location, you can do away with it just like with any other option your relationship does not need. Here are two examples of typealiases that do away with all of the various auxiliary information:
typealias ToOne<T: JSONAPIIdentifiable> = JSONAPI.ToOneRelationship<T, NoIdMetadata, NoMetadata, NoLinks>
typealias ToMany<T: JSONAPI.Relatable> = JSONAPI.ToManyRelationship<T,NoIdMetadata, NoMetadata, NoLinks>