Skip to content

Commit ba9c9f2

Browse files
author
Josh Holtz
committed
Merge branch 'jkarmstr-master'
2 parents 1b16399 + 99ff687 commit ba9c9f2

32 files changed

+1447
-617
lines changed

Classes/JSONAPI.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,20 @@
99
#import <Foundation/Foundation.h>
1010

1111
#import "JSONAPIResource.h"
12-
#import "JSONAPIResourceFormatter.h"
13-
#import "JSONAPIResourceModeler.h"
14-
#import "JSONAPIErrorResource.h"
1512

13+
/**
14+
* Represents a complete JSON-API formatted message body.
15+
*/
1616
@interface JSONAPI : NSObject
1717

18-
@property (nonatomic, strong, readonly) NSDictionary *meta;
19-
@property (nonatomic, strong, readonly) id data;
18+
/**
19+
* Returns Content-Type string for JSON-API
20+
*
21+
* @return Content-Type string for JSON-API
22+
*/
23+
+ (NSString*)MEDIA_TYPE;
24+
25+
@property (readonly) NSDictionary *meta;
2026
@property (nonatomic, strong, readonly) NSArray *errors;
2127

2228
@property (readonly) id resource;
@@ -28,9 +34,13 @@
2834
// Initializers
2935
+ (instancetype)jsonAPIWithDictionary:(NSDictionary *)dictionary;
3036
+ (instancetype)jsonAPIWithString:(NSString *)string;
37+
+ (instancetype)jsonAPIWithResource:(NSObject <JSONAPIResource> *)resource;
38+
3139
- (instancetype)initWithDictionary:(NSDictionary*)dictionary;
3240
- (instancetype)initWithString:(NSString*)string;
3341

42+
- (NSDictionary*)dictionary;
43+
3444
- (id)includedResource:(id)ID withType:(NSString*)type;
3545
- (BOOL)hasErrors;
3646

Classes/JSONAPI.m

Lines changed: 95 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
#import "JSONAPI.h"
1010

1111
#import "JSONAPIErrorResource.h"
12+
#import "JSONAPIResourceParser.h"
13+
#import "JSONAPIResourceDescriptor.h"
14+
15+
16+
static NSString *gMEDIA_TYPE = @"application/vnd.api+json";
1217

1318
@interface JSONAPI()
1419

@@ -20,6 +25,10 @@ @implementation JSONAPI
2025

2126
#pragma mark - Class
2227

28+
+ (NSString*)MEDIA_TYPE {
29+
return gMEDIA_TYPE;
30+
}
31+
2332
+ (instancetype)jsonAPIWithDictionary:(NSDictionary *)dictionary {
2433
return [[JSONAPI alloc] initWithDictionary:dictionary];
2534
}
@@ -28,8 +37,16 @@ + (instancetype)jsonAPIWithString:(NSString *)string {
2837
return [[JSONAPI alloc] initWithString:string];
2938
}
3039

40+
+ (instancetype)jsonAPIWithResource:(NSObject <JSONAPIResource> *)resource {
41+
return [[JSONAPI alloc] initWithResource:resource];
42+
}
43+
3144
#pragma mark - Instance
3245

