aronbalog / vox Goto Github PK
View Code? Open in Web Editor NEWSwift JSON:API client framework
Home Page: http://jsonapi.org
License: MIT License
Swift JSON:API client framework
Home Page: http://jsonapi.org
License: MIT License
I am not sure the userInfo is the best way to handle user-specific data.
In the documentation it says that userInfo is used for adding headers at run-time.
I think headers
should be an attribute of Request
and methods such as Request::addHeader
and Request::removeHeader
should be used to manipulate headers to be able to do something like
personDataSource
.fetch(1)
.addHeader("token", "YsbV6gL35d")
.result(/* [..] */)
At another extent, the Alamofire
Client does not uses the userData
for headers here
I would like to log any error in a text log system. As an error is not a resource, it is not possible to serialize it. We could imagine something like keeping the dictionary from the init()
and transform it as json string afterward.
Hi, I've tried to convert Data to a JSONAPIDocument struct, and it doesn't throw an error, but when I read documen.data
, all the Person
attributes return nil
. Any suggestion for this problem? Thanks
Here is sample of my code:
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
let deserializer = Deserializer.Collection<Person>()
do {
let document = try deserializer.deserialize(data: jsonData)
if let personData = document.data {
self?.person = personData
}
} catch JSONAPIError.API(let errors) {
errors.forEach { error in
}
} catch JSONAPIError.serialization {
print("Given data is not valid JSONAPI document")
} catch {
print("Something went wrong. Maybe `data` does not contain valid JSON?")
}
jsonObject
type is [String:Any]
Add userInfo dictionary for custom configuration of request
delete
Hi , I have a relationship and I want to set subset for it . How can I do that ?
You know I want POST my relationships and attributes .
We have a URL and we use it to create a request to a server . Cause of this , we should create a class and we have 2 attributes and 2 other attributes which they should be in our relationship as subsets .
I don't know how can I set it as code , How can I define attribute for relationship ????
here is the screenshot which I mean .
Could you help me to figure out how to set meta-information in the resource?
Hello,
Thanks for the library.
let dataSource = DataSource(strategy: .path("/"), client: client)
I'm trying to create a request but i still can't find what the "client" must be?The documentation is incomplete and can't find any information about it.
Best,
I am still waiting ... there is no library for json api and I am in force to use your library , please post an update and solve this bug
There is still 28% of devices running with iOS 10, the library should be compatible with those as there is nothing requiring iOS 11.
I've installed Vox using Cocoapods. But when I write:
let paginationStrategy = Pagination.PageBased(number: 1, size: 10)
the compiler show me the error: 'Use of unresolved identifier 'Pagination'; did you mean 'PaginationData'?'
In my opinion the reason of this issue that Pagination struct is not public. If I change this struct to public - everything is ok.
The Request::execute function is maked as thowing, but it is not throwing anything. In fact the try resource?.documentDictionary()
does not throw either, so the try
could be removed. Then the throws
for Request:result can be removed as well allowing to lighten calls to APIs
If you try to deserialize an object such as
"data": {
"attributes": {
"name": "John Doe",
"age": null,
"gender": "male"
},
"type": "persons",
"id": "id-1",
"relationships": {
"favoriteArticle": {
"data": {
"id": "article-identifier",
"type": "articles"
}
}
}
}
when trying to get person.favoritArticle.id
you are getting ""
because When fetching the attribute, the relationship is properly found and the Context tries to get the relationship In Context_Query but the ResourcePool has no knowledge of a favoriteArticle
with the ID article-identifier
, the the value is null...
I think it should be a nice feature to add the objects in the resourcePool either at the deserialization or even bette when a relationship exists and the object is not found, creating it.
In addition, a resource object MAY contain any of these top-level members:
links: a links object containing links related to the resource.
http://jsonapi.org/format/#document-resource-objects
This links object is currently not on Resource.swift
Hi dear Mr Vox 😁 .
I can't filter my objects while fetching from the server . You know I don't know where should I use it and how ?🧐 Could you please help me ???
It doesn't seems like Vox handles error with a JSONAPI format. From the specification an error object may look like:
{
"errors": [
{
"id": "BAD",
"links": {
"about": "https://my.api/kb/BAD"
},
"status": "400",
"code": "BAD-ERROR-CODE",
"title": "bad attribute",
"detail": "name can't be blank",
"source": {
"pointer": "/data/attributes/name"
},
"meta": {}
}
]
}
For now, on error the data is deserialized as a DataSource's resource type, see here.
I'm not sure if this is a bug or if it's an issue with how I'm using the lib, but I can't seem to get related objects to populate correctly.
Given the following JSON:
{
"data": [
{
"attributes": {
"arrival_time": "2019-01-02T09:27:37-05:00",
"departure_time": "2019-01-02T09:27:37-05:00",
"direction_id": 1,
"schedule_relationship": null,
"status": null,
"stop_sequence": 3
},
"id": "prediction-39288223-2844-3",
"relationships": {
"route": {
"data": {
"id": "93",
"type": "route"
}
},
"stop": {
"data": {
"id": "2844",
"type": "stop"
}
},
"trip": {
"data": {
"id": "39288223",
"type": "trip"
}
}
},
"type": "prediction"
},
{
"attributes": {
"arrival_time": "2019-01-02T09:41:32-05:00",
"departure_time": "2019-01-02T09:41:32-05:00",
"direction_id": 1,
"schedule_relationship": null,
"status": null,
"stop_sequence": 3
},
"id": "prediction-39288313-2844-3",
"relationships": {
"route": {
"data": {
"id": "93",
"type": "route"
}
},
"stop": {
"data": {
"id": "2844",
"type": "stop"
}
},
"trip": {
"data": {
"id": "39288313",
"type": "trip"
}
}
},
"type": "prediction"
},
{
"attributes": {
"arrival_time": "2019-01-02T09:56:32-05:00",
"departure_time": "2019-01-02T09:56:32-05:00",
"direction_id": 1,
"schedule_relationship": null,
"status": null,
"stop_sequence": 3
},
"id": "prediction-39288212-2844-3",
"relationships": {
"route": {
"data": {
"id": "93",
"type": "route"
}
},
"stop": {
"data": {
"id": "2844",
"type": "stop"
}
},
"trip": {
"data": {
"id": "39288212",
"type": "trip"
}
}
},
"type": "prediction"
},
{
"attributes": {
"arrival_time": "2019-01-02T10:14:11-05:00",
"departure_time": "2019-01-02T10:14:11-05:00",
"direction_id": 1,
"schedule_relationship": null,
"status": null,
"stop_sequence": 3
},
"id": "prediction-39288292-2844-3",
"relationships": {
"route": {
"data": {
"id": "93",
"type": "route"
}
},
"stop": {
"data": {
"id": "2844",
"type": "stop"
}
},
"trip": {
"data": {
"id": "39288292",
"type": "trip"
}
}
},
"type": "prediction"
}
],
"included": [
{
"attributes": {
"color": "FFC72C",
"description": "Local Bus",
"direction_destinations": [
"Sullivan",
"Downtown Boston"
],
"direction_names": [
"Outbound",
"Inbound"
],
"long_name": "Sullivan - Downtown Boston",
"short_name": "93",
"sort_order": 9300,
"text_color": "000000",
"type": 3
},
"id": "93",
"links": {
"self": "/routes/93"
},
"type": "route"
}
],
"jsonapi": {
"version": "1.0"
}
}
And these resource classes:
class MBTAPrediction: Resource {
@objc dynamic var arrivalTime: String?
@objc dynamic var route: MBTARoute?
override static var resourceType: String {
return "prediction"
}
override class var codingKeys: [String : String] {
return [
"arrivalTime": "arrival_time"
]
}
}
class MBTARoute: Resource {
@objc dynamic var long_name: String?
@objc dynamic var short_name: String?
override static var resourceType: String {
return "route"
}
}
The following code fails because the related route
object is nil.
guard let url = Bundle.main.url(forResource: "mbta_predictions", withExtension: "json"),
let data = try? Data(contentsOf: url) else {
fatalError()
}
let deserializer = Deserializer.Collection<MBTAPrediction>()
do {
let document = try deserializer.deserialize(data: data)
let predictions = document.data
let routeDesc = predictions?.first?.route?.long_name
// routeDesc should not be nil
} catch {
NSLog("Error deserializing MBTA response: \(error)")
}
@aronbalog I found that if I put a break point inside of the ResourcePool
where each resource is added and step through slowly, it makes the code work. So I think this maybe some sort of async race condition.
If I'm just doing something wrong, please let me know. Thanks!
Deserializer.Collection init is not exposed.
Hello Mr Balog. I’m using your Vox library , I have a question . I use it in whole of my project , I write repeating code every time and it’s not ok. Look at this attached swift file , this is my function that every time I use. Is there any way to write it once and call it every where I need and just change resource and method every time not to write all function , because I am doing same in on failure block every time .
I’ll be really happy if you help me dear Aron Balog .
let client = JSONAPIClient.Alamofire(baseURL: baseURL)
let dataSource = DataSource<resourse>(strategy: .path("/path"), client: client)
do{
try dataSource
.fetch().include(["something"])
.result({ (document: Document<[resourse]>) in
let results = document.data
// do some thing
}) { (error) in
// and do this every time
if let error = error as? JSONAPIError {
switch error {
case .API( let errors):
ApiResponse(response: errors)
default:()
}
return
}else{
ApiResponse(response: nil)
}
}
}catch let error_object{
print(error_object)
}
It'd be nice if the Alamofire client could be subclassed and functions overriden. My use-case is that I want to add pre/post-processing in request handling while using Alamofire client.
To do so, I think it would be a good idea to have a more-atomic control on the request process.
Hi! It'd be awesome to have support for importing the library via Carthage.
My response payload contains the following error:
{
"errors": {
"detail": {
"auth": [
"invalid password"
]
}
}
}
However Vox will always respond with JSONAPIError.serialization
due to not casting the error array correctly:
https://github.com/aronbalog/Vox/blob/master/Vox/Core/Class/Context.swift#L42
(lldb) po dictionary["errors"] as? NSArray
nil
(lldb) po dictionary["errors"] as? NSMutableArray
nil
(lldb) po type(of: dictionary["errors"])
Swift.Optional<Any>
(lldb) po dictionary["errors"]
▿ Optional<Any>
▿ some : 1 element
▿ 0 : 2 elements
- key : detail
▿ value : 1 element
▿ 0 : 2 elements
- key : auth
▿ value : 1 element
- 0 : invalid email or password
When trying to access a nested object my app crashes
let baseURL = Environment.backendURL.appendingPathComponent("api")
let dataSource = DataSource<ProductOfferResource>(strategy: .path(ProductOfferResource.resourceType), client: self.client)
try! dataSource.fetch()
.result({ (document: Document<[ProductOfferResource]>) in
let productOffers = document.data
observer.onNext(productOffers ?? [])
// Causes crash
let background = productOffers?[0].design?.background_image?.meta
observer.onCompleted()
}, { (error) in
observer.onError(error ?? NetworkServiceError.unknown)
})
All objects can be found https://gist.github.com/sadiq81/bf0e77a8561049ecd0a105479fc3ff5a
I want to implement ssl pinning in Vox , Could you please help me how to do it ?
Hi There -
I've got a JSONAPI compliant Rails server that I'm using with VOX on a pretty simple Swift app. When tying to create a resource as per the README, I'm running into issues unwrapping the server response which it appears VOX is wrapping in an optional. I've currently got the following:
func authenticate(auth: AuthResource, completion: @escaping (AuthResource) -> ()) {
let auth = AuthResource()
auth.email = emailField.text
auth.password = passwordField.text
try? dataSource.create(auth).result({ (document: Document<AuthResource>?) in
let auth = document?.data
completion(auth)
}) { (error) in
print(error!)
}
}
When I inspect the auth
variable I see the server response && document tree however I'm having trouble unwrapping and then gaining access to the variables from the server response. A simple p auth
returns nil and if I wrap the completion
statement it in an if let
it doesn't do anything.
I'm pretty new to Swift but hoping I can get this to work. Any help is appreciated!
For a resource with relationships, it should be possible to get a relationship route
"author": { "links": { "self": "http://example.com/articles/1/relationships/author", "related": "http://example.com/articles/1/author" }, "data": { "type": "people", "id": "9" } }
At another extent, the JSONAPI does not provides any specifications on routing. I think it'd be nice if we could build REST routes for nested/related resources see this post to a better example of what I mean.
The ResourcePool store resources, with a key that use id
and type
private func keyForBasicObject(_ basicObject: [String: String]) -> String {
return basicObject["id"]! + "_" + basicObject["type"]!
}
private func keyForResource(_ resource: Resource) -> String {
return resource.id! + "_" + resource.type
}
So, any Resource without an id
can not be stores. Problem is, when you want to create object and relation on client-side, it fails.
From the JSONAPI :
Resource Objects
“Resource objects” appear in a JSON API document to represent resources.
A resource object MUST contain at least the following top-level members:
id
type
Exception: Theid
member is not required when the resource object originates at the client and represents a new resource to be created on the server.
Such a Thing would not work :
import Vox
class Tire: Resource {
override class var resourceType: String {
return "tires"
}
}
class Car: Resource {
@objc dynamic
var tires: [Tire]?
override class var resourceType: String {
return "cars"
}
}
func createCar() {
let c = Car()
let t = Tire()
c.tires = [t]
}
I would suggest to add a default id
to a resource in ResourcePool::addResource if none is given by the user :
func addResource(_ resource: Resource) {
queue.async(flags: .barrier) {
if resource.id == nil {
resource.id = resource.internalIdentifier
}
self.mapTable.setObject(resource, forKey: self.keyForResource(resource) as NSString)
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.