Compare commits

...

16 Commits

Author SHA1 Message Date
Mathew Polzin 491fe1fbea clarifying name change in tests. update documentation around entity direct subscript accessor 2019-04-18 22:54:08 -07:00
Mathew Polzin 6b6f40c968 breaking change: change spelling of Entity subscript accessor that returns values of properties within Attributes without digging into the AttributeType. This change will make it possible for the compiler to unambiguously determine the type of subscript access being made before the value is stored, cast, or compared. 2019-04-18 22:40:12 -07:00
Mathew Polzin 14bd29bf42 Update podspec in expectance of breaking change. 2019-04-18 22:37:58 -07:00
Mathew Polzin cf4d8fc378 Update README.md 2019-04-16 22:39:33 -07:00
Mathew Polzin 10b756f6db Update README.md 2019-04-16 22:37:35 -07:00
Mathew Polzin 4cd697aae4 Merge pull request #17 from mattpolzin/swift-5
Default to Swift version 5.0 and Swift Tools version 5.0
2019-04-16 22:35:11 -07:00
Mathew Polzin db0f7329e4 Comment out playground code that is failing in Xcode 10.2 2019-04-16 22:11:09 -07:00
Mathew Polzin fb710c397b Entirely remove Result type that not only is unneeded with Swift 5 but also was not being used by this library (it was left over from before Poly became its own library). Fix a few redundent public warnings. 2019-04-16 21:56:49 -07:00
Mathew Polzin b1adc60719 Update package and podspec files 2019-04-16 21:52:50 -07:00
Mathew Polzin 423e61b285 Merge branch 'master' into swift-5 2019-04-16 21:42:17 -07:00
Mathew Polzin 895d575cdb Fix indentation in README 2019-04-14 21:03:14 -07:00
Mathew Polzin 16004051e9 Update language version to 5.0 and specify deployment targets for iOS and Mac OS. 2019-02-17 15:13:02 -08:00
Mathew Polzin 864a66f1ea Merge remote-tracking branch 'origin/master' into swift-5 2019-02-17 14:43:19 -08:00
Mathew Polzin 0c7bf9a92d Merge branch 'master' into swift-5 2019-01-29 16:42:59 -08:00
Mathew Polzin b294cec542 Merge remote-tracking branch 'origin/master' into swift-5 2019-01-08 21:27:39 -08:00
Mathew Polzin 11e8d13f02 Switch package to swift 5, fix warnings. 2019-01-08 20:47:12 -08:00
10 changed files with 41 additions and 78 deletions
@@ -49,11 +49,15 @@ print("-----")
// MARK: - Pass successfully parsed body to other parts of the code
/*
---- CRASHING IN XCODE 10.2 PLAYGROUND ----
if case let .data(bodyData) = peopleResponse.body {
print("first person's name: \(bodyData.primary.values[0][\.fullName])")
} else {
print("no body data")
}
*/
// MARK: - Work in the abstract
+1 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='macos' executeOnSourceChanges='false'>
<playground version='6.0' target-platform='macos'>
<pages>
<page name='Test Library'/>
<page name='Usage'/>
+3 -3
View File
@@ -16,7 +16,7 @@ Pod::Spec.new do |spec|
#
spec.name = "JSONAPI"
spec.version = "0.19.0"
spec.version = "0.21.0"
spec.summary = "Swift Codable JSON API framework."
# This description is used to generate tags and improve search results.
@@ -132,10 +132,10 @@ See the JSON API Spec here: https://jsonapi.org/format/
# 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 = "4.2"
spec.swift_version = "5.0"
# spec.requires_arc = true
# spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
spec.dependency "Poly", "~> 1.1"
spec.dependency "Poly", "~> 2.0"
end
+2 -2
View File
@@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/mattpolzin/Poly.git",
"state": {
"branch": null,
"revision": "77f45b8963a51c02d71fc4075eba5cff47ff0d07",
"version": "1.0.0"
"revision": "d24d4c1214dd05f89eb1182a46592856dd0a0645",
"version": "2.0.0"
}
}
]
+7 -3
View File
@@ -1,10 +1,14 @@
// swift-tools-version:4.2
// swift-tools-version:5.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "JSONAPI",
platforms: [
.macOS(.v10_10),
.iOS(.v10)
],
products: [
.library(
name: "JSONAPI",
@@ -14,7 +18,7 @@ let package = Package(
targets: ["JSONAPITesting"])
],
dependencies: [
.package(url: "https://github.com/mattpolzin/Poly.git", from: "1.0.0"),
.package(url: "https://github.com/mattpolzin/Poly.git", from: "2.0.0"),
],
targets: [
.target(
@@ -30,5 +34,5 @@ let package = Package(
name: "JSONAPITestingTests",
dependencies: ["JSONAPI", "JSONAPITesting"])
],
swiftLanguageVersions: [.v4_2]
swiftLanguageVersions: [.v5]
)
+6 -9
View File
@@ -1,5 +1,5 @@
# JSONAPI
[![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) [![Swift 4.2](http://img.shields.io/badge/Swift-4.2-blue.svg)](https://swift.org) [![Build Status](https://app.bitrise.io/app/c8295b9589aa401e/status.svg?token=vzcyqWD5bQ4xqQfZsaVzNw&branch=master)](https://app.bitrise.io/app/c8295b9589aa401e)
[![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) [![Swift 5.0](http://img.shields.io/badge/Swift-5.0-blue.svg)](https://swift.org) [![Build Status](https://app.bitrise.io/app/c8295b9589aa401e/status.svg?token=vzcyqWD5bQ4xqQfZsaVzNw&branch=master)](https://app.bitrise.io/app/c8295b9589aa401e)
A Swift package for encoding to- and decoding from **JSON API** compliant requests and responses.
@@ -90,7 +90,7 @@ If you find something wrong with this library and it isn't already mentioned und
To use this framework in your project via Cocoapods instead of Swift Package Manager, add the following dependencies to your Podfile.
```
pod 'Poly', :git => 'https://github.com/mattpolzin/Poly.git'
pod 'JSONAPI', :git => 'https://github.com/mattpolzin/JSONAPI.git'
pod 'JSONAPI', :git => 'https://github.com/mattpolzin/JSONAPI.git'
```
### Xcode project
@@ -348,11 +348,6 @@ typealias Attributes = NoAttributes
let favoriteColor: String = person[\.favoriteColor]
```
NOTE: Because of support for computed properties that are not wrapped in `Attribute`, `TransformedAttribute`, or `ValidatedAttribute`, the compiler cannot always infer the type of thing you want back when using subscript attribute access. The following code is ambiguous about whether it should return a `String` or an `Attribute<String>`:
```swift
let favoriteColor = person[\.favoriteColor]
```
#### `Transformer`
Sometimes you need to use a type that does not encode or decode itself in the way you need to represent it as a serialized JSON object. For example, the Swift `Foundation` type `Date` can encode/decode itself to `Double` out of the box, but you might want to represent dates as ISO 8601 compliant `String`s instead. The Foundation library `JSONDecoder` has a setting to make this adjustment, but for the sake of an example, you could create a `Transformer`.
@@ -391,7 +386,7 @@ You can also creator `Validators` and `ValidatedAttribute`s. A `Validator` is ju
#### Computed `Attribute`
You can add computed properties to your `EntityDescription.Attributes` struct if you would like to expose attributes that are not explicitly represented by the JSON. These computed properties do not have to be wrapped in `Attribute`, `ValidatedAttribute`, or `TransformedAttribute`. This allows computed attributes to be of types that are not `Codable`. Here's an example of how you might take the `Person[\.name]` attribute from the example above and create a `fullName` computed property.
You can add computed properties to your `EntityDescription.Attributes` struct if you would like to expose attributes that are not explicitly represented by the JSON. These computed properties do not have to be wrapped in `Attribute`, `ValidatedAttribute`, or `TransformedAttribute`. This allows computed attributes to be of types that are not `Codable`. Here's an example of how you might take the `person[\.name]` attribute from the example above and create a `fullName` computed property.
```swift
public var fullName: Attribute<String> {
@@ -399,6 +394,8 @@ public var fullName: Attribute<String> {
}
```
If your computed property is wrapped in a `AttributeType` then you can still use the default subscript operator to access it (as would be the case with the `person[\.fullName]` example above). However, if you add a property to the `Attributes` `struct` that is not wrapped in an `AttributeType`, you must either access it from its full path (`person.attributes.newThing`) or with the "direct" subscript accessor (`person[direct: \.newThing]`). This keeps the subscript access unambiguous enough for the compiler to be helpful prior to explicitly casting, comparing, or storing the result.
### Copying `Entities`
`Entity` is a value type, so copying is its default behavior. There are two common mutations you might want to make when copying an `Entity`:
1. Assigning a new `Identifier` to the copy of an identified `Entity`.
@@ -593,7 +590,7 @@ extension EntityDescription1.Attributes {
### Meta-Attributes
This advanced feature may not ever be useful, but if you find yourself in the situation of dealing with an API that does not 100% follow the **SPEC** then you might find meta-attributes are just the thing to make your entities more natural to work with.
Suppose, for example, you are presented with the unfortunate situation where a piece of information you need is only available as part of the `Id` of an entity. Perhaps a user's `Id` is formatted "{integer}-{createdAt}" where "createdAt" is the unix timestamp when the user account was created. The following `UserDescription` will expose what you need as an attribute. Realistically, this code is still terrible for its error handling. Using a `Result` type and/or invariants would clean things up substantially.
Suppose, for example, you are presented with the unfortunate situation where a piece of information you need is only available as part of the `Id` of an entity. Perhaps a user's `Id` is formatted "{integer}-{createdAt}" where "createdAt" is the unix timestamp when the user account was created. The following `UserDescription` will expose what you need as an attribute. Realistically, the following example code is still terrible for its error handling. Using a `Result` type and/or invariants would clean things up substantially.
```swift
enum UserDescription: EntityDescription {
+9 -6
View File
@@ -440,31 +440,34 @@ public extension Entity where EntityRawIdType: CreatableRawIdType {
public extension EntityProxy {
/// Access the attribute at the given keypath. This just
/// allows you to write `entity[\.propertyName]` instead
/// of `entity.relationships.propertyName`.
/// of `entity.attributes.propertyName`.
subscript<T: AttributeType>(_ path: KeyPath<Description.Attributes, T>) -> T.ValueType {
return attributes[keyPath: path].value
}
/// Access the attribute at the given keypath. This just
/// allows you to write `entity[\.propertyName]` instead
/// of `entity.relationships.propertyName`.
/// of `entity.attributes.propertyName`.
subscript<T: AttributeType>(_ path: KeyPath<Description.Attributes, T?>) -> T.ValueType? {
return attributes[keyPath: path]?.value
}
/// Access the attribute at the given keypath. This just
/// allows you to write `entity[\.propertyName]` instead
/// of `entity.relationships.propertyName`.
/// of `entity.attributes.propertyName`.
subscript<T: AttributeType, U>(_ path: KeyPath<Description.Attributes, T?>) -> U? where T.ValueType == U? {
// Implementation Note: Handles Transform that returns optional
// type.
return attributes[keyPath: path].flatMap { $0.value }
}
/// Access the computed attribute at the given keypath. This just
/// Access the storage of the attribute at the given keypath. This just
/// allows you to write `entity[\.propertyName]` instead
/// of `entity.relationships.propertyName`.
subscript<T>(_ path: KeyPath<Description.Attributes, T>) -> T {
/// of `entity.attributes.propertyName`.
/// Most of the subscripts dig into an `AttributeType`. This subscript
/// returns the `AttributeType` (or another type, if you are accessing
/// an attribute that is not stored in an `AttributeType`).
subscript<T>(direct path: KeyPath<Description.Attributes, T>) -> T {
// Implementation Note: Handles attributes that are not
// AttributeType. These should only exist as computed properties.
return attributes[keyPath: path]
-45
View File
@@ -1,45 +0,0 @@
//
// Result.swift
// JSONAPI
//
// Created by Mathew Polzin on 12/5/18.
//
enum Result<T, E: Swift.Error> {
case success(T)
case failure(E)
var value: T? {
guard case .success(let val) = self else {
return nil
}
return val
}
var error: E? {
guard case .failure(let err) = self else {
return nil
}
return err
}
func map<U>(_ transform: (T) -> U) -> Result<U, E> {
switch self {
case .failure(let err):
return .failure(err)
case .success(let val):
return .success(transform(val))
}
}
}
extension Result: CustomStringConvertible where T: CustomStringConvertible, E: CustomStringConvertible {
var description: String {
switch self {
case .success(let val):
return String(describing: val)
case .failure(let err):
return String(describing: err)
}
}
}
@@ -27,13 +27,13 @@ class ComputedPropertiesTests: XCTestCase {
let entity = decoded(type: TestType.self, data: computed_property_attribute)
XCTAssertEqual(entity[\.computed], "Sarah2")
XCTAssertEqual(entity[\.secretsOut], "shhhh")
XCTAssertEqual(entity[direct: \.directSecretsOut], "shhhh")
}
func test_ComputedNonAttributeAccess() {
let entity = decoded(type: TestType.self, data: computed_property_attribute)
XCTAssertEqual(entity[\.computed2], "Sarah2")
XCTAssertEqual(entity[direct: \.directComputed], "Sarah2")
}
func test_ComputedRelationshipAccess() {
@@ -56,11 +56,11 @@ extension ComputedPropertiesTests {
return name.map { $0 + "2" }
}
public var computed2: String {
public var directComputed: String {
return computed.value
}
public var secretsOut: String {
public var directSecretsOut: String {
return secret.value
}
}
+5 -5
View File
@@ -24,7 +24,7 @@ public class PolyProxyTests: XCTestCase {
XCTAssertEqual(polyUserA[\.name], "Ken Moore")
XCTAssertEqual(polyUserA.id, "1")
XCTAssertEqual(polyUserA.relationships, .none)
XCTAssertEqual(polyUserA[\.x], .init(x: "y"))
XCTAssertEqual(polyUserA[direct: \.x], .init(x: "y"))
}
func test_UserAAndBEncodeEquality() {
@@ -59,7 +59,7 @@ public class PolyProxyTests: XCTestCase {
XCTAssertEqual(polyUserB[\.name], "Ken Less")
XCTAssertEqual(polyUserB.id, "2")
XCTAssertEqual(polyUserB.relationships, .none)
XCTAssertEqual(polyUserB[\.x], .init(x: "y"))
XCTAssertEqual(polyUserB[direct: \.x], .init(x: "y"))
}
}
@@ -86,10 +86,10 @@ public extension PolyProxyTests {
public typealias Relationships = NoRelationships
}
public typealias UserA = BasicEntity<UserDescription1>
public typealias UserB = BasicEntity<UserDescription2>
typealias UserA = BasicEntity<UserDescription1>
typealias UserB = BasicEntity<UserDescription2>
public typealias User = Poly2<UserA, UserB>
typealias User = Poly2<UserA, UserB>
}
extension Poly2: EntityProxy, JSONTyped where A == PolyProxyTests.UserA, B == PolyProxyTests.UserB {