46+
- (NSDictionary*)meta {
47+
return self.dictionary[@"meta"];
48+
}
49+
3350
- (instancetype)initWithDictionary:(NSDictionary*)dictionary {
3451
self = [super init];
3552
if (self) {
@@ -46,6 +63,14 @@ - (instancetype)initWithString:(NSString*)string {
4663
return self;
4764
}
4865

66+
-(instancetype)initWithResource:(NSObject <JSONAPIResource> *)resource {
67+
self = [super init];
68+
if (self) {
69+
[self inflateWithResource:resource];
70+
}
71+
return self;
72+
}
73+
4974
- (void)inflateWithString:(NSString*)string {
5075
id json = [NSJSONSerialization JSONObjectWithData:[string dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
5176

@@ -79,72 +104,99 @@ - (void)inflateWithDictionary:(NSDictionary*)dictionary {
79104
// Sets internal dictionary
80105
_dictionary = dictionary;
81106

82-
// Sets meta
83-
_meta = dictionary[@"meta"];
84-
if ([_meta isKindOfClass:[NSDictionary class]] == NO) {
85-
_meta = nil;
86-
}
87-
88107
// Parse resources
89-
_data = _dictionary[@"data"];
90-
91-
NSMutableArray *resources = @[].mutableCopy;
92-
if ([_data isKindOfClass:[NSArray class]] == YES) {
108+
id data = dictionary[@"data"];
109+
if ([data isKindOfClass:[NSArray class]] == YES) {
110+
_resources = [JSONAPIResourceParser parseResources:data];
93111

94-
NSArray *dataArray = (NSArray*) _data;
95-
for (NSDictionary *data in dataArray) {
96-
id resource = [self inflateResourceData:data];
97-
if (resource) [resources addObject:resource];
98-
}
99-
100-
} else if ([_data isKindOfClass:[NSDictionary class]] == YES) {
101-
id resource = [self inflateResourceData:_data];
102-
if (resource) [resources addObject:resource];
112+
} else if ([data isKindOfClass:[NSDictionary class]] == YES) {
113+
id resource = [JSONAPIResourceParser parseResource:data];
114+
_resources = [[NSArray alloc] initWithObjects:resource, nil];
103115
}
104-
_resources = resources;
105116

106117
// Parses included resources
107-
NSArray *included = _dictionary[@"included"];
108-
NSMutableDictionary *includedResources = @{}.mutableCopy;
109-
for (NSDictionary *data in included) {
110-
111-
JSONAPIResource *resource = [self inflateResourceData:data];
118+
id included = dictionary[@"included"];
119+
NSMutableDictionary *includedResources = [[NSMutableDictionary alloc] init];
120+
if ([included isKindOfClass:[NSArray class]] == YES) {
121+
for (NSDictionary *data in included) {
122+
123+
NSObject <JSONAPIResource> *resource = [JSONAPIResourceParser parseResource:data];
124+
if (resource) {
125+
JSONAPIResourceDescriptor *desc = [JSONAPIResourceDescriptor forLinkedType:data[@"type"]];
126+
127+
NSMutableDictionary *typeDict = includedResources[desc.type] ?: @{}.mutableCopy;
128+
typeDict[resource.ID] = resource;
129+
130+
includedResources[desc.type] = typeDict;
131+
}
132+
}
133+
} else if ([included isKindOfClass:[NSDictionary class]] == YES) {
134+
NSObject <JSONAPIResource> *resource = [JSONAPIResourceParser parseResource:included];
112135
if (resource) {
113-
114-
NSMutableDictionary *typeDict = includedResources[resource.type] ?: @{}.mutableCopy;
136+
JSONAPIResourceDescriptor *desc = [JSONAPIResourceDescriptor forLinkedType:data[@"type"]];
137+
138+
NSMutableDictionary *typeDict = includedResources[desc.type] ?: @{}.mutableCopy;
115139
typeDict[resource.ID] = resource;
116140

117-
includedResources[resource.type] = typeDict;
141+
includedResources[desc.type] = typeDict;
118142
}
119143
}
120144
_includedResources = includedResources;
121145

122146
// Link included with included
123147
for (NSDictionary *typeIncluded in _includedResources.allValues) {
124-
for (JSONAPIResource *resource in typeIncluded.allValues) {
125-
[resource linkWithIncluded:self];
148+
for (NSObject <JSONAPIResource> *resource in typeIncluded.allValues) {
149+
[JSONAPIResourceParser link:resource withIncluded:self];
126150
}
127151
}
128152

129153
// Link data with included
130-
for (JSONAPIResource *resource in _resources) {
131-
[resource linkWithIncluded:self];
154+
for (NSObject <JSONAPIResource> *resource in _resources) {
155+
[JSONAPIResourceParser link:resource withIncluded:self];
132156
}
133-
157+
134158
// Parse errors
135-
NSMutableArray *errors = @[].mutableCopy;
136-
NSLog(@"ERROS - %@", _dictionary[@"errors"]);
137-
for (NSDictionary *data in _dictionary[@"errors"]) {
138-
139-
JSONAPIErrorResource *resource = [[JSONAPIErrorResource alloc] initWithDictionary:data];
140-
NSLog(@"Error resource - %@", resource);
141-
if (resource) [errors addObject:resource];
159+
if (dictionary[@"errors"]) {
160+
NSMutableArray *errors = [[NSMutableArray alloc] init];
161+
NSLog(@"ERRORS - %@", dictionary[@"errors"]);
162+
for (NSDictionary *data in dictionary[@"errors"]) {
163+
164+
JSONAPIErrorResource *resource = [[JSONAPIErrorResource alloc] initWithDictionary: data];
165+
NSLog(@"Error resource - %@", resource);
166+
if (resource) [errors addObject:resource];
167+
}
168+
_errors = errors;
142169
}
143-
_errors = errors;
144170
}
145171

146-
- (id)inflateResourceData:(NSDictionary*)data {
147-
return [JSONAPIResource jsonAPIResource:data];
172+
- (void)inflateWithResource:(NSObject <JSONAPIResource> *)resource
173+
{
174+
NSMutableArray *resourceArray = [[NSMutableArray alloc] init];
175+
[resourceArray addObject:resource];
176+
_resources = resourceArray;
177+
178+
NSMutableDictionary *newDictionary = [[NSMutableDictionary alloc] init];
179+
180+
newDictionary[@"data"] = [JSONAPIResourceParser dictionaryFor:resource];
181+
182+
NSArray *included = [JSONAPIResourceParser relatedResourcesFor:resource];
183+
if (included.count) {
184+
newDictionary[@"included"] = included;
185+
186+
NSMutableDictionary *includedResources = [[NSMutableDictionary alloc] init];
187+
for (NSObject <JSONAPIResource> *linked in included) {
188+
189+
JSONAPIResourceDescriptor *desc = [[linked class] descriptor];
190+
191+
NSMutableDictionary *typeDict = includedResources[desc.type] ?: @{}.mutableCopy;
192+
typeDict[linked.ID] = resource;
193+
194+
includedResources[desc.type] = typeDict;
195+
}
196+
_includedResources = includedResources;
197+
}
198+
199+
_dictionary = newDictionary;
148200
}
149201

150202
@end

Classes/JSONAPIErrorResource.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@
66
// Copyright (c) 2015 Josh Holtz. All rights reserved.
77
//
88

9-
#import "JSONAPIResource.h"
10-
11-
@interface JSONAPIErrorResource : JSONAPIResource
9+
/**
10+
* Class respresentation of a JSON-API error structure.
11+
*/
12+
@interface JSONAPIErrorResource : NSObject
1213

14+
@property (nonatomic, strong) NSString *ID;
15+
@property (nonatomic, strong) NSString *href;
1316
@property (nonatomic, strong) NSString *status;
1417
@property (nonatomic, strong) NSString *code;
1518
@property (nonatomic, strong) NSString *title;
1619
@property (nonatomic, strong) NSString *detail;
1720
@property (nonatomic, strong) NSArray *links;
1821
@property (nonatomic, strong) NSArray *paths;
1922

23+
- (instancetype) initWithDictionary:(NSDictionary*)dictionary;
24+
2025
@end

Classes/JSONAPIErrorResource.m

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,25 @@
88

99
#import "JSONAPIErrorResource.h"
1010

11+
#import "JSONAPIResourceDescriptor.h"
12+
1113
@implementation JSONAPIErrorResource
1214

13-
- (NSDictionary *)mapKeysToProperties {
14-
return @{
15-
@"status" : @"status",
16-
@"code" : @"code",
17-
@"title" : @"title",
18-
@"detail" : @"detail",
19-
@"paths" : @"paths",
20-
};
15+
- (instancetype) initWithDictionary:(NSDictionary*)dictionary {
16+
self = [self init];
17+
18+
if (self) {
19+
_ID = dictionary[@"id"];
20+
_href = dictionary[@"href"];
21+
_status = dictionary[@"status"];
22+
_code = dictionary[@"code"];
23+
_title = dictionary[@"title"];
24+
_detail = dictionary[@"detail"];
25+
_links = dictionary[@"links"];
26+
_paths = dictionary[@"paths"];
27+
}
28+
29+
return self;
2130
}
2231

2332
@end

Classes/JSONAPIPropertyDescriptor.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//
2+
// JSONAPIProperty.h
3+
// JSONAPI
4+
//
5+
// Created by Jonathan Karl Armstrong, 2015.
6+
//
7+
8+
#import <Foundation/Foundation.h>
9+
10+
@class JSONAPIResourceDescriptor;
11+
12+
/**
13+
* JSON API metadata for a <JSONAPIResource> property. This is intended to be used in
14+
* dictionary contained in <JSONAPIResourceDescriptor>. The dictionary key is the model
15+
* class property name, and the <JSONAPIResourceDescriptor> contains the rest of the
16+
* model class metadata.
17+
*
18+
* This class is used primarily to transform an individual model property to and from JSON.
19+
*
20+
* There are two basic resouce model property types; simple values and related resource
21+
* references. This class handles both, although some internal properties may only apply
22+
* to one or the other.
23+
*
24+
* A simple value property includes anything that can be expressed or transformed into
25+
* a single key-value pair in JSON. This includes types like NSNumber and NSString that
26+
* have native JSON serialization support, as well as types like NSDate that can be
27+
* transformed into an NSString representation using an NSFormatter.
28+
*
29+
* Related resources are objects that must also conform to the <JSONAPIResource> protocol.
30+
* Classes that do not conform to the protocol, or do not have a <JSONAPIResourceDescriptor>
31+
* registered at runtime, are not supported, even if they theoretically have a valid JSON
32+
* serialization. Since it is easy to implement the protocol on existing classes, this
33+
* should not be a great limitation.
34+
*/
35+
@interface JSONAPIPropertyDescriptor : NSObject
36+
37+
/**
38+
* Name used for property in JSON serialization
39+
*/
40+
@property (nonatomic, readonly) NSString *jsonName;
41+
42+
/**
43+
* Optional formatter instance used to serialize/deserialize property. For example,
44+
* NSDate instances must be serialized to a string representation. Any type that can be
45+
* reversibly serialized to a String can be used as a property by setting an appropriate
46+
* serializer.
47+
*
48+
* For array properties, this is applied to each element of an array if set.
49+
*
50+
* Formatters are only for simple property types. A _linked_ <JSONAPIResource> may not have
51+
* a formatter.
52+
*/
53+
@property (nonatomic) NSFormatter *formatter;
54+
55+
/**
56+
* For a linked <JSONAPIResource> type, this is a reference to the resource class.
57+
*
58+
* For array properties (has many relationship), this applies to each element of the array.
59+
*
60+
* Should be nil for simple types.
61+
*/
62+
@property (nonatomic, readonly) Class resourceType;
63+
64+
/**
65+
* Initialize new instance. For supported simple types (NSSTring, NSNumber, etc.) this is usually
66+
* sufficient.
67+
*
68+
* @param name property label used in JSON
69+
*/
70+
- (instancetype)initWithJsonName:(NSString*)name;
71+
72+
/**
73+
* Initialize new instance with NSFormatter. For simple properties that are not directly
74+
* supported by NSJSONSerialization, use this. For example, NSDate requires an NSDateFormatter.
75+
*
76+
* @param name property label used in JSON
77+
* @param fmt an NSFormatter that converts to/fram JSON string
78+
*/
79+
- (instancetype)initWithJsonName:(NSString*)name withFormat:(NSFormatter*)fmt;
80+
81+
/**
82+
* Initialize new instance for linked resource property. For related resource properties use this.
83+
* This applies to both has-one relationships and has-many relationships.
84+
*
85+
* @param name property label used in JSON
86+
* @param resource Class of property. If property is an array (has_many), this is the element Class.
87+
*/
88+
- (instancetype)initWithJsonName:(NSString*)name withResource:(Class)resource;
89+
90+
@end

0 commit comments

Comments
 (0)