├── .gitignore ├── .ruby-gemset ├── .travis.yml ├── CHANGELOG.md ├── Classes ├── JSONAPI.h ├── JSONAPI.m ├── JSONAPIErrorResource.h ├── JSONAPIErrorResource.m ├── JSONAPIPropertyDescriptor.h ├── JSONAPIPropertyDescriptor.m ├── JSONAPIResource.h ├── JSONAPIResourceBase.h ├── JSONAPIResourceBase.m ├── JSONAPIResourceDescriptor.h ├── JSONAPIResourceDescriptor.m ├── JSONAPIResourceParser.h ├── JSONAPIResourceParser.m ├── NSDateFormatter+JSONAPIDateFormatter.h └── NSDateFormatter+JSONAPIDateFormatter.m ├── Examples ├── JSONAPIExample1 │ ├── JSONAPIExample1.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── JSONAPIExample1 │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── Main.storyboard │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.launchimage │ │ │ │ └── Contents.json │ │ ├── JSONAPIExample1-Info.plist │ │ ├── JSONAPIExample1-Prefix.pch │ │ ├── ViewController.h │ │ ├── ViewController.m │ │ ├── en.lproj │ │ │ └── InfoPlist.strings │ │ └── main.m │ └── JSONAPIExample1Tests │ │ ├── JSONAPIExample1Tests-Info.plist │ │ ├── JSONAPIExample1Tests.m │ │ └── en.lproj │ │ └── InfoPlist.strings └── Podfile ├── JSONAPI.podspec ├── LICENSE ├── Project ├── JSONAPI.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── JSONAPI.xccheckout │ └── xcshareddata │ │ └── xcschemes │ │ └── JSONAPI.xcscheme ├── JSONAPI.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── JSONAPI.xccheckout ├── JSONAPI │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── ArticleResource.h │ ├── ArticleResource.m │ ├── Base.lproj │ │ └── Main.storyboard │ ├── CommentResource.h │ ├── CommentResource.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── JSONAPI-Info.plist │ ├── JSONAPI-Prefix.pch │ ├── MediaResource.h │ ├── MediaResource.m │ ├── NewsFeedPostResource.h │ ├── NewsFeedPostResource.m │ ├── PeopleResource.h │ ├── PeopleResource.m │ ├── SocialCommunityResource.h │ ├── SocialCommunityResource.m │ ├── UserResource.h │ ├── UserResource.m │ ├── ViewController.h │ ├── ViewController.m │ ├── WebPageResource.h │ ├── WebPageResource.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── JSONAPITests │ ├── JSONAPITests-Info.plist │ ├── JSONAPITests.m │ ├── empty_relationship_example.json │ ├── en.lproj │ └── InfoPlist.strings │ ├── error_example.json │ ├── generic_relationships_example.json │ └── main_example.json ├── README.md └── Rakefile /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | xcshareddata 14 | profile 15 | *.moved-aside 16 | DerivedData 17 | .idea/ 18 | *.hmap 19 | 20 | #CocoaPods 21 | Pods 22 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | cocoapods 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.1 3 | before_install: 4 | - brew update 5 | - brew uninstall xctool && brew install xctool --HEAD 6 | - cd Project 7 | script: xctool -project JSONAPI.xcodeproj -scheme JSONAPI -sdk iphonesimulator test 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # JSONAPI CHANGELOG 2 | 3 | ## 0.1.0 4 | 5 | Initial release. 6 | -------------------------------------------------------------------------------- /Classes/JSONAPI.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPI.h 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "JSONAPIResource.h" 12 | 13 | /** 14 | * Represents a complete JSON-API formatted message body. 15 | */ 16 | @interface JSONAPI : NSObject 17 | 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; 26 | @property (nonatomic, strong, readonly) NSArray *errors; 27 | 28 | @property (readonly) id resource; 29 | @property (nonatomic, strong, readonly) NSArray *resources; 30 | @property (nonatomic, strong, readonly) NSDictionary *includedResources; 31 | 32 | @property (nonatomic, strong, readonly) NSError *internalError; 33 | 34 | // Initializers 35 | + (instancetype)jsonAPIWithDictionary:(NSDictionary *)dictionary; 36 | + (instancetype)jsonAPIWithString:(NSString *)string; 37 | + (instancetype)jsonAPIWithResource:(NSObject *)resource; 38 | 39 | - (instancetype)initWithDictionary:(NSDictionary*)dictionary; 40 | - (instancetype)initWithString:(NSString*)string; 41 | 42 | - (NSDictionary*)dictionary; 43 | 44 | - (id)includedResource:(id)ID withType:(NSString*)type; 45 | - (BOOL)hasErrors; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Classes/JSONAPI.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPITopLevel.m 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "JSONAPI.h" 10 | 11 | #import "JSONAPIErrorResource.h" 12 | #import "JSONAPIResourceParser.h" 13 | #import "JSONAPIResourceDescriptor.h" 14 | 15 | 16 | static NSString *gMEDIA_TYPE = @"application/vnd.api+json"; 17 | 18 | @interface JSONAPI() 19 | 20 | @property (nonatomic, strong) NSDictionary *dictionary; 21 | 22 | @end 23 | 24 | @implementation JSONAPI 25 | 26 | #pragma mark - Class 27 | 28 | + (NSString*)MEDIA_TYPE { 29 | return gMEDIA_TYPE; 30 | } 31 | 32 | + (instancetype)jsonAPIWithDictionary:(NSDictionary *)dictionary { 33 | return [[JSONAPI alloc] initWithDictionary:dictionary]; 34 | } 35 | 36 | + (instancetype)jsonAPIWithString:(NSString *)string { 37 | return [[JSONAPI alloc] initWithString:string]; 38 | } 39 | 40 | + (instancetype)jsonAPIWithResource:(NSObject *)resource { 41 | return [[JSONAPI alloc] initWithResource:resource]; 42 | } 43 | 44 | #pragma mark - Instance 45 | 46 | - (NSDictionary*)meta { 47 | return self.dictionary[@"meta"]; 48 | } 49 | 50 | - (instancetype)initWithDictionary:(NSDictionary*)dictionary { 51 | self = [super init]; 52 | if (self) { 53 | [self inflateWithDictionary:dictionary]; 54 | } 55 | return self; 56 | } 57 | 58 | - (instancetype)initWithString:(NSString*)string { 59 | self = [super init]; 60 | if (self) { 61 | [self inflateWithString:string]; 62 | } 63 | return self; 64 | } 65 | 66 | -(instancetype)initWithResource:(NSObject *)resource { 67 | self = [super init]; 68 | if (self) { 69 | [self inflateWithResource:resource]; 70 | } 71 | return self; 72 | } 73 | 74 | - (void)inflateWithString:(NSString*)string { 75 | id json = [NSJSONSerialization JSONObjectWithData:[string dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; 76 | 77 | if ([json isKindOfClass:[NSDictionary class]] == YES) { 78 | [self inflateWithDictionary:json]; 79 | } else { 80 | _internalError = [NSError errorWithDomain:@"Could not parse JSON" code:0 userInfo:nil]; 81 | } 82 | } 83 | 84 | #pragma mark - Resources 85 | 86 | - (id)resource { 87 | return _resources.firstObject; 88 | } 89 | 90 | - (id)includedResource:(id)ID withType:(NSString *)type { 91 | if (ID == nil) return nil; 92 | if (type == nil) return nil; 93 | return _includedResources[type][ID]; 94 | } 95 | 96 | - (BOOL)hasErrors { 97 | return _errors.count > 0; 98 | } 99 | 100 | #pragma mark - Private 101 | 102 | - (void)inflateWithDictionary:(NSDictionary*)dictionary { 103 | 104 | // Sets internal dictionary 105 | _dictionary = dictionary; 106 | 107 | // Parse resources 108 | id data = dictionary[@"data"]; 109 | if ([data isKindOfClass:[NSArray class]] == YES) { 110 | _resources = [JSONAPIResourceParser parseResources:data]; 111 | 112 | } else if ([data isKindOfClass:[NSDictionary class]] == YES) { 113 | id resource = [JSONAPIResourceParser parseResource:data]; 114 | _resources = [[NSArray alloc] initWithObjects:resource, nil]; 115 | } 116 | 117 | // Parses included resources 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 *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 *resource = [JSONAPIResourceParser parseResource:included]; 135 | if (resource) { 136 | JSONAPIResourceDescriptor *desc = [JSONAPIResourceDescriptor forLinkedType:data[@"type"]]; 137 | 138 | NSMutableDictionary *typeDict = includedResources[desc.type] ?: @{}.mutableCopy; 139 | typeDict[resource.ID] = resource; 140 | 141 | includedResources[desc.type] = typeDict; 142 | } 143 | } 144 | _includedResources = includedResources; 145 | 146 | // Link included with included 147 | for (NSDictionary *typeIncluded in _includedResources.allValues) { 148 | for (NSObject *resource in typeIncluded.allValues) { 149 | [JSONAPIResourceParser link:resource withIncluded:self]; 150 | } 151 | } 152 | 153 | // Link data with included 154 | for (NSObject *resource in _resources) { 155 | [JSONAPIResourceParser link:resource withIncluded:self]; 156 | } 157 | 158 | // Parse errors 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; 169 | } 170 | } 171 | 172 | - (void)inflateWithResource:(NSObject *)resource 173 | { 174 | _resources = @[resource]; 175 | 176 | NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; 177 | dictionary[@"data"] = [JSONAPIResourceParser dictionaryFor:resource]; 178 | 179 | NSArray *relatedResources = [JSONAPIResourceParser relatedResourcesFor:resource]; 180 | if (relatedResources.count) { 181 | _includedResources = [self mapIncludedResources:relatedResources forResource:resource]; 182 | dictionary[@"included"] = [self parseRelatedResources:relatedResources]; 183 | } 184 | _dictionary = dictionary; 185 | } 186 | 187 | - (NSDictionary *)mapIncludedResources:(NSArray *)relatedResources forResource:(NSObject *)resource 188 | { 189 | NSMutableDictionary *includedResources = [NSMutableDictionary new]; 190 | for (NSObject *linked in relatedResources) { 191 | JSONAPIResourceDescriptor *desc = [[linked class] descriptor]; 192 | NSMutableDictionary *typeDict = includedResources[desc.type] ?: @{}.mutableCopy; 193 | typeDict[linked.ID] = resource; 194 | includedResources[desc.type] = typeDict; 195 | } 196 | return includedResources; 197 | } 198 | 199 | - (NSArray *)parseRelatedResources:(NSArray *)relatedResources 200 | { 201 | NSMutableArray *parsedResources = [NSMutableArray new]; 202 | for (NSObject *linked in relatedResources) { 203 | [parsedResources addObject:[JSONAPIResourceParser dictionaryFor:linked]]; 204 | } 205 | return parsedResources; 206 | } 207 | 208 | @end 209 | -------------------------------------------------------------------------------- /Classes/JSONAPIErrorResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIErrorResource.h 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 3/17/15. 6 | // Copyright (c) 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * Class respresentation of a JSON-API error structure. 13 | */ 14 | @interface JSONAPIErrorResource : NSObject 15 | 16 | @property (nonatomic, strong) NSString *ID; 17 | @property (nonatomic, strong) NSString *href; 18 | @property (nonatomic, strong) NSString *status; 19 | @property (nonatomic, strong) NSString *code; 20 | @property (nonatomic, strong) NSString *title; 21 | @property (nonatomic, strong) NSString *detail; 22 | @property (nonatomic, strong) NSArray *links; 23 | @property (nonatomic, strong) NSArray *paths; 24 | 25 | - (instancetype) initWithDictionary:(NSDictionary*)dictionary; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Classes/JSONAPIErrorResource.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIErrorResource.m 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 3/17/15. 6 | // Copyright (c) 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "JSONAPIErrorResource.h" 10 | 11 | #import "JSONAPIResourceDescriptor.h" 12 | 13 | @implementation JSONAPIErrorResource 14 | 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; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Classes/JSONAPIPropertyDescriptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIProperty.h 3 | // JSONAPI 4 | // 5 | // Created by Jonathan Karl Armstrong, 2015. 6 | // 7 | 8 | #import 9 | 10 | @class JSONAPIResourceDescriptor; 11 | 12 | /** 13 | * JSON API metadata for a property. This is intended to be used in 14 | * dictionary contained in . The dictionary key is the model 15 | * class property name, and the 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 protocol. 30 | * Classes that do not conform to the protocol, or do not have a 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_ may not have 51 | * a formatter. 52 | */ 53 | @property (nonatomic) NSFormatter *formatter; 54 | 55 | /** 56 | * For a linked 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 91 | -------------------------------------------------------------------------------- /Classes/JSONAPIPropertyDescriptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIProperty.m 3 | // JSONAPI 4 | // 5 | // Created by Jonathan Karl Armstrong, 2015. 6 | // 7 | 8 | #import "JSONAPIPropertyDescriptor.h" 9 | 10 | @implementation JSONAPIPropertyDescriptor 11 | 12 | - (instancetype)initWithJsonName:(NSString*)name { 13 | return [self initWithJsonName:name withFormat:nil]; 14 | } 15 | 16 | - (instancetype)initWithJsonName:(NSString*)name withFormat:(NSFormatter*)fmt { 17 | self = [self init]; 18 | 19 | if (self) { 20 | _jsonName = name; 21 | _formatter = fmt; 22 | _resourceType = nil; 23 | } 24 | 25 | return self; 26 | } 27 | 28 | - (instancetype)initWithJsonName:(NSString*)name withResource:(Class)resource { 29 | self = [self init]; 30 | 31 | if (self) { 32 | _jsonName = name; 33 | _formatter = nil; 34 | _resourceType = resource; 35 | } 36 | 37 | return self; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Classes/JSONAPIResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIResource.h 3 | // JSONAPI 4 | // 5 | // Created by Jonathan Karl Armstrong, 2015. 6 | // 7 | 8 | #import 9 | 10 | @class JSONAPI; 11 | @class JSONAPIPropertyDescriptor; 12 | @class JSONAPIResourceDescriptor; 13 | 14 | /** 15 | * Protocol of an object that is available for JSON API serialization. 16 | * 17 | * When developing model classes for use with JSON-API, it is suggested that classes 18 | * be derived from , but that is not required. An existing model 19 | * class can be adapted for JSON-API by implementing this protocol. 20 | */ 21 | @protocol JSONAPIResource 22 | 23 | #pragma mark - Class Methods 24 | 25 | /** 26 | Get the JSON API resource metadata description. This will be different for each resource 27 | model class. It must be defined by the subclass. 28 | 29 | The definition should look something like: 30 | 31 |

 32 |       #import <JSONAPI/JSONAPIPropertyDescriptor.h>
 33 |       #import <JSONAPI/JSONAPIResourceDescriptor.h>
 34 |  
 35 |       @implementation PeopleResource
 36 | 
 37 |       static JSONAPIResourceDescriptor *__descriptor = nil;
 38 | 
 39 |       + (JSONAPIResourceDescriptor*)descriptor {
 40 |          static dispatch_once_t onceToken;
 41 |          dispatch_once(&onceToken, ^{
 42 |            __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"people"];
 43 | 
 44 |            [__descriptor setIdProperty:@"ID"];
 45 | 
 46 |            [__descriptor addProperty:@"telephone"];
 47 |            [__descriptor addProperty:@"birthday" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"birthday" withFormat:[NSDateFormatter RFC3339DateFormatter]]];
 48 |            [__descriptor addProperty:@"firstName" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"first"]];
 49 |            [__descriptor addProperty:@"lastName" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"last"]];
 50 |          });
 51 | 
 52 |          return __descriptor;
 53 |        }
 54 |  
55 | 56 | In this example, `PeopleResource` is a class that inherits from 57 | (defines property 'ID'), and defines properties `NSString *telephone`, `NSDate *birthday`, 58 | `NSString *firstName` and `NSString *lastName`. 59 | 60 | * The `telephone` property needs no special transform rules. 61 | * The `birthday` property must be transformed into a string for JSON. 62 | * The API you are targeting uses the labels `first` and `last` for the last two properties, 63 | which you prefer to relabel internally. 64 | 65 | @return Resource description for the target model class. 66 | */ 67 | + (JSONAPIResourceDescriptor*)descriptor; 68 | 69 | 70 | #pragma mark - Properties 71 | 72 | /** 73 | * Get the URL that corresponds to this resource. May be nil. Should be set if returned from a 74 | * server. A GET request on the JSON-API endpoint should return the same resource. 75 | * 76 | * There should be no for this property. It is set from the 'links' 77 | * property in the JSON body, and the JSON value is always a string URL. 78 | * 79 | * In general, this should be implemented by a @property selfLink in the realized class. The 80 | * @property declaration will automatically synthesize the get/set members declared in this 81 | * protocol. The property storage is an implementation detail, which is why the protocol does 82 | * not use a @property declaration. 83 | * 84 | * @return The URL that corresponds to this resource. 85 | */ 86 | - (NSString *)selfLink; 87 | 88 | /** 89 | * Set the URL that corresponds to this resource. This attribute is set from the 'links' 90 | * property in the JSON body, and the JSON value is always a string URL. A GET request on the 91 | * JSON-API endpoint should return the same resource. 92 | * 93 | * In general, this should be implemented by a @property selfLink in the realized class. The 94 | * @property declaration will automatically synthesize the get/set members declared in this 95 | * protocol. The property storage is an implementation detail, which is why the protocol does 96 | * not use a @property declaration. 97 | * 98 | * @param path The URL that corresponds to this resource. 99 | */ 100 | - (void)setSelfLink:(NSString*)path; 101 | 102 | /** 103 | * Get the API record identifier for a resource instance. Required for resources that come 104 | * from persistance storage (i.e. the server), but may be nil for new records that have not 105 | * been saved. Every saved resource record should be uniquely identifiable by the combination 106 | * of type and ID. 107 | * 108 | * This is typically a database sequence number associated withe the resource record, 109 | * but that is not required. The JSON API requires ID to be serialized as a string. 110 | * 111 | * In general, this should be implemented by a @property ID in the realized class. The 112 | * @property declaration will automatically synthesize the get/set members declared in this 113 | * protocol. The property storage is an implementation detail, which is why the protocol does 114 | * not use a @property declaration. 115 | * 116 | * @return The record identifier for a resource instance. 117 | */ 118 | - (id)ID; 119 | 120 | /** 121 | * Set the API record identifier for a resource instance. Required for resources that come 122 | * from persistance storage (i.e. the server), but may be nil for new records that have not 123 | * been saved. Every saved resource record should be uniquely identifiable by the combination 124 | * of type and ID. 125 | * 126 | * This is typically a database sequence number associated withe the resource record, 127 | * but that is not required. The JSON API requires ID to be serialized as a string. 128 | * 129 | * In general, this should be implemented by a @property ID in the realized class. The 130 | * @property declaration will automatically synthesize the get/set members declared in this 131 | * protocol. The property storage is an implementation detail, which is why the protocol does 132 | * not use a @property declaration. 133 | * 134 | * @param identifier The record identifier for a resource instance. 135 | */ 136 | - (void)setID:(id)identifier; 137 | 138 | /** 139 | * Get the meta for a resource instance. Optional for resources that come 140 | * from persistance storage (i.e. the server). 141 | * 142 | * In general, this should be implemented by a @property ID in the realized class. The 143 | * @property declaration will automatically synthesize the get/set members declared in this 144 | * protocol. The property storage is an implementation detail, which is why the protocol does 145 | * not use a @property declaration. 146 | * 147 | * @return The meta for a resource instance. 148 | */ 149 | - (NSDictionary*)meta; 150 | 151 | /** 152 | * Set the meta for a resource instance. Optional for resources that come 153 | * from persistance storage (i.e. the server). 154 | * 155 | * In general, this should be implemented by a @property ID in the realized class. The 156 | * @property declaration will automatically synthesize the get/set members declared in this 157 | * protocol. The property storage is an implementation detail, which is why the protocol does 158 | * not use a @property declaration. 159 | * 160 | * @param meta The meta for a resource instance. 161 | */ 162 | - (void)setMeta:(NSDictionary*)meta; 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /Classes/JSONAPIResourceBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIResourceBase.h 3 | // JSONAPI 4 | // 5 | // Created by Jonathan Karl Armstrong, 2015. 6 | // 7 | 8 | #import 9 | 10 | #include "JSONAPIResource.h" 11 | 12 | /** 13 | * Model classes that are developed with JSON-API in mind may be derived from this abstract 14 | * base class. This implements the required properties for a JSON-API model resource. 15 | * 16 | * The descriptor for a model resource is not implemented, and must be declared in the 17 | * realized model class. 18 | */ 19 | @interface JSONAPIResourceBase : NSObject 20 | 21 | /** 22 | * The URL that corresponds to this resource. May be nil. Should be set if returned from a 23 | * server. 24 | * 25 | * There is no for this property. It is set from the 'links' 26 | * property in the JSON body, and the JSON value is always a string URL. 27 | */ 28 | @property (strong, nonatomic) NSString *selfLink; 29 | 30 | /** 31 | * API identifier for a resource instance. Required for resources that come from the 32 | * server, but may be nil for new records that have not been saved. Every saved 33 | * record should be uniquely identifiable by the combination of type and ID. 34 | * 35 | * This is typically a database sequence number associated withe the resource record, 36 | * but that is not required. The JSON API requires ID to be serialized as a string. 37 | * 38 | * @warning Each subclass must add this property to its with the line: 39 | * 40 | * \[descriptor setIdProperty:@"ID"\]; 41 | * 42 | * where 'descriptor' is the name of your classes' static . 43 | */ 44 | @property (strong, atomic) id ID; 45 | 46 | /** 47 | * Meta for a resource instance. Optional for resources that come 48 | * from persistance storage (i.e. the server). 49 | */ 50 | @property (strong, atomic) NSDictionary *meta; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Classes/JSONAPIResourceBase.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIResourceBase.m 3 | // JSONAPI 4 | // 5 | // Created by Jonathan Karl Armstrong, 2015. 6 | // 7 | 8 | #import "JSONAPIResourceBase.h" 9 | 10 | @implementation JSONAPIResourceBase 11 | 12 | + (JSONAPIResourceDescriptor *)descriptor { 13 | // subclass must override 14 | [self doesNotRecognizeSelector:_cmd]; 15 | return nil; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Classes/JSONAPIResourceDescriptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIResourceDescriptor.h 3 | // JSONAPI 4 | // 5 | // Created by Jonathan Karl Armstrong, 2015. 6 | // 7 | 8 | #import 9 | 10 | @class JSONAPIPropertyDescriptor; 11 | 12 | /** 13 | * Metadata for a class. Describes the JSON attributes for a the class. 14 | */ 15 | @interface JSONAPIResourceDescriptor : NSObject 16 | 17 | #pragma mark - Properties 18 | 19 | /** 20 | * JSON-API "type" name 21 | * 22 | * This is required for any model resource. 23 | */ 24 | @property (strong, readonly) NSString *type; 25 | 26 | /** 27 | * JSON-API "id" property description. 28 | * 29 | * This is required for any model resource. 30 | */ 31 | @property (strong) NSString *idProperty; 32 | 33 | @property (strong) NSString *selfLinkProperty; 34 | 35 | /** 36 | * JSON-API "id" optional format. 37 | * 38 | * JSON-API requires the 'id' property to be serialized as a string. The formatter 39 | * allows you to specify any convertable object. If the property is nil, the 'id' 40 | * will use the default JSON serialization. 41 | */ 42 | @property (strong) NSFormatter *idFormatter; 43 | 44 | /** The resource class that is described. */ 45 | @property (readonly) Class resourceClass; 46 | 47 | /** Maps model property names to . */ 48 | @property (readonly) NSDictionary *properties; 49 | 50 | 51 | #pragma mark - Class Methods 52 | 53 | /** 54 | * Register a resouce type. 55 | * 56 | * This must be called before any serious parsing can be done. You must add a line 57 | * for each resource class like: 58 | * 59 | * \[JSONAPIResourceDescriptor addResource:\[PeopleResource class\]\]; 60 | * 61 | * somewhere in your test/application setup. This is all you have to do to configure this 62 | * module. 63 | * 64 | * @param resourceClass The class represented. Must implement the protocol. 65 | */ 66 | + (void)addResource:(Class)resourceClass; 67 | 68 | /** 69 | * Get the resource descriptor for the JSON "type" label. 70 | * 71 | * This only works if the class has been registered first. 72 | * 73 | * @param linkedType The label associated with the resource class in JSON 74 | * 75 | * @return The resource class descriptor. 76 | */ 77 | + (instancetype)forLinkedType:(NSString *)linkedType; 78 | 79 | 80 | #pragma mark - Instance Methods 81 | 82 | /** 83 | * Initialize a new instance. 84 | * 85 | * @param resource A class 86 | * @param linkedType Label used in JSON for type 87 | */ 88 | - (instancetype)initWithClass:(Class)resource forLinkedType:(NSString*)linkedType; 89 | 90 | /** 91 | * Add a simple property. 92 | * 93 | * Creates a default based on the name. The default property 94 | * description assumes the JSON name matches the property name, and no string format is used. 95 | * 96 | * @param name The name of the property in the model class. 97 | */ 98 | - (void)addProperty:(NSString*)name; 99 | 100 | /** 101 | * Add a simple property with custom json name. 102 | * 103 | * @param name The name of the property in the class. 104 | * @param jsonName The label of the property in JSON. 105 | */ 106 | - (void)addProperty:(NSString*)name withJsonName:(NSString *)json; 107 | 108 | /** 109 | * Add a simple property with custom transform object. 110 | * 111 | * @param name The name of the property in the class. 112 | * @param description Describes how the property is transformed to JSON 113 | */ 114 | - (void)addProperty:(NSString*)name withDescription:(JSONAPIPropertyDescriptor*)description; 115 | 116 | /** 117 | * Add a has-one related resource property. 118 | * 119 | * Creates a default based on the name. The default property 120 | * description assumes the JSON name matches the property name. 121 | * 122 | * @param jsonApiResource The related property class. 123 | * @param name The name of the property in the class. 124 | */ 125 | - (void)hasOne:(Class)jsonApiResource withName:(NSString*)name; 126 | 127 | /** 128 | * Add a has-one related resource property with a JSON property label different from 129 | * the property name. 130 | * 131 | * Creates a based on the arguments. 132 | * 133 | * @param jsonApiResource The related property class. 134 | * @param name The name of the property in the class. 135 | * @param json The label of the property in JSON 136 | */ 137 | - (void)hasOne:(Class)jsonApiResource withName:(NSString*)name withJsonName:(NSString*)json; 138 | 139 | /** 140 | * Add a has-many related resource property. 141 | * 142 | * Note that the Class argmuent refers to the collection element type. It assumes the 143 | * property is instantiated in an NSArray. 144 | * 145 | * Creates a default based on the name. The default property 146 | * description assumes the JSON name matches the property name. 147 | * 148 | * @param jsonApiResource The related property class. 149 | * @param name The name of the property in the class. 150 | */ 151 | - (void)hasMany:(Class)jsonApiResource withName:(NSString*)name; 152 | 153 | /** 154 | * Add a has-many related resource property with a JSON property label different from 155 | * the property name. 156 | * 157 | * Note that the Class argmuent refers to the collection element type. It assumes the 158 | * property is instantiated in an NSArray. 159 | * 160 | * Creates a based on the arguments. 161 | * 162 | * @param jsonApiResource The related property class. 163 | * @param name The name of the property in the class. 164 | * @param json The label of the property in JSON 165 | */ 166 | - (void)hasMany:(Class)jsonApiResource withName:(NSString*)name withJsonName:(NSString*)json; 167 | 168 | @end 169 | -------------------------------------------------------------------------------- /Classes/JSONAPIResourceDescriptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIResourceDescriptor.m 3 | // JSONAPI 4 | // 5 | // Created by Jonathan Karl Armstrong, 2015. 6 | // 7 | 8 | #import "JSONAPIResourceDescriptor.h" 9 | #import "JSONAPIPropertyDescriptor.h" 10 | #import "JSONAPIResource.h" 11 | 12 | 13 | static NSMutableDictionary *linkedTypeToResource = nil; 14 | 15 | @implementation JSONAPIResourceDescriptor 16 | 17 | /** 18 | * Create the 'class' table for all resources. 19 | */ 20 | + (void)initialize { 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | linkedTypeToResource = [[NSMutableDictionary alloc] init]; 24 | }); 25 | } 26 | 27 | + (void)addResource:(Class)resourceClass { 28 | JSONAPIResourceDescriptor *descriptor = [resourceClass descriptor]; 29 | NSAssert(descriptor.type, @"A JSONAPIResourceDescriptor must have a type attribute."); 30 | NSAssert(descriptor.idProperty, @"A JSONAPIResourceDescriptor must have an id attribute."); 31 | 32 | NSString *type = descriptor.type; 33 | [linkedTypeToResource setObject:descriptor forKey:type]; 34 | } 35 | 36 | + (instancetype)forLinkedType:(NSString *)linkedType { 37 | return [linkedTypeToResource objectForKey:linkedType]; 38 | } 39 | 40 | - (instancetype)initWithClass:(Class)resource forLinkedType:(NSString*)linkedType { 41 | self = [super init]; 42 | 43 | if (self) { 44 | _type = linkedType; 45 | _resourceClass = resource; 46 | _properties = [[NSMutableDictionary alloc] init]; 47 | 48 | [self setIdProperty:@"ID"]; 49 | [self setSelfLinkProperty:@"selfLink"]; 50 | } 51 | 52 | return self; 53 | } 54 | 55 | - (void)addProperty:(NSString*)name { 56 | [self addProperty:name withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:name]]; 57 | } 58 | 59 | - (void)addProperty:(NSString*)name withJsonName:(NSString *)json 60 | { 61 | [self addProperty:name withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:json]]; 62 | } 63 | 64 | - (void)addProperty:(NSString*)name withDescription:(JSONAPIPropertyDescriptor*)description { 65 | [[self properties] setValue:description forKey:name]; 66 | } 67 | 68 | // note: hasOne and hasMany are identical for now, but seems reasonable to keep both 69 | 70 | - (void)hasOne:(Class)jsonApiResource withName:(NSString*)name { 71 | [self addProperty:name 72 | withDescription:[[JSONAPIPropertyDescriptor alloc] 73 | initWithJsonName:name 74 | withResource:jsonApiResource]]; 75 | } 76 | 77 | - (void)hasOne:(Class)jsonApiResource withName:(NSString*)name withJsonName:(NSString*)json { 78 | [self addProperty:name 79 | withDescription:[[JSONAPIPropertyDescriptor alloc] 80 | initWithJsonName:json 81 | withResource:jsonApiResource]]; 82 | } 83 | 84 | - (void)hasMany:(Class)jsonApiResource withName:(NSString*)name { 85 | [self addProperty:name 86 | withDescription:[[JSONAPIPropertyDescriptor alloc] 87 | initWithJsonName:name 88 | withResource:jsonApiResource]]; 89 | } 90 | 91 | - (void)hasMany:(Class)jsonApiResource withName:(NSString*)name withJsonName:(NSString*)json { 92 | [self addProperty:name 93 | withDescription:[[JSONAPIPropertyDescriptor alloc] 94 | initWithJsonName:json 95 | withResource:jsonApiResource]]; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /Classes/JSONAPIResourceParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIResourceParser.h 3 | // JSONAPI 4 | // 5 | // Created by Jonathan Karl Armstrong, 2015. 6 | // 7 | 8 | #import 9 | 10 | #import "JSONAPIResource.h" 11 | 12 | @class JSONAPI; 13 | 14 | /** Class to perform serialization and deserialization of JSON-API to model. */ 15 | @interface JSONAPIResourceParser : NSObject 16 | 17 | 18 | #pragma mark - Class methods 19 | 20 | /** 21 | * Allocate a resource object from a JSON dictionary. The dictionary argument should 22 | * describe one resource in JSON API format. This can be a JSON API "data" element, 23 | * one of the JSON API "included" elemets, or even a "linkage" element. A valid 24 | * dictionary contains at least "type" and "id" fields. 25 | * 26 | * TODO: spec allows a resource to be specified by a 'related resource URL'. 27 | * 28 | * @param dictionary JSON instance definition as NSDictionary 29 | * 30 | * @return newly allocated model instance. 31 | */ 32 | + (id )parseResource:(NSDictionary*)dictionary; 33 | 34 | /** 35 | * Allocate an array of resource objects. The array argument must be an array of 36 | * dictionary objects follwing the same rules for a single resource. 37 | * 38 | * @param array array of dictionary JSON instance definition as NSDictionary. 39 | * 40 | * @return Array of newly allocated model instances. 41 | */ 42 | + (NSArray*)parseResources:(NSArray*)array; 43 | 44 | /** 45 | * Serialize resource to a JSON dictionary. 46 | * 47 | * @return NSDictionary that fully describes this instance. This is ready to include in 48 | * a JSON-API message 'data' set or 'included' set. 49 | * 50 | * @param resource model object 51 | */ 52 | + (NSDictionary*)dictionaryFor:(NSObject *)resource; 53 | 54 | /** 55 | * Initialize the instance properties in the model object instance from the JSON 56 | * dictionary. 57 | * 58 | * @warning The caller is responsible for insuring the resource class matches the 59 | * dictionary type. Results of mismatched types are unpredictable. 60 | * 61 | * @param resource model object 62 | * @param dictionary JSON-API data dictionary 63 | */ 64 | + (void)set:(NSObject *)resource withDictionary:dictionary; 65 | 66 | /** 67 | * Update the linked resources from a deserialized set of JSON API "included" elements. 68 | * Linked resource instances are replaced with full definition, when type and ID match. 69 | * 70 | * @param resource model object 71 | * @param jsonAPI JSON-API message object 72 | */ 73 | + (void)link:(NSObject *)resource withIncluded:(JSONAPI*)jsonAPI; 74 | 75 | /** 76 | * Get array of associated resource instances. 77 | * 78 | * @param resource model object 79 | * 80 | * @return The collection of related model instances. 81 | */ 82 | + (NSArray*)relatedResourcesFor:(NSObject *)resource; 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /Classes/JSONAPIResourceParser.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIResourceParser.m 3 | // JSONAPI 4 | // 5 | // Created by Jonathan Karl Armstrong, 2015. 6 | // 7 | 8 | #import "JSONAPIResourceParser.h" 9 | 10 | #import "JSONAPI.h" 11 | #import "JSONAPIResourceDescriptor.h" 12 | #import "JSONAPIPropertyDescriptor.h" 13 | 14 | #pragma mark - JSONAPIResourceParser 15 | 16 | @interface JSONAPIResourceParser() 17 | 18 | /** 19 | * Allocate a resource link instance from a deserialized JSON dictionary. The dictionary argument 20 | * should describe one resource in JSON API format. This must be a JSON API "links" element. 21 | * It is not a complete JSON API response block. 22 | * 23 | * This will return either a instance, or an NSArray of instances. 24 | * 25 | * @param dictionary deserialized JSON data object 26 | * 27 | * @return initialized resource instance. 28 | */ 29 | + (id)jsonAPILink:(NSDictionary*)dictionary; 30 | 31 | 32 | /** 33 | * Generate a 'links' element for the data dictionary of the owner resource instance. 34 | * 35 | * A 'links' element contains the self link, a 'related' link that can be used to 36 | * retrieve this instance given the container data, and a 'linkage' element that 37 | * describes the minimum respresentation of this instance (type and ID). 38 | * 39 | * @param resource model object 40 | * @param owner the resource instance that contains the model object 41 | * @param key label used in JSON for **owner** property linked to the model object 42 | * 43 | * @return newly allocated JSON dictionary for linkage 44 | */ 45 | + (NSDictionary*)link:(NSObject *)resource from:(NSObject *)owner withKey:(NSString*)key; 46 | 47 | @end 48 | 49 | @implementation JSONAPIResourceParser 50 | 51 | #pragma mark - 52 | #pragma mark - Class Methods 53 | 54 | 55 | + (id )parseResource:(NSDictionary*)dictionary { 56 | NSString *type = dictionary[@"type"] ?: @""; 57 | JSONAPIResourceDescriptor *descriptor = [JSONAPIResourceDescriptor forLinkedType:type]; 58 | 59 | NSObject *resource = [[[descriptor resourceClass] alloc] init]; 60 | [self set:resource withDictionary:dictionary]; 61 | 62 | return resource; 63 | } 64 | 65 | + (NSArray*)parseResources:(NSArray*)array { 66 | 67 | NSMutableArray *mutableArray = @[].mutableCopy; 68 | for (NSDictionary *dictionary in array) { 69 | NSObject *resource = [self parseResource:dictionary]; 70 | if(resource) { 71 | [mutableArray addObject:resource]; 72 | } 73 | } 74 | 75 | return mutableArray; 76 | } 77 | 78 | + (NSDictionary*)dictionaryFor:(NSObject *)resource { 79 | NSFormatter *format; 80 | 81 | NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; 82 | 83 | // linkage is only allocated if there are 1 or more links 84 | NSMutableDictionary *linkage = nil; 85 | 86 | JSONAPIResourceDescriptor *descriptor = [[resource class] descriptor]; 87 | 88 | [dictionary setValue:[descriptor type] forKey: @"type"]; 89 | 90 | id ID = [resource valueForKey:[descriptor idProperty]]; 91 | if (ID) { 92 | // ID optional only for create (POST request) 93 | format = [descriptor idFormatter]; 94 | if (format) { 95 | [dictionary setValue:[format stringForObjectValue:ID] forKey:@"id"]; 96 | } else { 97 | [dictionary setValue:ID forKey:@"id"]; 98 | } 99 | } 100 | 101 | NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init]; 102 | 103 | // Loops through all keys to map to properties 104 | NSDictionary *properties = [descriptor properties]; 105 | for (NSString *key in properties) { 106 | JSONAPIPropertyDescriptor *property = [properties objectForKey:key]; 107 | 108 | id value = [resource valueForKey:key]; 109 | if (value) { 110 | if ([value isKindOfClass:[NSArray class]]) { 111 | NSArray *valueArray = value; 112 | if (valueArray.count > 0) { 113 | NSMutableArray *dictionaryArray = [[NSMutableArray alloc] initWithCapacity:valueArray.count]; 114 | 115 | if ([property resourceType] || [((NSArray *)value).firstObject conformsToProtocol:@protocol(JSONAPIResource)]) { 116 | if (linkage == nil) { 117 | linkage = [[NSMutableDictionary alloc] init]; 118 | } 119 | 120 | for (id valueElement in valueArray) { 121 | [dictionaryArray addObject:[self link:valueElement from:resource withKey:[property jsonName]]]; 122 | } 123 | 124 | NSDictionary *dataDictionary = @{@"data" : dictionaryArray}; 125 | [linkage setValue:dataDictionary forKey:[property jsonName]]; 126 | } else { 127 | NSFormatter *format = [property formatter]; 128 | 129 | for (id valueElement in valueArray) { 130 | if (format) { 131 | [dictionaryArray addObject:[format stringForObjectValue:valueElement]]; 132 | } else { 133 | [dictionaryArray addObject:valueElement]; 134 | } 135 | } 136 | 137 | [attributes setValue:dictionaryArray forKey:[property jsonName]]; 138 | } 139 | } 140 | } else { 141 | if ([property resourceType] || [value conformsToProtocol:@protocol(JSONAPIResource)]) { 142 | if (linkage == nil) { 143 | linkage = [[NSMutableDictionary alloc] init]; 144 | } 145 | 146 | NSObject *attribute = value; 147 | [linkage setValue:[self link:attribute from:resource withKey:[property jsonName]] forKey:[property jsonName]]; 148 | } else { 149 | format = [property formatter]; 150 | if (format) { 151 | [attributes setValue:[format stringForObjectValue:value] forKey:[property jsonName]]; 152 | } else { 153 | [attributes setValue:value forKey:[property jsonName]]; 154 | } 155 | } 156 | } 157 | } 158 | } 159 | 160 | if (attributes.count > 0) { 161 | [dictionary setValue:attributes forKey:@"attributes"]; 162 | } 163 | 164 | if (linkage) { 165 | [dictionary setValue:linkage forKey:@"relationships"]; 166 | } 167 | 168 | // TODO: Need to also add in all other links 169 | if (resource.selfLink) { 170 | dictionary[@"links"] = @{ @"self": resource.selfLink }; 171 | } 172 | 173 | return dictionary; 174 | } 175 | 176 | + (void)set:(NSObject *)resource withDictionary:dictionary { 177 | NSString *error; 178 | 179 | JSONAPIResourceDescriptor *descriptor = [[resource class] descriptor]; 180 | 181 | NSDictionary *relationships = [dictionary objectForKey:@"relationships"]; 182 | NSDictionary *attributes = [dictionary objectForKey:@"attributes"]; 183 | NSDictionary *links = [dictionary objectForKey:@"links"]; 184 | NSDictionary *meta = [dictionary objectForKey:@"meta"]; 185 | 186 | id ID = [dictionary objectForKey:@"id"]; 187 | NSFormatter *format = [descriptor idFormatter]; 188 | if (format) { 189 | id xformed; 190 | if ([format getObjectValue:&xformed forString:ID errorDescription:&error]) { 191 | [resource setValue:xformed forKey:[descriptor idProperty]]; 192 | } 193 | } else { 194 | [resource setValue:ID forKey:[descriptor idProperty]]; 195 | } 196 | 197 | if (descriptor.selfLinkProperty) { 198 | NSString *selfLink = links[@"self"]; 199 | [resource setValue:selfLink forKey:descriptor.selfLinkProperty]; 200 | } 201 | 202 | if ([resource respondsToSelector:@selector(setMeta:)]) { 203 | [resource setMeta:meta]; 204 | } 205 | 206 | // Loops through all keys to map to properties 207 | NSDictionary *properties = [descriptor properties]; 208 | for (NSString *key in properties) { 209 | JSONAPIPropertyDescriptor *property = [properties objectForKey:key]; 210 | 211 | if (property.resourceType) { 212 | if (relationships) { 213 | id value = [relationships objectForKey:[property jsonName]]; 214 | if (value[@"data"] != [NSNull null]) { 215 | [resource setValue:[JSONAPIResourceParser jsonAPILink:value] forKey:key]; 216 | } 217 | } 218 | 219 | } else if (relationships[key]) { 220 | if (relationships) { 221 | id value = relationships[key]; 222 | if (value[@"data"] != [NSNull null]) { 223 | [resource setValue:[JSONAPIResourceParser jsonAPILink:value] forKey:key]; 224 | } 225 | } 226 | } else { 227 | id value = [attributes objectForKey:[property jsonName]]; 228 | if ((id)[NSNull null] == value) { 229 | value = [dictionary objectForKey:[property jsonName]]; 230 | } 231 | 232 | if (value) { 233 | // anything else should be a value property 234 | format = [property formatter]; 235 | 236 | if ([value isKindOfClass:[NSArray class]]) { 237 | if (format) { 238 | NSMutableArray *temp = [value mutableCopy]; 239 | for (int i = 0; i < [value count]; ++i) { 240 | id xformed; 241 | if ([format getObjectValue:&xformed forString:temp[i] errorDescription:&error]) { 242 | temp[i] = xformed; 243 | } 244 | } 245 | [resource setValue:temp forKey:key]; 246 | } else { 247 | [resource setValue:[[NSArray alloc] initWithArray:value] forKey:key]; 248 | } 249 | 250 | } else { 251 | if (format) { 252 | id xformed; 253 | if ([format getObjectValue:&xformed forString:value errorDescription:&error]) { 254 | [resource setValue:xformed forKey:key]; 255 | } 256 | } else { 257 | [resource setValue:value forKey:key]; 258 | } 259 | } 260 | } 261 | } 262 | } 263 | } 264 | 265 | + (id)jsonAPILink:(NSDictionary*)dictionary { 266 | id linkage = dictionary[@"data"]; 267 | if ([linkage isKindOfClass:[NSArray class]]) { 268 | NSMutableArray *linkArray = [[NSMutableArray alloc] initWithCapacity:[linkage count]]; 269 | for (NSDictionary *linkElement in linkage) { 270 | [linkArray addObject:[JSONAPIResourceParser parseResource:linkElement]]; 271 | } 272 | 273 | return linkArray; 274 | 275 | } else { 276 | return [JSONAPIResourceParser parseResource:linkage]; 277 | } 278 | } 279 | 280 | 281 | + (void)link:(NSObject *)resource withIncluded:(JSONAPI*)jsonAPI { 282 | 283 | NSDictionary *included = jsonAPI.includedResources; 284 | if (nil == included) return; 285 | 286 | JSONAPIResourceDescriptor *descriptor = [[resource class] descriptor]; 287 | 288 | // Loops through all keys to map to properties 289 | NSDictionary *properties = [descriptor properties]; 290 | for (NSString *key in properties) { 291 | JSONAPIPropertyDescriptor *propertyDescriptor = [properties objectForKey:key]; 292 | id value = [resource valueForKey:key]; 293 | 294 | Class valueClass = nil; 295 | if (propertyDescriptor.resourceType) { 296 | valueClass = propertyDescriptor.resourceType; 297 | } else if ([value conformsToProtocol:@protocol(JSONAPIResource)] || [value isKindOfClass:[NSArray class]]) { 298 | valueClass = [value class]; 299 | } 300 | 301 | // ordinary attribute 302 | if (valueClass == nil) { 303 | continue; 304 | // has many 305 | } else if ([value isKindOfClass:[NSArray class]]) { 306 | NSMutableArray *matched = [value mutableCopy]; 307 | [value enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 308 | if ([obj conformsToProtocol:@protocol(JSONAPIResource)]) { 309 | NSObject *res = obj; 310 | id includedValue = included[[[res.class descriptor] type]]; 311 | if (includedValue) { 312 | id v = includedValue[res.ID]; 313 | if (v != nil) { 314 | matched[idx] = v; 315 | } 316 | } 317 | } 318 | }]; 319 | 320 | [resource setValue:matched forKey:key]; 321 | // has one 322 | } else if (value != nil) { 323 | if ([value conformsToProtocol:@protocol(JSONAPIResource)]) { 324 | id res = value; 325 | id includedValue = included[[[res.class descriptor] type]]; 326 | if (includedValue) { 327 | id v = included[[[res.class descriptor] type]][res.ID]; 328 | if (v != nil) { 329 | [resource setValue:v forKey:key]; 330 | } 331 | } 332 | } 333 | } 334 | } 335 | } 336 | 337 | + (NSArray*)relatedResourcesFor:(NSObject *)resource { 338 | NSMutableArray *related = [[NSMutableArray alloc] init]; 339 | 340 | JSONAPIResourceDescriptor *descriptor = [[resource class] descriptor]; 341 | 342 | // Loops through all keys to map to properties 343 | NSDictionary *properties = [descriptor properties]; 344 | for (NSString *key in properties) { 345 | JSONAPIPropertyDescriptor *property = [properties objectForKey:key]; 346 | if (property.resourceType) { 347 | id value = [resource valueForKey:key]; 348 | if ([value isKindOfClass:[NSArray class]]) { 349 | [related addObjectsFromArray:value]; 350 | } else { 351 | [related addObject:value]; 352 | } 353 | } 354 | } 355 | return related; 356 | } 357 | 358 | + (NSDictionary*)link:(NSObject *)resource from:(NSObject *)owner withKey:(NSString*)key { 359 | JSONAPIResourceDescriptor *descriptor = [[resource class] descriptor]; 360 | NSMutableDictionary *reference = [[NSMutableDictionary alloc] init]; 361 | 362 | if (owner.selfLink) { 363 | NSMutableString *link_to_self = [owner.selfLink mutableCopy]; 364 | [link_to_self appendString:@"/links/"]; 365 | [link_to_self appendString:key]; 366 | [reference setValue:link_to_self forKey:@"self"]; 367 | 368 | NSMutableString *related = [owner.selfLink mutableCopy]; 369 | [related appendString:@"/"]; 370 | [related appendString:key]; 371 | [reference setValue:related forKey:@"related"]; 372 | } 373 | 374 | if (resource.ID) { 375 | NSDictionary *referenceObject = @{ 376 | @"type" : descriptor.type, 377 | @"id" : resource.ID 378 | }; 379 | if ([[owner valueForKey:key] isKindOfClass:[NSArray class]]) { 380 | reference = referenceObject.mutableCopy; 381 | } else { 382 | [reference setValue:referenceObject forKey:@"data"]; 383 | } 384 | } 385 | 386 | return reference; 387 | } 388 | 389 | @end 390 | -------------------------------------------------------------------------------- /Classes/NSDateFormatter+JSONAPIDateFormatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDateFormatter+JSONAPIDateFormatter.h 3 | // 4 | 5 | #import 6 | 7 | /** 8 | * Adds RFC 3339 formater to NSDateFormatter 9 | */ 10 | @interface NSDateFormatter (JSONAPIDateFormatter) 11 | 12 | /** 13 | * NSDate formatter that conforms to RFC 3339. This is a common JSON convention for dates. 14 | * Format string is "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'". 15 | */ 16 | + (instancetype)RFC3339DateFormatter; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Classes/NSDateFormatter+JSONAPIDateFormatter.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDateFormatter+JSONAPIDateFormatter.m 3 | // 4 | 5 | #import "NSDateFormatter+JSONAPIDateFormatter.h" 6 | 7 | @implementation NSDateFormatter (JSONAPIDateFormatter) 8 | 9 | + (instancetype)RFC3339DateFormatter 10 | { 11 | static NSDateFormatter *dateFormatter = nil; 12 | 13 | static dispatch_once_t onceToken; 14 | dispatch_once(&onceToken, ^{ 15 | dateFormatter = [[NSDateFormatter alloc] init]; 16 | NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier: @"en_US_POSIX"]; 17 | 18 | [dateFormatter setLocale: enUSPOSIXLocale]; 19 | [dateFormatter setDateFormat: @"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'"]; 20 | [dateFormatter setTimeZone: [NSTimeZone timeZoneForSecondsFromGMT: 0]]; 21 | }); 22 | 23 | return dateFormatter; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 03A0552F1868D617004807F0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A0552E1868D617004807F0 /* Foundation.framework */; }; 11 | 03A055311868D617004807F0 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A055301868D617004807F0 /* CoreGraphics.framework */; }; 12 | 03A055331868D617004807F0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A055321868D617004807F0 /* UIKit.framework */; }; 13 | 03A055391868D617004807F0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 03A055371868D617004807F0 /* InfoPlist.strings */; }; 14 | 03A0553B1868D617004807F0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A0553A1868D617004807F0 /* main.m */; }; 15 | 03A0553F1868D617004807F0 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A0553E1868D617004807F0 /* AppDelegate.m */; }; 16 | 03A055421868D617004807F0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 03A055401868D617004807F0 /* Main.storyboard */; }; 17 | 03A055451868D617004807F0 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A055441868D617004807F0 /* ViewController.m */; }; 18 | 03A055471868D617004807F0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03A055461868D617004807F0 /* Images.xcassets */; }; 19 | 03A0554E1868D617004807F0 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A0554D1868D617004807F0 /* XCTest.framework */; }; 20 | 03A0554F1868D617004807F0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A0552E1868D617004807F0 /* Foundation.framework */; }; 21 | 03A055501868D617004807F0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A055321868D617004807F0 /* UIKit.framework */; }; 22 | 03A055581868D617004807F0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 03A055561868D617004807F0 /* InfoPlist.strings */; }; 23 | 03A0555A1868D617004807F0 /* JSONAPIExample1Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A055591868D617004807F0 /* JSONAPIExample1Tests.m */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 03A055511868D617004807F0 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 03A055231868D617004807F0 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 03A0552A1868D617004807F0; 32 | remoteInfo = JSONAPIExample1; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | 03A0552B1868D617004807F0 /* JSONAPIExample1.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JSONAPIExample1.app; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 03A0552E1868D617004807F0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 39 | 03A055301868D617004807F0 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 40 | 03A055321868D617004807F0 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 41 | 03A055361868D617004807F0 /* JSONAPIExample1-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "JSONAPIExample1-Info.plist"; sourceTree = ""; }; 42 | 03A055381868D617004807F0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 43 | 03A0553A1868D617004807F0 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 44 | 03A0553C1868D617004807F0 /* JSONAPIExample1-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JSONAPIExample1-Prefix.pch"; sourceTree = ""; }; 45 | 03A0553D1868D617004807F0 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 46 | 03A0553E1868D617004807F0 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 47 | 03A055411868D617004807F0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | 03A055431868D617004807F0 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 49 | 03A055441868D617004807F0 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 50 | 03A055461868D617004807F0 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 51 | 03A0554C1868D617004807F0 /* JSONAPIExample1Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JSONAPIExample1Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 03A0554D1868D617004807F0 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 53 | 03A055551868D617004807F0 /* JSONAPIExample1Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "JSONAPIExample1Tests-Info.plist"; sourceTree = ""; }; 54 | 03A055571868D617004807F0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 55 | 03A055591868D617004807F0 /* JSONAPIExample1Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSONAPIExample1Tests.m; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 03A055281868D617004807F0 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | 03A055311868D617004807F0 /* CoreGraphics.framework in Frameworks */, 64 | 03A055331868D617004807F0 /* UIKit.framework in Frameworks */, 65 | 03A0552F1868D617004807F0 /* Foundation.framework in Frameworks */, 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | 03A055491868D617004807F0 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | 03A0554E1868D617004807F0 /* XCTest.framework in Frameworks */, 74 | 03A055501868D617004807F0 /* UIKit.framework in Frameworks */, 75 | 03A0554F1868D617004807F0 /* Foundation.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | 03A055221868D617004807F0 = { 83 | isa = PBXGroup; 84 | children = ( 85 | 03A055341868D617004807F0 /* JSONAPIExample1 */, 86 | 03A055531868D617004807F0 /* JSONAPIExample1Tests */, 87 | 03A0552D1868D617004807F0 /* Frameworks */, 88 | 03A0552C1868D617004807F0 /* Products */, 89 | ); 90 | sourceTree = ""; 91 | }; 92 | 03A0552C1868D617004807F0 /* Products */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 03A0552B1868D617004807F0 /* JSONAPIExample1.app */, 96 | 03A0554C1868D617004807F0 /* JSONAPIExample1Tests.xctest */, 97 | ); 98 | name = Products; 99 | sourceTree = ""; 100 | }; 101 | 03A0552D1868D617004807F0 /* Frameworks */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 03A0552E1868D617004807F0 /* Foundation.framework */, 105 | 03A055301868D617004807F0 /* CoreGraphics.framework */, 106 | 03A055321868D617004807F0 /* UIKit.framework */, 107 | 03A0554D1868D617004807F0 /* XCTest.framework */, 108 | ); 109 | name = Frameworks; 110 | sourceTree = ""; 111 | }; 112 | 03A055341868D617004807F0 /* JSONAPIExample1 */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 03A0553D1868D617004807F0 /* AppDelegate.h */, 116 | 03A0553E1868D617004807F0 /* AppDelegate.m */, 117 | 03A055401868D617004807F0 /* Main.storyboard */, 118 | 03A055431868D617004807F0 /* ViewController.h */, 119 | 03A055441868D617004807F0 /* ViewController.m */, 120 | 03A055461868D617004807F0 /* Images.xcassets */, 121 | 03A055351868D617004807F0 /* Supporting Files */, 122 | ); 123 | path = JSONAPIExample1; 124 | sourceTree = ""; 125 | }; 126 | 03A055351868D617004807F0 /* Supporting Files */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 03A055361868D617004807F0 /* JSONAPIExample1-Info.plist */, 130 | 03A055371868D617004807F0 /* InfoPlist.strings */, 131 | 03A0553A1868D617004807F0 /* main.m */, 132 | 03A0553C1868D617004807F0 /* JSONAPIExample1-Prefix.pch */, 133 | ); 134 | name = "Supporting Files"; 135 | sourceTree = ""; 136 | }; 137 | 03A055531868D617004807F0 /* JSONAPIExample1Tests */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 03A055591868D617004807F0 /* JSONAPIExample1Tests.m */, 141 | 03A055541868D617004807F0 /* Supporting Files */, 142 | ); 143 | path = JSONAPIExample1Tests; 144 | sourceTree = ""; 145 | }; 146 | 03A055541868D617004807F0 /* Supporting Files */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 03A055551868D617004807F0 /* JSONAPIExample1Tests-Info.plist */, 150 | 03A055561868D617004807F0 /* InfoPlist.strings */, 151 | ); 152 | name = "Supporting Files"; 153 | sourceTree = ""; 154 | }; 155 | /* End PBXGroup section */ 156 | 157 | /* Begin PBXNativeTarget section */ 158 | 03A0552A1868D617004807F0 /* JSONAPIExample1 */ = { 159 | isa = PBXNativeTarget; 160 | buildConfigurationList = 03A0555D1868D617004807F0 /* Build configuration list for PBXNativeTarget "JSONAPIExample1" */; 161 | buildPhases = ( 162 | 03A055271868D617004807F0 /* Sources */, 163 | 03A055281868D617004807F0 /* Frameworks */, 164 | 03A055291868D617004807F0 /* Resources */, 165 | ); 166 | buildRules = ( 167 | ); 168 | dependencies = ( 169 | ); 170 | name = JSONAPIExample1; 171 | productName = JSONAPIExample1; 172 | productReference = 03A0552B1868D617004807F0 /* JSONAPIExample1.app */; 173 | productType = "com.apple.product-type.application"; 174 | }; 175 | 03A0554B1868D617004807F0 /* JSONAPIExample1Tests */ = { 176 | isa = PBXNativeTarget; 177 | buildConfigurationList = 03A055601868D617004807F0 /* Build configuration list for PBXNativeTarget "JSONAPIExample1Tests" */; 178 | buildPhases = ( 179 | 03A055481868D617004807F0 /* Sources */, 180 | 03A055491868D617004807F0 /* Frameworks */, 181 | 03A0554A1868D617004807F0 /* Resources */, 182 | ); 183 | buildRules = ( 184 | ); 185 | dependencies = ( 186 | 03A055521868D617004807F0 /* PBXTargetDependency */, 187 | ); 188 | name = JSONAPIExample1Tests; 189 | productName = JSONAPIExample1Tests; 190 | productReference = 03A0554C1868D617004807F0 /* JSONAPIExample1Tests.xctest */; 191 | productType = "com.apple.product-type.bundle.unit-test"; 192 | }; 193 | /* End PBXNativeTarget section */ 194 | 195 | /* Begin PBXProject section */ 196 | 03A055231868D617004807F0 /* Project object */ = { 197 | isa = PBXProject; 198 | attributes = { 199 | LastUpgradeCheck = 0500; 200 | ORGANIZATIONNAME = "Josh Holtz"; 201 | TargetAttributes = { 202 | 03A0554B1868D617004807F0 = { 203 | TestTargetID = 03A0552A1868D617004807F0; 204 | }; 205 | }; 206 | }; 207 | buildConfigurationList = 03A055261868D617004807F0 /* Build configuration list for PBXProject "JSONAPIExample1" */; 208 | compatibilityVersion = "Xcode 3.2"; 209 | developmentRegion = English; 210 | hasScannedForEncodings = 0; 211 | knownRegions = ( 212 | en, 213 | Base, 214 | ); 215 | mainGroup = 03A055221868D617004807F0; 216 | productRefGroup = 03A0552C1868D617004807F0 /* Products */; 217 | projectDirPath = ""; 218 | projectRoot = ""; 219 | targets = ( 220 | 03A0552A1868D617004807F0 /* JSONAPIExample1 */, 221 | 03A0554B1868D617004807F0 /* JSONAPIExample1Tests */, 222 | ); 223 | }; 224 | /* End PBXProject section */ 225 | 226 | /* Begin PBXResourcesBuildPhase section */ 227 | 03A055291868D617004807F0 /* Resources */ = { 228 | isa = PBXResourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | 03A055471868D617004807F0 /* Images.xcassets in Resources */, 232 | 03A055391868D617004807F0 /* InfoPlist.strings in Resources */, 233 | 03A055421868D617004807F0 /* Main.storyboard in Resources */, 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | 03A0554A1868D617004807F0 /* Resources */ = { 238 | isa = PBXResourcesBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | 03A055581868D617004807F0 /* InfoPlist.strings in Resources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | /* End PBXResourcesBuildPhase section */ 246 | 247 | /* Begin PBXSourcesBuildPhase section */ 248 | 03A055271868D617004807F0 /* Sources */ = { 249 | isa = PBXSourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | 03A055451868D617004807F0 /* ViewController.m in Sources */, 253 | 03A0553F1868D617004807F0 /* AppDelegate.m in Sources */, 254 | 03A0553B1868D617004807F0 /* main.m in Sources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | 03A055481868D617004807F0 /* Sources */ = { 259 | isa = PBXSourcesBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | 03A0555A1868D617004807F0 /* JSONAPIExample1Tests.m in Sources */, 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | }; 266 | /* End PBXSourcesBuildPhase section */ 267 | 268 | /* Begin PBXTargetDependency section */ 269 | 03A055521868D617004807F0 /* PBXTargetDependency */ = { 270 | isa = PBXTargetDependency; 271 | target = 03A0552A1868D617004807F0 /* JSONAPIExample1 */; 272 | targetProxy = 03A055511868D617004807F0 /* PBXContainerItemProxy */; 273 | }; 274 | /* End PBXTargetDependency section */ 275 | 276 | /* Begin PBXVariantGroup section */ 277 | 03A055371868D617004807F0 /* InfoPlist.strings */ = { 278 | isa = PBXVariantGroup; 279 | children = ( 280 | 03A055381868D617004807F0 /* en */, 281 | ); 282 | name = InfoPlist.strings; 283 | sourceTree = ""; 284 | }; 285 | 03A055401868D617004807F0 /* Main.storyboard */ = { 286 | isa = PBXVariantGroup; 287 | children = ( 288 | 03A055411868D617004807F0 /* Base */, 289 | ); 290 | name = Main.storyboard; 291 | sourceTree = ""; 292 | }; 293 | 03A055561868D617004807F0 /* InfoPlist.strings */ = { 294 | isa = PBXVariantGroup; 295 | children = ( 296 | 03A055571868D617004807F0 /* en */, 297 | ); 298 | name = InfoPlist.strings; 299 | sourceTree = ""; 300 | }; 301 | /* End PBXVariantGroup section */ 302 | 303 | /* Begin XCBuildConfiguration section */ 304 | 03A0555B1868D617004807F0 /* Debug */ = { 305 | isa = XCBuildConfiguration; 306 | buildSettings = { 307 | ALWAYS_SEARCH_USER_PATHS = NO; 308 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 309 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 310 | CLANG_CXX_LIBRARY = "libc++"; 311 | CLANG_ENABLE_MODULES = YES; 312 | CLANG_ENABLE_OBJC_ARC = YES; 313 | CLANG_WARN_BOOL_CONVERSION = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 316 | CLANG_WARN_EMPTY_BODY = YES; 317 | CLANG_WARN_ENUM_CONVERSION = YES; 318 | CLANG_WARN_INT_CONVERSION = YES; 319 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 320 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 321 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 322 | COPY_PHASE_STRIP = NO; 323 | GCC_C_LANGUAGE_STANDARD = gnu99; 324 | GCC_DYNAMIC_NO_PIC = NO; 325 | GCC_OPTIMIZATION_LEVEL = 0; 326 | GCC_PREPROCESSOR_DEFINITIONS = ( 327 | "DEBUG=1", 328 | "$(inherited)", 329 | ); 330 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 331 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 332 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 333 | GCC_WARN_UNDECLARED_SELECTOR = YES; 334 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 335 | GCC_WARN_UNUSED_FUNCTION = YES; 336 | GCC_WARN_UNUSED_VARIABLE = YES; 337 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 338 | ONLY_ACTIVE_ARCH = YES; 339 | SDKROOT = iphoneos; 340 | }; 341 | name = Debug; 342 | }; 343 | 03A0555C1868D617004807F0 /* Release */ = { 344 | isa = XCBuildConfiguration; 345 | buildSettings = { 346 | ALWAYS_SEARCH_USER_PATHS = NO; 347 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 348 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 349 | CLANG_CXX_LIBRARY = "libc++"; 350 | CLANG_ENABLE_MODULES = YES; 351 | CLANG_ENABLE_OBJC_ARC = YES; 352 | CLANG_WARN_BOOL_CONVERSION = YES; 353 | CLANG_WARN_CONSTANT_CONVERSION = YES; 354 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 355 | CLANG_WARN_EMPTY_BODY = YES; 356 | CLANG_WARN_ENUM_CONVERSION = YES; 357 | CLANG_WARN_INT_CONVERSION = YES; 358 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 359 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 360 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 361 | COPY_PHASE_STRIP = YES; 362 | ENABLE_NS_ASSERTIONS = NO; 363 | GCC_C_LANGUAGE_STANDARD = gnu99; 364 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 365 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 366 | GCC_WARN_UNDECLARED_SELECTOR = YES; 367 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 368 | GCC_WARN_UNUSED_FUNCTION = YES; 369 | GCC_WARN_UNUSED_VARIABLE = YES; 370 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 371 | SDKROOT = iphoneos; 372 | VALIDATE_PRODUCT = YES; 373 | }; 374 | name = Release; 375 | }; 376 | 03A0555E1868D617004807F0 /* Debug */ = { 377 | isa = XCBuildConfiguration; 378 | buildSettings = { 379 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 380 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 381 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 382 | GCC_PREFIX_HEADER = "JSONAPIExample1/JSONAPIExample1-Prefix.pch"; 383 | INFOPLIST_FILE = "JSONAPIExample1/JSONAPIExample1-Info.plist"; 384 | PRODUCT_NAME = "$(TARGET_NAME)"; 385 | WRAPPER_EXTENSION = app; 386 | }; 387 | name = Debug; 388 | }; 389 | 03A0555F1868D617004807F0 /* Release */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 393 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 394 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 395 | GCC_PREFIX_HEADER = "JSONAPIExample1/JSONAPIExample1-Prefix.pch"; 396 | INFOPLIST_FILE = "JSONAPIExample1/JSONAPIExample1-Info.plist"; 397 | PRODUCT_NAME = "$(TARGET_NAME)"; 398 | WRAPPER_EXTENSION = app; 399 | }; 400 | name = Release; 401 | }; 402 | 03A055611868D617004807F0 /* Debug */ = { 403 | isa = XCBuildConfiguration; 404 | buildSettings = { 405 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 406 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/JSONAPIExample1.app/JSONAPIExample1"; 407 | FRAMEWORK_SEARCH_PATHS = ( 408 | "$(SDKROOT)/Developer/Library/Frameworks", 409 | "$(inherited)", 410 | "$(DEVELOPER_FRAMEWORKS_DIR)", 411 | ); 412 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 413 | GCC_PREFIX_HEADER = "JSONAPIExample1/JSONAPIExample1-Prefix.pch"; 414 | GCC_PREPROCESSOR_DEFINITIONS = ( 415 | "DEBUG=1", 416 | "$(inherited)", 417 | ); 418 | INFOPLIST_FILE = "JSONAPIExample1Tests/JSONAPIExample1Tests-Info.plist"; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | TEST_HOST = "$(BUNDLE_LOADER)"; 421 | WRAPPER_EXTENSION = xctest; 422 | }; 423 | name = Debug; 424 | }; 425 | 03A055621868D617004807F0 /* Release */ = { 426 | isa = XCBuildConfiguration; 427 | buildSettings = { 428 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 429 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/JSONAPIExample1.app/JSONAPIExample1"; 430 | FRAMEWORK_SEARCH_PATHS = ( 431 | "$(SDKROOT)/Developer/Library/Frameworks", 432 | "$(inherited)", 433 | "$(DEVELOPER_FRAMEWORKS_DIR)", 434 | ); 435 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 436 | GCC_PREFIX_HEADER = "JSONAPIExample1/JSONAPIExample1-Prefix.pch"; 437 | INFOPLIST_FILE = "JSONAPIExample1Tests/JSONAPIExample1Tests-Info.plist"; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | TEST_HOST = "$(BUNDLE_LOADER)"; 440 | WRAPPER_EXTENSION = xctest; 441 | }; 442 | name = Release; 443 | }; 444 | /* End XCBuildConfiguration section */ 445 | 446 | /* Begin XCConfigurationList section */ 447 | 03A055261868D617004807F0 /* Build configuration list for PBXProject "JSONAPIExample1" */ = { 448 | isa = XCConfigurationList; 449 | buildConfigurations = ( 450 | 03A0555B1868D617004807F0 /* Debug */, 451 | 03A0555C1868D617004807F0 /* Release */, 452 | ); 453 | defaultConfigurationIsVisible = 0; 454 | defaultConfigurationName = Release; 455 | }; 456 | 03A0555D1868D617004807F0 /* Build configuration list for PBXNativeTarget "JSONAPIExample1" */ = { 457 | isa = XCConfigurationList; 458 | buildConfigurations = ( 459 | 03A0555E1868D617004807F0 /* Debug */, 460 | 03A0555F1868D617004807F0 /* Release */, 461 | ); 462 | defaultConfigurationIsVisible = 0; 463 | }; 464 | 03A055601868D617004807F0 /* Build configuration list for PBXNativeTarget "JSONAPIExample1Tests" */ = { 465 | isa = XCConfigurationList; 466 | buildConfigurations = ( 467 | 03A055611868D617004807F0 /* Debug */, 468 | 03A055621868D617004807F0 /* Release */, 469 | ); 470 | defaultConfigurationIsVisible = 0; 471 | }; 472 | /* End XCConfigurationList section */ 473 | }; 474 | rootObject = 03A055231868D617004807F0 /* Project object */; 475 | } 476 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // JSONAPIExample1 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // JSONAPIExample1 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/JSONAPIExample1-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.joshholtz.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/JSONAPIExample1-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // JSONAPIExample1 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // JSONAPIExample1 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad 18 | { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view, typically from a nib. 21 | } 22 | 23 | - (void)didReceiveMemoryWarning 24 | { 25 | [super didReceiveMemoryWarning]; 26 | // Dispose of any resources that can be recreated. 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JSONAPIExample1 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1Tests/JSONAPIExample1Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.joshholtz.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1Tests/JSONAPIExample1Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPIExample1Tests.m 3 | // JSONAPIExample1Tests 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JSONAPIExample1Tests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation JSONAPIExample1Tests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Examples/JSONAPIExample1/JSONAPIExample1Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Examples/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '7.0' 2 | -------------------------------------------------------------------------------- /JSONAPI.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "JSONAPI" 3 | s.version = "1.0.7" 4 | s.summary = "A library for loading data from a JSON API datasource." 5 | s.description = <<-DESC 6 | A library for loading data from a JSON API datasource. Parses JSON API data into models with support for auto-linking of resources and custom model classes. 7 | DESC 8 | s.homepage = "https://github.com/joshdholtz/jsonapi-ios.git" 9 | #s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" 10 | s.license = 'MIT' 11 | s.author = { "Josh Holtz" => "me@joshholtz.com" } 12 | s.source = { :git => "https://github.com/joshdholtz/jsonapi-ios.git", :tag => s.version.to_s } 13 | 14 | s.platform = :ios, '7.0' 15 | s.ios.deployment_target = '5.0' 16 | s.requires_arc = true 17 | 18 | s.public_header_files = 'Classes/*.h' 19 | s.source_files = 'Classes/*' 20 | end 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Josh Holtz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Project/JSONAPI.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | 689B17A11AFFE0EA00D2854E /* Documentation */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = 689B17A41AFFE0EA00D2854E /* Build configuration list for PBXAggregateTarget "Documentation" */; 13 | buildPhases = ( 14 | 689B17A51AFFE0FD00D2854E /* ShellScript */, 15 | ); 16 | dependencies = ( 17 | ); 18 | name = Documentation; 19 | productName = Documentation; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 03866451186A909200985CEC /* PeopleResource.m in Sources */ = {isa = PBXBuildFile; fileRef = 03866450186A909200985CEC /* PeopleResource.m */; }; 25 | 03866454186A94B700985CEC /* CommentResource.m in Sources */ = {isa = PBXBuildFile; fileRef = 03866453186A94B700985CEC /* CommentResource.m */; }; 26 | 03866457186A94C200985CEC /* ArticleResource.m in Sources */ = {isa = PBXBuildFile; fileRef = 03866456186A94C200985CEC /* ArticleResource.m */; }; 27 | 03866459186CCE6600985CEC /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 03866458186CCE6600985CEC /* CHANGELOG.md */; }; 28 | 03A055BB1868E038004807F0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A055BA1868E038004807F0 /* Foundation.framework */; }; 29 | 03A055BD1868E038004807F0 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A055BC1868E038004807F0 /* CoreGraphics.framework */; }; 30 | 03A055BF1868E038004807F0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A055BE1868E038004807F0 /* UIKit.framework */; }; 31 | 03A055C51868E038004807F0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 03A055C31868E038004807F0 /* InfoPlist.strings */; }; 32 | 03A055C71868E038004807F0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A055C61868E038004807F0 /* main.m */; }; 33 | 03A055CB1868E038004807F0 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A055CA1868E038004807F0 /* AppDelegate.m */; }; 34 | 03A055CE1868E038004807F0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 03A055CC1868E038004807F0 /* Main.storyboard */; }; 35 | 03A055D11868E038004807F0 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A055D01868E038004807F0 /* ViewController.m */; }; 36 | 03A055D31868E038004807F0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03A055D21868E038004807F0 /* Images.xcassets */; }; 37 | 03A055DA1868E038004807F0 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A055D91868E038004807F0 /* XCTest.framework */; }; 38 | 03A055DB1868E038004807F0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A055BA1868E038004807F0 /* Foundation.framework */; }; 39 | 03A055DC1868E038004807F0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A055BE1868E038004807F0 /* UIKit.framework */; }; 40 | 03A055E41868E038004807F0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 03A055E21868E038004807F0 /* InfoPlist.strings */; }; 41 | 03A055E61868E038004807F0 /* JSONAPITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A055E51868E038004807F0 /* JSONAPITests.m */; }; 42 | 03A055F71868E08A004807F0 /* JSONAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 03A055F41868E08A004807F0 /* JSONAPI.m */; }; 43 | 03A055F81868E08A004807F0 /* JSONAPI.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 03A055F51868E08A004807F0 /* JSONAPI.podspec */; }; 44 | 03FBD8DA1AB879FD00789DF3 /* main_example.json in Resources */ = {isa = PBXBuildFile; fileRef = 03FBD8D91AB879FD00789DF3 /* main_example.json */; }; 45 | 03FBD8DC1AB87EE000789DF3 /* main_example.json in Resources */ = {isa = PBXBuildFile; fileRef = 03FBD8D91AB879FD00789DF3 /* main_example.json */; }; 46 | 03FBD8DF1AB8DF8E00789DF3 /* JSONAPIErrorResource.m in Sources */ = {isa = PBXBuildFile; fileRef = 03FBD8DE1AB8DF8E00789DF3 /* JSONAPIErrorResource.m */; }; 47 | 03FBD8E21AB8E46E00789DF3 /* error_example.json in Resources */ = {isa = PBXBuildFile; fileRef = 03FBD8E11AB8E46E00789DF3 /* error_example.json */; }; 48 | 03FBD8E31AB8E46E00789DF3 /* error_example.json in Resources */ = {isa = PBXBuildFile; fileRef = 03FBD8E11AB8E46E00789DF3 /* error_example.json */; }; 49 | 680E14F51B08146E004FF8CD /* JSONAPIResourceParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 680E14F41B08146E004FF8CD /* JSONAPIResourceParser.m */; }; 50 | 681B47761B08EA9800A99D76 /* JSONAPIResourceBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 681B47751B08EA9800A99D76 /* JSONAPIResourceBase.m */; }; 51 | 685481EB1AE4161900D3A633 /* JSONAPIPropertyDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 685481EA1AE4161900D3A633 /* JSONAPIPropertyDescriptor.m */; }; 52 | 685481EF1AE419CB00D3A633 /* JSONAPIResourceDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 685481EE1AE419CB00D3A633 /* JSONAPIResourceDescriptor.m */; }; 53 | 68A469941AE47E0000E7BBC8 /* NSDateFormatter+JSONAPIDateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A469931AE47E0000E7BBC8 /* NSDateFormatter+JSONAPIDateFormatter.m */; }; 54 | 7817CDAE1C39A51100D864E6 /* empty_relationship_example.json in Resources */ = {isa = PBXBuildFile; fileRef = 7817CDAD1C39A51100D864E6 /* empty_relationship_example.json */; }; 55 | 7817CDAF1C39A85000D864E6 /* empty_relationship_example.json in Resources */ = {isa = PBXBuildFile; fileRef = 7817CDAD1C39A51100D864E6 /* empty_relationship_example.json */; }; 56 | 781CF6CA1C1ECC260052D755 /* generic_relationships_example.json in Resources */ = {isa = PBXBuildFile; fileRef = 781CF6C91C1ECC260052D755 /* generic_relationships_example.json */; }; 57 | 781CF6CB1C1ECC710052D755 /* generic_relationships_example.json in Resources */ = {isa = PBXBuildFile; fileRef = 781CF6C91C1ECC260052D755 /* generic_relationships_example.json */; }; 58 | 78A5C1CA1C1E1336008C8632 /* NewsFeedPostResource.m in Sources */ = {isa = PBXBuildFile; fileRef = 78A5C1C91C1E1336008C8632 /* NewsFeedPostResource.m */; }; 59 | 78A5C1CD1C1E13A8008C8632 /* UserResource.m in Sources */ = {isa = PBXBuildFile; fileRef = 78A5C1CC1C1E13A8008C8632 /* UserResource.m */; }; 60 | 78A5C1D01C1E147B008C8632 /* SocialCommunityResource.m in Sources */ = {isa = PBXBuildFile; fileRef = 78A5C1CF1C1E147B008C8632 /* SocialCommunityResource.m */; }; 61 | 78A5C1D31C1E14D6008C8632 /* MediaResource.m in Sources */ = {isa = PBXBuildFile; fileRef = 78A5C1D21C1E14D6008C8632 /* MediaResource.m */; }; 62 | 78A5C1D61C1E154C008C8632 /* WebPageResource.m in Sources */ = {isa = PBXBuildFile; fileRef = 78A5C1D51C1E154C008C8632 /* WebPageResource.m */; }; 63 | /* End PBXBuildFile section */ 64 | 65 | /* Begin PBXContainerItemProxy section */ 66 | 03A055DD1868E038004807F0 /* PBXContainerItemProxy */ = { 67 | isa = PBXContainerItemProxy; 68 | containerPortal = 03A055AF1868E038004807F0 /* Project object */; 69 | proxyType = 1; 70 | remoteGlobalIDString = 03A055B61868E038004807F0; 71 | remoteInfo = JSONAPI; 72 | }; 73 | /* End PBXContainerItemProxy section */ 74 | 75 | /* Begin PBXFileReference section */ 76 | 033A6C5D18695CD2001CF9FA /* JSONAPIResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONAPIResource.h; sourceTree = ""; }; 77 | 0386644F186A909200985CEC /* PeopleResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeopleResource.h; sourceTree = ""; }; 78 | 03866450186A909200985CEC /* PeopleResource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PeopleResource.m; sourceTree = ""; }; 79 | 03866452186A94B700985CEC /* CommentResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommentResource.h; sourceTree = ""; }; 80 | 03866453186A94B700985CEC /* CommentResource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommentResource.m; sourceTree = ""; }; 81 | 03866455186A94C200985CEC /* ArticleResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArticleResource.h; sourceTree = ""; }; 82 | 03866456186A94C200985CEC /* ArticleResource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArticleResource.m; sourceTree = ""; }; 83 | 03866458186CCE6600985CEC /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = ""; }; 84 | 03A055B71868E038004807F0 /* JSONAPI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JSONAPI.app; sourceTree = BUILT_PRODUCTS_DIR; }; 85 | 03A055BA1868E038004807F0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 86 | 03A055BC1868E038004807F0 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 87 | 03A055BE1868E038004807F0 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 88 | 03A055C21868E038004807F0 /* JSONAPI-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "JSONAPI-Info.plist"; sourceTree = ""; }; 89 | 03A055C41868E038004807F0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 90 | 03A055C61868E038004807F0 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 91 | 03A055C81868E038004807F0 /* JSONAPI-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JSONAPI-Prefix.pch"; sourceTree = ""; }; 92 | 03A055C91868E038004807F0 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 93 | 03A055CA1868E038004807F0 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 94 | 03A055CD1868E038004807F0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 95 | 03A055CF1868E038004807F0 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 96 | 03A055D01868E038004807F0 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 97 | 03A055D21868E038004807F0 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 98 | 03A055D81868E038004807F0 /* JSONAPITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JSONAPITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 99 | 03A055D91868E038004807F0 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 100 | 03A055E11868E038004807F0 /* JSONAPITests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "JSONAPITests-Info.plist"; sourceTree = ""; }; 101 | 03A055E31868E038004807F0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 102 | 03A055E51868E038004807F0 /* JSONAPITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSONAPITests.m; sourceTree = ""; }; 103 | 03A055F31868E08A004807F0 /* JSONAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONAPI.h; sourceTree = ""; }; 104 | 03A055F41868E08A004807F0 /* JSONAPI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONAPI.m; sourceTree = ""; }; 105 | 03A055F51868E08A004807F0 /* JSONAPI.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = JSONAPI.podspec; path = ../JSONAPI.podspec; sourceTree = ""; }; 106 | 03FBD8D91AB879FD00789DF3 /* main_example.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = main_example.json; sourceTree = ""; }; 107 | 03FBD8DD1AB8DF8E00789DF3 /* JSONAPIErrorResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONAPIErrorResource.h; sourceTree = ""; }; 108 | 03FBD8DE1AB8DF8E00789DF3 /* JSONAPIErrorResource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONAPIErrorResource.m; sourceTree = ""; }; 109 | 03FBD8E11AB8E46E00789DF3 /* error_example.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = error_example.json; sourceTree = ""; }; 110 | 680E14F31B08146E004FF8CD /* JSONAPIResourceParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONAPIResourceParser.h; sourceTree = ""; }; 111 | 680E14F41B08146E004FF8CD /* JSONAPIResourceParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONAPIResourceParser.m; sourceTree = ""; }; 112 | 681B47741B08EA9800A99D76 /* JSONAPIResourceBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONAPIResourceBase.h; sourceTree = ""; }; 113 | 681B47751B08EA9800A99D76 /* JSONAPIResourceBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONAPIResourceBase.m; sourceTree = ""; }; 114 | 685481E91AE4161900D3A633 /* JSONAPIPropertyDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONAPIPropertyDescriptor.h; sourceTree = ""; }; 115 | 685481EA1AE4161900D3A633 /* JSONAPIPropertyDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONAPIPropertyDescriptor.m; sourceTree = ""; }; 116 | 685481ED1AE419CB00D3A633 /* JSONAPIResourceDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONAPIResourceDescriptor.h; sourceTree = ""; }; 117 | 685481EE1AE419CB00D3A633 /* JSONAPIResourceDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONAPIResourceDescriptor.m; sourceTree = ""; }; 118 | 68A469921AE47E0000E7BBC8 /* NSDateFormatter+JSONAPIDateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+JSONAPIDateFormatter.h"; sourceTree = ""; }; 119 | 68A469931AE47E0000E7BBC8 /* NSDateFormatter+JSONAPIDateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+JSONAPIDateFormatter.m"; sourceTree = ""; }; 120 | 68DF9E951B06B4C500FA6429 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 121 | 7817CDAD1C39A51100D864E6 /* empty_relationship_example.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = empty_relationship_example.json; sourceTree = ""; }; 122 | 781CF6C91C1ECC260052D755 /* generic_relationships_example.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = generic_relationships_example.json; sourceTree = ""; }; 123 | 78A5C1C81C1E1336008C8632 /* NewsFeedPostResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NewsFeedPostResource.h; sourceTree = ""; }; 124 | 78A5C1C91C1E1336008C8632 /* NewsFeedPostResource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NewsFeedPostResource.m; sourceTree = ""; }; 125 | 78A5C1CB1C1E13A8008C8632 /* UserResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserResource.h; sourceTree = ""; }; 126 | 78A5C1CC1C1E13A8008C8632 /* UserResource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UserResource.m; sourceTree = ""; }; 127 | 78A5C1CE1C1E147B008C8632 /* SocialCommunityResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocialCommunityResource.h; sourceTree = ""; }; 128 | 78A5C1CF1C1E147B008C8632 /* SocialCommunityResource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocialCommunityResource.m; sourceTree = ""; }; 129 | 78A5C1D11C1E14D6008C8632 /* MediaResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaResource.h; sourceTree = ""; }; 130 | 78A5C1D21C1E14D6008C8632 /* MediaResource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MediaResource.m; sourceTree = ""; }; 131 | 78A5C1D41C1E154C008C8632 /* WebPageResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebPageResource.h; sourceTree = ""; }; 132 | 78A5C1D51C1E154C008C8632 /* WebPageResource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebPageResource.m; sourceTree = ""; }; 133 | /* End PBXFileReference section */ 134 | 135 | /* Begin PBXFrameworksBuildPhase section */ 136 | 03A055B41868E038004807F0 /* Frameworks */ = { 137 | isa = PBXFrameworksBuildPhase; 138 | buildActionMask = 2147483647; 139 | files = ( 140 | 03A055BD1868E038004807F0 /* CoreGraphics.framework in Frameworks */, 141 | 03A055BF1868E038004807F0 /* UIKit.framework in Frameworks */, 142 | 03A055BB1868E038004807F0 /* Foundation.framework in Frameworks */, 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | 03A055D51868E038004807F0 /* Frameworks */ = { 147 | isa = PBXFrameworksBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | 03A055DA1868E038004807F0 /* XCTest.framework in Frameworks */, 151 | 03A055DC1868E038004807F0 /* UIKit.framework in Frameworks */, 152 | 03A055DB1868E038004807F0 /* Foundation.framework in Frameworks */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXFrameworksBuildPhase section */ 157 | 158 | /* Begin PBXGroup section */ 159 | 0386643E186A791D00985CEC /* ResourceModels */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 03866455186A94C200985CEC /* ArticleResource.h */, 163 | 03866456186A94C200985CEC /* ArticleResource.m */, 164 | 0386644F186A909200985CEC /* PeopleResource.h */, 165 | 03866450186A909200985CEC /* PeopleResource.m */, 166 | 03866452186A94B700985CEC /* CommentResource.h */, 167 | 03866453186A94B700985CEC /* CommentResource.m */, 168 | 78A5C1C81C1E1336008C8632 /* NewsFeedPostResource.h */, 169 | 78A5C1C91C1E1336008C8632 /* NewsFeedPostResource.m */, 170 | 78A5C1CB1C1E13A8008C8632 /* UserResource.h */, 171 | 78A5C1CC1C1E13A8008C8632 /* UserResource.m */, 172 | 78A5C1CE1C1E147B008C8632 /* SocialCommunityResource.h */, 173 | 78A5C1CF1C1E147B008C8632 /* SocialCommunityResource.m */, 174 | 78A5C1D11C1E14D6008C8632 /* MediaResource.h */, 175 | 78A5C1D21C1E14D6008C8632 /* MediaResource.m */, 176 | 78A5C1D41C1E154C008C8632 /* WebPageResource.h */, 177 | 78A5C1D51C1E154C008C8632 /* WebPageResource.m */, 178 | ); 179 | name = ResourceModels; 180 | sourceTree = ""; 181 | }; 182 | 03A055AE1868E038004807F0 = { 183 | isa = PBXGroup; 184 | children = ( 185 | 68DF9E951B06B4C500FA6429 /* README.md */, 186 | 03A055F51868E08A004807F0 /* JSONAPI.podspec */, 187 | 03866458186CCE6600985CEC /* CHANGELOG.md */, 188 | 03A055EF1868E08A004807F0 /* Classes */, 189 | 03A055C01868E038004807F0 /* JSONAPI */, 190 | 03A055DF1868E038004807F0 /* JSONAPITests */, 191 | 03A055B91868E038004807F0 /* Frameworks */, 192 | 03A055B81868E038004807F0 /* Products */, 193 | ); 194 | sourceTree = ""; 195 | }; 196 | 03A055B81868E038004807F0 /* Products */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | 03A055B71868E038004807F0 /* JSONAPI.app */, 200 | 03A055D81868E038004807F0 /* JSONAPITests.xctest */, 201 | ); 202 | name = Products; 203 | sourceTree = ""; 204 | }; 205 | 03A055B91868E038004807F0 /* Frameworks */ = { 206 | isa = PBXGroup; 207 | children = ( 208 | 03A055BA1868E038004807F0 /* Foundation.framework */, 209 | 03A055BC1868E038004807F0 /* CoreGraphics.framework */, 210 | 03A055BE1868E038004807F0 /* UIKit.framework */, 211 | 03A055D91868E038004807F0 /* XCTest.framework */, 212 | ); 213 | name = Frameworks; 214 | sourceTree = ""; 215 | }; 216 | 03A055C01868E038004807F0 /* JSONAPI */ = { 217 | isa = PBXGroup; 218 | children = ( 219 | 03A055C91868E038004807F0 /* AppDelegate.h */, 220 | 03A055CA1868E038004807F0 /* AppDelegate.m */, 221 | 03A055CC1868E038004807F0 /* Main.storyboard */, 222 | 03A055CF1868E038004807F0 /* ViewController.h */, 223 | 03A055D01868E038004807F0 /* ViewController.m */, 224 | 0386643E186A791D00985CEC /* ResourceModels */, 225 | 03A055D21868E038004807F0 /* Images.xcassets */, 226 | 03A055C11868E038004807F0 /* Supporting Files */, 227 | ); 228 | path = JSONAPI; 229 | sourceTree = ""; 230 | }; 231 | 03A055C11868E038004807F0 /* Supporting Files */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | 03A055C21868E038004807F0 /* JSONAPI-Info.plist */, 235 | 03A055C31868E038004807F0 /* InfoPlist.strings */, 236 | 03A055C61868E038004807F0 /* main.m */, 237 | 03A055C81868E038004807F0 /* JSONAPI-Prefix.pch */, 238 | ); 239 | name = "Supporting Files"; 240 | sourceTree = ""; 241 | }; 242 | 03A055DF1868E038004807F0 /* JSONAPITests */ = { 243 | isa = PBXGroup; 244 | children = ( 245 | 03FBD8D81AB879E800789DF3 /* Assets */, 246 | 03A055E51868E038004807F0 /* JSONAPITests.m */, 247 | 03A055E01868E038004807F0 /* Supporting Files */, 248 | ); 249 | path = JSONAPITests; 250 | sourceTree = ""; 251 | }; 252 | 03A055E01868E038004807F0 /* Supporting Files */ = { 253 | isa = PBXGroup; 254 | children = ( 255 | 03A055E11868E038004807F0 /* JSONAPITests-Info.plist */, 256 | 03A055E21868E038004807F0 /* InfoPlist.strings */, 257 | ); 258 | name = "Supporting Files"; 259 | sourceTree = ""; 260 | }; 261 | 03A055EF1868E08A004807F0 /* Classes */ = { 262 | isa = PBXGroup; 263 | children = ( 264 | 68A469921AE47E0000E7BBC8 /* NSDateFormatter+JSONAPIDateFormatter.h */, 265 | 68A469931AE47E0000E7BBC8 /* NSDateFormatter+JSONAPIDateFormatter.m */, 266 | 03A055F31868E08A004807F0 /* JSONAPI.h */, 267 | 03A055F41868E08A004807F0 /* JSONAPI.m */, 268 | 685481E91AE4161900D3A633 /* JSONAPIPropertyDescriptor.h */, 269 | 685481EA1AE4161900D3A633 /* JSONAPIPropertyDescriptor.m */, 270 | 033A6C5D18695CD2001CF9FA /* JSONAPIResource.h */, 271 | 03FBD8DD1AB8DF8E00789DF3 /* JSONAPIErrorResource.h */, 272 | 03FBD8DE1AB8DF8E00789DF3 /* JSONAPIErrorResource.m */, 273 | 685481ED1AE419CB00D3A633 /* JSONAPIResourceDescriptor.h */, 274 | 685481EE1AE419CB00D3A633 /* JSONAPIResourceDescriptor.m */, 275 | 680E14F31B08146E004FF8CD /* JSONAPIResourceParser.h */, 276 | 680E14F41B08146E004FF8CD /* JSONAPIResourceParser.m */, 277 | 681B47741B08EA9800A99D76 /* JSONAPIResourceBase.h */, 278 | 681B47751B08EA9800A99D76 /* JSONAPIResourceBase.m */, 279 | ); 280 | name = Classes; 281 | path = ../Classes; 282 | sourceTree = ""; 283 | }; 284 | 03FBD8D81AB879E800789DF3 /* Assets */ = { 285 | isa = PBXGroup; 286 | children = ( 287 | 7817CDAD1C39A51100D864E6 /* empty_relationship_example.json */, 288 | 781CF6C91C1ECC260052D755 /* generic_relationships_example.json */, 289 | 03FBD8D91AB879FD00789DF3 /* main_example.json */, 290 | 03FBD8E11AB8E46E00789DF3 /* error_example.json */, 291 | ); 292 | name = Assets; 293 | sourceTree = ""; 294 | }; 295 | /* End PBXGroup section */ 296 | 297 | /* Begin PBXNativeTarget section */ 298 | 03A055B61868E038004807F0 /* JSONAPI */ = { 299 | isa = PBXNativeTarget; 300 | buildConfigurationList = 03A055E91868E038004807F0 /* Build configuration list for PBXNativeTarget "JSONAPI" */; 301 | buildPhases = ( 302 | 03A055B31868E038004807F0 /* Sources */, 303 | 03A055B41868E038004807F0 /* Frameworks */, 304 | 03A055B51868E038004807F0 /* Resources */, 305 | ); 306 | buildRules = ( 307 | ); 308 | dependencies = ( 309 | ); 310 | name = JSONAPI; 311 | productName = JSONAPI; 312 | productReference = 03A055B71868E038004807F0 /* JSONAPI.app */; 313 | productType = "com.apple.product-type.application"; 314 | }; 315 | 03A055D71868E038004807F0 /* JSONAPITests */ = { 316 | isa = PBXNativeTarget; 317 | buildConfigurationList = 03A055EC1868E038004807F0 /* Build configuration list for PBXNativeTarget "JSONAPITests" */; 318 | buildPhases = ( 319 | 03A055D41868E038004807F0 /* Sources */, 320 | 03A055D51868E038004807F0 /* Frameworks */, 321 | 03A055D61868E038004807F0 /* Resources */, 322 | ); 323 | buildRules = ( 324 | ); 325 | dependencies = ( 326 | 03A055DE1868E038004807F0 /* PBXTargetDependency */, 327 | ); 328 | name = JSONAPITests; 329 | productName = JSONAPITests; 330 | productReference = 03A055D81868E038004807F0 /* JSONAPITests.xctest */; 331 | productType = "com.apple.product-type.bundle.unit-test"; 332 | }; 333 | /* End PBXNativeTarget section */ 334 | 335 | /* Begin PBXProject section */ 336 | 03A055AF1868E038004807F0 /* Project object */ = { 337 | isa = PBXProject; 338 | attributes = { 339 | LastUpgradeCheck = 0620; 340 | ORGANIZATIONNAME = "Josh Holtz"; 341 | TargetAttributes = { 342 | 03A055D71868E038004807F0 = { 343 | TestTargetID = 03A055B61868E038004807F0; 344 | }; 345 | 689B17A11AFFE0EA00D2854E = { 346 | CreatedOnToolsVersion = 6.3.1; 347 | }; 348 | }; 349 | }; 350 | buildConfigurationList = 03A055B21868E038004807F0 /* Build configuration list for PBXProject "JSONAPI" */; 351 | compatibilityVersion = "Xcode 3.2"; 352 | developmentRegion = English; 353 | hasScannedForEncodings = 0; 354 | knownRegions = ( 355 | en, 356 | Base, 357 | ); 358 | mainGroup = 03A055AE1868E038004807F0; 359 | productRefGroup = 03A055B81868E038004807F0 /* Products */; 360 | projectDirPath = ""; 361 | projectRoot = ""; 362 | targets = ( 363 | 03A055B61868E038004807F0 /* JSONAPI */, 364 | 03A055D71868E038004807F0 /* JSONAPITests */, 365 | 689B17A11AFFE0EA00D2854E /* Documentation */, 366 | ); 367 | }; 368 | /* End PBXProject section */ 369 | 370 | /* Begin PBXResourcesBuildPhase section */ 371 | 03A055B51868E038004807F0 /* Resources */ = { 372 | isa = PBXResourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | 7817CDAF1C39A85000D864E6 /* empty_relationship_example.json in Resources */, 376 | 781CF6CB1C1ECC710052D755 /* generic_relationships_example.json in Resources */, 377 | 03A055D31868E038004807F0 /* Images.xcassets in Resources */, 378 | 03866459186CCE6600985CEC /* CHANGELOG.md in Resources */, 379 | 03A055C51868E038004807F0 /* InfoPlist.strings in Resources */, 380 | 03FBD8DC1AB87EE000789DF3 /* main_example.json in Resources */, 381 | 03FBD8E21AB8E46E00789DF3 /* error_example.json in Resources */, 382 | 03A055CE1868E038004807F0 /* Main.storyboard in Resources */, 383 | 03A055F81868E08A004807F0 /* JSONAPI.podspec in Resources */, 384 | ); 385 | runOnlyForDeploymentPostprocessing = 0; 386 | }; 387 | 03A055D61868E038004807F0 /* Resources */ = { 388 | isa = PBXResourcesBuildPhase; 389 | buildActionMask = 2147483647; 390 | files = ( 391 | 03FBD8E31AB8E46E00789DF3 /* error_example.json in Resources */, 392 | 03A055E41868E038004807F0 /* InfoPlist.strings in Resources */, 393 | 781CF6CA1C1ECC260052D755 /* generic_relationships_example.json in Resources */, 394 | 7817CDAE1C39A51100D864E6 /* empty_relationship_example.json in Resources */, 395 | 03FBD8DA1AB879FD00789DF3 /* main_example.json in Resources */, 396 | ); 397 | runOnlyForDeploymentPostprocessing = 0; 398 | }; 399 | /* End PBXResourcesBuildPhase section */ 400 | 401 | /* Begin PBXShellScriptBuildPhase section */ 402 | 689B17A51AFFE0FD00D2854E /* ShellScript */ = { 403 | isa = PBXShellScriptBuildPhase; 404 | buildActionMask = 2147483647; 405 | files = ( 406 | ); 407 | inputPaths = ( 408 | ); 409 | outputPaths = ( 410 | ); 411 | runOnlyForDeploymentPostprocessing = 0; 412 | shellPath = /bin/sh; 413 | shellScript = "#appledoc Xcode script\n# Start constants\ncompany=\"jkarmstr\";\ncompanyID=\"JKA\";\ncompanyURL=\"http://github.com\";\ntarget=\"iphoneos\";\n#target=\"macosx\";\noutputPath=\"~/help\";\n# End constants\n/usr/local/bin/appledoc \\\n--project-name \"${PROJECT_NAME}\" \\\n--project-company \"${company}\" \\\n--company-id \"${companyID}\" \\\n--output \"${outputPath}\" \\\n--install-docset \\\n--explicit-crossref \\\n--docset-platform-family \"${target}\" \\\n--logformat xcode \\\n--no-repeat-first-par \\\n--exit-threshold 2 \\\n--index-desc ../README.md \\\n\"../Classes\""; 414 | }; 415 | /* End PBXShellScriptBuildPhase section */ 416 | 417 | /* Begin PBXSourcesBuildPhase section */ 418 | 03A055B31868E038004807F0 /* Sources */ = { 419 | isa = PBXSourcesBuildPhase; 420 | buildActionMask = 2147483647; 421 | files = ( 422 | 78A5C1CD1C1E13A8008C8632 /* UserResource.m in Sources */, 423 | 78A5C1D61C1E154C008C8632 /* WebPageResource.m in Sources */, 424 | 03866451186A909200985CEC /* PeopleResource.m in Sources */, 425 | 681B47761B08EA9800A99D76 /* JSONAPIResourceBase.m in Sources */, 426 | 78A5C1CA1C1E1336008C8632 /* NewsFeedPostResource.m in Sources */, 427 | 680E14F51B08146E004FF8CD /* JSONAPIResourceParser.m in Sources */, 428 | 03866457186A94C200985CEC /* ArticleResource.m in Sources */, 429 | 03A055D11868E038004807F0 /* ViewController.m in Sources */, 430 | 03A055F71868E08A004807F0 /* JSONAPI.m in Sources */, 431 | 78A5C1D31C1E14D6008C8632 /* MediaResource.m in Sources */, 432 | 68A469941AE47E0000E7BBC8 /* NSDateFormatter+JSONAPIDateFormatter.m in Sources */, 433 | 685481EB1AE4161900D3A633 /* JSONAPIPropertyDescriptor.m in Sources */, 434 | 03866454186A94B700985CEC /* CommentResource.m in Sources */, 435 | 03A055CB1868E038004807F0 /* AppDelegate.m in Sources */, 436 | 78A5C1D01C1E147B008C8632 /* SocialCommunityResource.m in Sources */, 437 | 685481EF1AE419CB00D3A633 /* JSONAPIResourceDescriptor.m in Sources */, 438 | 03A055C71868E038004807F0 /* main.m in Sources */, 439 | 03FBD8DF1AB8DF8E00789DF3 /* JSONAPIErrorResource.m in Sources */, 440 | ); 441 | runOnlyForDeploymentPostprocessing = 0; 442 | }; 443 | 03A055D41868E038004807F0 /* Sources */ = { 444 | isa = PBXSourcesBuildPhase; 445 | buildActionMask = 2147483647; 446 | files = ( 447 | 03A055E61868E038004807F0 /* JSONAPITests.m in Sources */, 448 | ); 449 | runOnlyForDeploymentPostprocessing = 0; 450 | }; 451 | /* End PBXSourcesBuildPhase section */ 452 | 453 | /* Begin PBXTargetDependency section */ 454 | 03A055DE1868E038004807F0 /* PBXTargetDependency */ = { 455 | isa = PBXTargetDependency; 456 | target = 03A055B61868E038004807F0 /* JSONAPI */; 457 | targetProxy = 03A055DD1868E038004807F0 /* PBXContainerItemProxy */; 458 | }; 459 | /* End PBXTargetDependency section */ 460 | 461 | /* Begin PBXVariantGroup section */ 462 | 03A055C31868E038004807F0 /* InfoPlist.strings */ = { 463 | isa = PBXVariantGroup; 464 | children = ( 465 | 03A055C41868E038004807F0 /* en */, 466 | ); 467 | name = InfoPlist.strings; 468 | sourceTree = ""; 469 | }; 470 | 03A055CC1868E038004807F0 /* Main.storyboard */ = { 471 | isa = PBXVariantGroup; 472 | children = ( 473 | 03A055CD1868E038004807F0 /* Base */, 474 | ); 475 | name = Main.storyboard; 476 | sourceTree = ""; 477 | }; 478 | 03A055E21868E038004807F0 /* InfoPlist.strings */ = { 479 | isa = PBXVariantGroup; 480 | children = ( 481 | 03A055E31868E038004807F0 /* en */, 482 | ); 483 | name = InfoPlist.strings; 484 | sourceTree = ""; 485 | }; 486 | /* End PBXVariantGroup section */ 487 | 488 | /* Begin XCBuildConfiguration section */ 489 | 03A055E71868E038004807F0 /* Debug */ = { 490 | isa = XCBuildConfiguration; 491 | buildSettings = { 492 | ALWAYS_SEARCH_USER_PATHS = NO; 493 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 494 | CLANG_CXX_LIBRARY = "libc++"; 495 | CLANG_ENABLE_MODULES = YES; 496 | CLANG_ENABLE_OBJC_ARC = YES; 497 | CLANG_WARN_BOOL_CONVERSION = YES; 498 | CLANG_WARN_CONSTANT_CONVERSION = YES; 499 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 500 | CLANG_WARN_EMPTY_BODY = YES; 501 | CLANG_WARN_ENUM_CONVERSION = YES; 502 | CLANG_WARN_INT_CONVERSION = YES; 503 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 504 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 505 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 506 | COPY_PHASE_STRIP = NO; 507 | GCC_C_LANGUAGE_STANDARD = gnu99; 508 | GCC_DYNAMIC_NO_PIC = NO; 509 | GCC_OPTIMIZATION_LEVEL = 0; 510 | GCC_PREPROCESSOR_DEFINITIONS = ( 511 | "DEBUG=1", 512 | "$(inherited)", 513 | ); 514 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 515 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 516 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 517 | GCC_WARN_UNDECLARED_SELECTOR = YES; 518 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 519 | GCC_WARN_UNUSED_FUNCTION = YES; 520 | GCC_WARN_UNUSED_VARIABLE = YES; 521 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 522 | ONLY_ACTIVE_ARCH = YES; 523 | SDKROOT = iphoneos; 524 | }; 525 | name = Debug; 526 | }; 527 | 03A055E81868E038004807F0 /* Release */ = { 528 | isa = XCBuildConfiguration; 529 | buildSettings = { 530 | ALWAYS_SEARCH_USER_PATHS = NO; 531 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 532 | CLANG_CXX_LIBRARY = "libc++"; 533 | CLANG_ENABLE_MODULES = YES; 534 | CLANG_ENABLE_OBJC_ARC = YES; 535 | CLANG_WARN_BOOL_CONVERSION = YES; 536 | CLANG_WARN_CONSTANT_CONVERSION = YES; 537 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 538 | CLANG_WARN_EMPTY_BODY = YES; 539 | CLANG_WARN_ENUM_CONVERSION = YES; 540 | CLANG_WARN_INT_CONVERSION = YES; 541 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 542 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 543 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 544 | COPY_PHASE_STRIP = YES; 545 | ENABLE_NS_ASSERTIONS = NO; 546 | GCC_C_LANGUAGE_STANDARD = gnu99; 547 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 548 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 549 | GCC_WARN_UNDECLARED_SELECTOR = YES; 550 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 551 | GCC_WARN_UNUSED_FUNCTION = YES; 552 | GCC_WARN_UNUSED_VARIABLE = YES; 553 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 554 | SDKROOT = iphoneos; 555 | VALIDATE_PRODUCT = YES; 556 | }; 557 | name = Release; 558 | }; 559 | 03A055EA1868E038004807F0 /* Debug */ = { 560 | isa = XCBuildConfiguration; 561 | buildSettings = { 562 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 563 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 564 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 565 | GCC_PREFIX_HEADER = "JSONAPI/JSONAPI-Prefix.pch"; 566 | INFOPLIST_FILE = "JSONAPI/JSONAPI-Info.plist"; 567 | PRODUCT_NAME = "$(TARGET_NAME)"; 568 | WRAPPER_EXTENSION = app; 569 | }; 570 | name = Debug; 571 | }; 572 | 03A055EB1868E038004807F0 /* Release */ = { 573 | isa = XCBuildConfiguration; 574 | buildSettings = { 575 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 576 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 577 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 578 | GCC_PREFIX_HEADER = "JSONAPI/JSONAPI-Prefix.pch"; 579 | INFOPLIST_FILE = "JSONAPI/JSONAPI-Info.plist"; 580 | PRODUCT_NAME = "$(TARGET_NAME)"; 581 | WRAPPER_EXTENSION = app; 582 | }; 583 | name = Release; 584 | }; 585 | 03A055ED1868E038004807F0 /* Debug */ = { 586 | isa = XCBuildConfiguration; 587 | buildSettings = { 588 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/JSONAPI.app/JSONAPI"; 589 | FRAMEWORK_SEARCH_PATHS = ( 590 | "$(SDKROOT)/Developer/Library/Frameworks", 591 | "$(inherited)", 592 | "$(DEVELOPER_FRAMEWORKS_DIR)", 593 | ); 594 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 595 | GCC_PREFIX_HEADER = "JSONAPI/JSONAPI-Prefix.pch"; 596 | GCC_PREPROCESSOR_DEFINITIONS = ( 597 | "DEBUG=1", 598 | "$(inherited)", 599 | ); 600 | INFOPLIST_FILE = "JSONAPITests/JSONAPITests-Info.plist"; 601 | PRODUCT_NAME = "$(TARGET_NAME)"; 602 | TEST_HOST = "$(BUNDLE_LOADER)"; 603 | WRAPPER_EXTENSION = xctest; 604 | }; 605 | name = Debug; 606 | }; 607 | 03A055EE1868E038004807F0 /* Release */ = { 608 | isa = XCBuildConfiguration; 609 | buildSettings = { 610 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/JSONAPI.app/JSONAPI"; 611 | FRAMEWORK_SEARCH_PATHS = ( 612 | "$(SDKROOT)/Developer/Library/Frameworks", 613 | "$(inherited)", 614 | "$(DEVELOPER_FRAMEWORKS_DIR)", 615 | ); 616 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 617 | GCC_PREFIX_HEADER = "JSONAPI/JSONAPI-Prefix.pch"; 618 | INFOPLIST_FILE = "JSONAPITests/JSONAPITests-Info.plist"; 619 | PRODUCT_NAME = "$(TARGET_NAME)"; 620 | TEST_HOST = "$(BUNDLE_LOADER)"; 621 | WRAPPER_EXTENSION = xctest; 622 | }; 623 | name = Release; 624 | }; 625 | 689B17A21AFFE0EA00D2854E /* Debug */ = { 626 | isa = XCBuildConfiguration; 627 | buildSettings = { 628 | PRODUCT_NAME = "$(TARGET_NAME)"; 629 | }; 630 | name = Debug; 631 | }; 632 | 689B17A31AFFE0EA00D2854E /* Release */ = { 633 | isa = XCBuildConfiguration; 634 | buildSettings = { 635 | PRODUCT_NAME = "$(TARGET_NAME)"; 636 | }; 637 | name = Release; 638 | }; 639 | /* End XCBuildConfiguration section */ 640 | 641 | /* Begin XCConfigurationList section */ 642 | 03A055B21868E038004807F0 /* Build configuration list for PBXProject "JSONAPI" */ = { 643 | isa = XCConfigurationList; 644 | buildConfigurations = ( 645 | 03A055E71868E038004807F0 /* Debug */, 646 | 03A055E81868E038004807F0 /* Release */, 647 | ); 648 | defaultConfigurationIsVisible = 0; 649 | defaultConfigurationName = Release; 650 | }; 651 | 03A055E91868E038004807F0 /* Build configuration list for PBXNativeTarget "JSONAPI" */ = { 652 | isa = XCConfigurationList; 653 | buildConfigurations = ( 654 | 03A055EA1868E038004807F0 /* Debug */, 655 | 03A055EB1868E038004807F0 /* Release */, 656 | ); 657 | defaultConfigurationIsVisible = 0; 658 | defaultConfigurationName = Release; 659 | }; 660 | 03A055EC1868E038004807F0 /* Build configuration list for PBXNativeTarget "JSONAPITests" */ = { 661 | isa = XCConfigurationList; 662 | buildConfigurations = ( 663 | 03A055ED1868E038004807F0 /* Debug */, 664 | 03A055EE1868E038004807F0 /* Release */, 665 | ); 666 | defaultConfigurationIsVisible = 0; 667 | defaultConfigurationName = Release; 668 | }; 669 | 689B17A41AFFE0EA00D2854E /* Build configuration list for PBXAggregateTarget "Documentation" */ = { 670 | isa = XCConfigurationList; 671 | buildConfigurations = ( 672 | 689B17A21AFFE0EA00D2854E /* Debug */, 673 | 689B17A31AFFE0EA00D2854E /* Release */, 674 | ); 675 | defaultConfigurationIsVisible = 0; 676 | defaultConfigurationName = Release; 677 | }; 678 | /* End XCConfigurationList section */ 679 | }; 680 | rootObject = 03A055AF1868E038004807F0 /* Project object */; 681 | } 682 | -------------------------------------------------------------------------------- /Project/JSONAPI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Project/JSONAPI.xcodeproj/project.xcworkspace/xcshareddata/JSONAPI.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | F88BDF8C-DA51-4F26-8F41-0B24041871ED 9 | IDESourceControlProjectName 10 | JSONAPI 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 316D8028CBF982160D8D1C5442A0AAF09059D0E3 14 | github.com:joshdholtz/jsonapi-ios.git 15 | 16 | IDESourceControlProjectPath 17 | Project/JSONAPI.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 316D8028CBF982160D8D1C5442A0AAF09059D0E3 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | github.com:joshdholtz/jsonapi-ios.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 316D8028CBF982160D8D1C5442A0AAF09059D0E3 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 316D8028CBF982160D8D1C5442A0AAF09059D0E3 36 | IDESourceControlWCCName 37 | jsonapi-ios 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Project/JSONAPI.xcodeproj/xcshareddata/xcschemes/JSONAPI.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 63 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Project/JSONAPI.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Project/JSONAPI.xcworkspace/xcshareddata/JSONAPI.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | DA49755E-FD70-48BA-8E28-2C90B0ABED83 9 | IDESourceControlProjectName 10 | JSONAPI 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 316D8028CBF982160D8D1C5442A0AAF09059D0E3 14 | github.com:joshdholtz/jsonapi-ios.git 15 | 7EE6692C-FAB2-484E-B1C6-2C33369BAE38 16 | ssh://github.com/joshdholtz/jsonapi-ios.git 17 | 18 | IDESourceControlProjectPath 19 | Project/JSONAPI.xcworkspace 20 | IDESourceControlProjectRelativeInstallPathDictionary 21 | 22 | 316D8028CBF982160D8D1C5442A0AAF09059D0E3 23 | ../.. 24 | 7EE6692C-FAB2-484E-B1C6-2C33369BAE38 25 | ../../../JSONAPI 26 | 27 | IDESourceControlProjectURL 28 | github.com:joshdholtz/jsonapi-ios.git 29 | IDESourceControlProjectVersion 30 | 111 31 | IDESourceControlProjectWCCIdentifier 32 | 316D8028CBF982160D8D1C5442A0AAF09059D0E3 33 | IDESourceControlProjectWCConfigurations 34 | 35 | 36 | IDESourceControlRepositoryExtensionIdentifierKey 37 | public.vcs.git 38 | IDESourceControlWCCIdentifierKey 39 | 7EE6692C-FAB2-484E-B1C6-2C33369BAE38 40 | IDESourceControlWCCName 41 | JSONAPI 42 | 43 | 44 | IDESourceControlRepositoryExtensionIdentifierKey 45 | public.vcs.git 46 | IDESourceControlWCCIdentifierKey 47 | 316D8028CBF982160D8D1C5442A0AAF09059D0E3 48 | IDESourceControlWCCName 49 | jsonapi-ios 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Project/JSONAPI/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Project/JSONAPI/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Project/JSONAPI/ArticleResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // ArticleResource.h 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/24/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "JSONAPIResourceBase.h" 10 | 11 | @class PeopleResource; 12 | 13 | @interface ArticleResource : JSONAPIResourceBase 14 | 15 | @property (nonatomic, strong) NSString *title; 16 | @property (nonatomic, strong) PeopleResource *author; 17 | @property (nonatomic, strong) NSDate *date; 18 | @property (nonatomic, strong) NSArray *comments; 19 | 20 | @property (nonatomic, strong) NSArray *versions; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Project/JSONAPI/ArticleResource.m: -------------------------------------------------------------------------------- 1 | // 2 | // ArticleResource.m 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/24/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "ArticleResource.h" 10 | 11 | #import "JSONAPIPropertyDescriptor.h" 12 | #import "JSONAPIResourceDescriptor.h" 13 | #import "NSDateFormatter+JSONAPIDateFormatter.h" 14 | 15 | #import "PeopleResource.h" 16 | #import "CommentResource.h" 17 | 18 | 19 | @implementation ArticleResource 20 | 21 | static JSONAPIResourceDescriptor *__descriptor = nil; 22 | 23 | + (JSONAPIResourceDescriptor*)descriptor { 24 | static dispatch_once_t onceToken; 25 | dispatch_once(&onceToken, ^{ 26 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"articles"]; 27 | 28 | // These are set by default in the JSONAPIResourceDescriptor init 29 | // [__descriptor setIdProperty:@"ID"]; 30 | // [__descriptor setSelfLinkProperty:@"selfLink"]; 31 | 32 | [__descriptor addProperty:@"title"]; 33 | [__descriptor addProperty:@"date" 34 | withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"date" withFormat:[NSDateFormatter RFC3339DateFormatter]]]; 35 | 36 | [__descriptor hasOne:[PeopleResource class] withName:@"author"]; 37 | [__descriptor hasMany:[CommentResource class] withName:@"comments"]; 38 | 39 | [__descriptor addProperty:@"versions" 40 | withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"versions" withFormat:[NSDateFormatter RFC3339DateFormatter]]]; 41 | }); 42 | 43 | return __descriptor; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Project/JSONAPI/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Project/JSONAPI/CommentResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // CommentResource.h 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/24/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "JSONAPIResourceBase.h" 10 | 11 | @class PeopleResource; 12 | 13 | @interface CommentResource : JSONAPIResourceBase 14 | 15 | @property (nonatomic, strong) NSString *text; 16 | @property (nonatomic, strong) PeopleResource *author; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Project/JSONAPI/CommentResource.m: -------------------------------------------------------------------------------- 1 | // 2 | // CommentResource.m 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/24/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "CommentResource.h" 10 | 11 | #import "JSONAPIPropertyDescriptor.h" 12 | #import "JSONAPIResourceDescriptor.h" 13 | 14 | #import "PeopleResource.h" 15 | 16 | 17 | @implementation CommentResource 18 | 19 | static JSONAPIResourceDescriptor *__descriptor = nil; 20 | 21 | + (JSONAPIResourceDescriptor*)descriptor { 22 | static dispatch_once_t onceToken; 23 | dispatch_once(&onceToken, ^{ 24 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"comments"]; 25 | [__descriptor addProperty:@"text" withJsonName:@"body"]; 26 | 27 | [__descriptor hasOne:[PeopleResource class] withName:@"author"]; 28 | }); 29 | 30 | return __descriptor; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Project/JSONAPI/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "60x60", 21 | "scale" : "3x" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /Project/JSONAPI/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Project/JSONAPI/JSONAPI-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.joshholtz.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Project/JSONAPI/JSONAPI-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Project/JSONAPI/MediaResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // MediaResource.h 3 | // JSONAPI 4 | // 5 | // Created by Rafael Kayumov on 14.12.15. 6 | // Copyright © 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "JSONAPIResourceBase.h" 10 | 11 | @interface MediaResource : JSONAPIResourceBase 12 | 13 | @property (nonatomic, strong) NSString *mimeType; 14 | @property (nonatomic, strong) NSString *fileUrl; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Project/JSONAPI/MediaResource.m: -------------------------------------------------------------------------------- 1 | // 2 | // MediaResource.m 3 | // JSONAPI 4 | // 5 | // Created by Rafael Kayumov on 14.12.15. 6 | // Copyright © 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "MediaResource.h" 10 | 11 | #import "JSONAPIPropertyDescriptor.h" 12 | #import "JSONAPIResourceDescriptor.h" 13 | 14 | @implementation MediaResource 15 | 16 | static JSONAPIResourceDescriptor *__descriptor = nil; 17 | 18 | + (JSONAPIResourceDescriptor*)descriptor { 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^{ 21 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"Media"]; 22 | 23 | [__descriptor setIdProperty:@"ID"]; 24 | 25 | [__descriptor addProperty:@"mimeType"]; 26 | [__descriptor addProperty:@"fileUrl"]; 27 | }); 28 | 29 | return __descriptor; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Project/JSONAPI/NewsFeedPostResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // NewsFeedPostResource.h 3 | // JSONAPI 4 | // 5 | // Created by Rafael Kayumov on 13.12.15. 6 | // Copyright © 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "JSONAPIResourceBase.h" 10 | #import "UserResource.h" 11 | #import "SocialCommunityResource.h" 12 | #import "MediaResource.h" 13 | #import "WebPageResource.h" 14 | 15 | @interface NewsFeedPostResource : JSONAPIResourceBase 16 | 17 | @property (nonatomic, strong) NSDate *createdAt; 18 | @property (nonatomic, strong) NSString *title; 19 | @property (nonatomic, strong) NSString *text; 20 | 21 | @property (nonatomic, strong) id publisher; 22 | @property (nonatomic, strong) NSArray *attachments; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Project/JSONAPI/NewsFeedPostResource.m: -------------------------------------------------------------------------------- 1 | // 2 | // NewsFeedPostResource.m 3 | // JSONAPI 4 | // 5 | // Created by Rafael Kayumov on 13.12.15. 6 | // Copyright © 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "NewsFeedPostResource.h" 10 | 11 | #import "JSONAPIPropertyDescriptor.h" 12 | #import "JSONAPIResourceDescriptor.h" 13 | #import "NSDateFormatter+JSONAPIDateFormatter.h" 14 | 15 | @implementation NewsFeedPostResource 16 | 17 | static JSONAPIResourceDescriptor *__descriptor = nil; 18 | 19 | + (JSONAPIResourceDescriptor*)descriptor { 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"NewsFeedPost"]; 23 | 24 | [__descriptor setIdProperty:@"ID"]; 25 | 26 | [__descriptor addProperty:@"createdAt" 27 | withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"createdAt" withFormat:[NSDateFormatter RFC3339DateFormatter]]]; 28 | [__descriptor addProperty:@"title"]; 29 | [__descriptor addProperty:@"text"]; 30 | 31 | [__descriptor hasOne:nil withName:@"publisher"]; 32 | [__descriptor hasMany:nil withName:@"attachments"]; 33 | }); 34 | 35 | return __descriptor; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Project/JSONAPI/PeopleResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // PeopleResource.h 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/24/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "JSONAPIResourceBase.h" 10 | 11 | @interface PeopleResource : JSONAPIResourceBase 12 | 13 | @property (nonatomic, strong) NSString *firstName; 14 | @property (nonatomic, strong) NSString *lastName; 15 | @property (nonatomic, strong) NSString *twitter; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Project/JSONAPI/PeopleResource.m: -------------------------------------------------------------------------------- 1 | // 2 | // PeopleResource.m 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/24/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "PeopleResource.h" 10 | 11 | #import "JSONAPIPropertyDescriptor.h" 12 | #import "JSONAPIResourceDescriptor.h" 13 | 14 | @implementation PeopleResource 15 | 16 | static JSONAPIResourceDescriptor *__descriptor = nil; 17 | 18 | + (JSONAPIResourceDescriptor*)descriptor { 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^{ 21 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"people"]; 22 | 23 | [__descriptor addProperty:@"firstName" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"first-name"]]; 24 | [__descriptor addProperty:@"lastName" withJsonName:@"last-name"]; 25 | [__descriptor addProperty:@"twitter"]; 26 | }); 27 | 28 | return __descriptor; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Project/JSONAPI/SocialCommunityResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SocialCommunityResource.h 3 | // JSONAPI 4 | // 5 | // Created by Rafael Kayumov on 14.12.15. 6 | // Copyright © 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "JSONAPIResourceBase.h" 10 | 11 | @interface SocialCommunityResource : JSONAPIResourceBase 12 | 13 | @property (nonatomic, strong) NSString *title; 14 | @property (nonatomic, strong) NSString *homePage; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Project/JSONAPI/SocialCommunityResource.m: -------------------------------------------------------------------------------- 1 | // 2 | // SocialCommunityResource.m 3 | // JSONAPI 4 | // 5 | // Created by Rafael Kayumov on 14.12.15. 6 | // Copyright © 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "SocialCommunityResource.h" 10 | 11 | #import "JSONAPIPropertyDescriptor.h" 12 | #import "JSONAPIResourceDescriptor.h" 13 | 14 | @implementation SocialCommunityResource 15 | 16 | static JSONAPIResourceDescriptor *__descriptor = nil; 17 | 18 | + (JSONAPIResourceDescriptor*)descriptor { 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^{ 21 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"SocialCommunity"]; 22 | 23 | [__descriptor setIdProperty:@"ID"]; 24 | 25 | [__descriptor addProperty:@"title"]; 26 | [__descriptor addProperty:@"homePage"]; 27 | }); 28 | 29 | return __descriptor; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Project/JSONAPI/UserResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // UserResource.h 3 | // JSONAPI 4 | // 5 | // Created by Rafael Kayumov on 13.12.15. 6 | // Copyright © 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "JSONAPIResourceBase.h" 10 | 11 | @interface UserResource : JSONAPIResourceBase 12 | 13 | @property (nonatomic, strong) NSString *name; 14 | @property (nonatomic, strong) NSString *email; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Project/JSONAPI/UserResource.m: -------------------------------------------------------------------------------- 1 | // 2 | // UserResource.m 3 | // JSONAPI 4 | // 5 | // Created by Rafael Kayumov on 13.12.15. 6 | // Copyright © 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "UserResource.h" 10 | 11 | #import "JSONAPIPropertyDescriptor.h" 12 | #import "JSONAPIResourceDescriptor.h" 13 | 14 | @implementation UserResource 15 | 16 | static JSONAPIResourceDescriptor *__descriptor = nil; 17 | 18 | + (JSONAPIResourceDescriptor*)descriptor { 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^{ 21 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"User"]; 22 | 23 | [__descriptor setIdProperty:@"ID"]; 24 | 25 | [__descriptor addProperty:@"name"]; 26 | [__descriptor addProperty:@"email"]; 27 | }); 28 | 29 | return __descriptor; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Project/JSONAPI/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Project/JSONAPI/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "JSONAPI.h" 12 | 13 | #import "CommentResource.h" 14 | #import "PeopleResource.h" 15 | #import "ArticleResource.h" 16 | 17 | @interface ViewController () 18 | 19 | @end 20 | 21 | @implementation ViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | 26 | 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Project/JSONAPI/WebPageResource.h: -------------------------------------------------------------------------------- 1 | // 2 | // WebPageResource.h 3 | // JSONAPI 4 | // 5 | // Created by Rafael Kayumov on 14.12.15. 6 | // Copyright © 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "JSONAPIResourceBase.h" 10 | 11 | @interface WebPageResource : JSONAPIResourceBase 12 | 13 | @property (nonatomic, strong) NSString *pageUrl; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Project/JSONAPI/WebPageResource.m: -------------------------------------------------------------------------------- 1 | // 2 | // WebPageResource.m 3 | // JSONAPI 4 | // 5 | // Created by Rafael Kayumov on 14.12.15. 6 | // Copyright © 2015 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import "WebPageResource.h" 10 | 11 | #import "JSONAPIPropertyDescriptor.h" 12 | #import "JSONAPIResourceDescriptor.h" 13 | 14 | @implementation WebPageResource 15 | 16 | static JSONAPIResourceDescriptor *__descriptor = nil; 17 | 18 | + (JSONAPIResourceDescriptor*)descriptor { 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^{ 21 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"WebPage"]; 22 | 23 | [__descriptor setIdProperty:@"ID"]; 24 | 25 | [__descriptor addProperty:@"pageUrl"]; 26 | }); 27 | 28 | return __descriptor; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Project/JSONAPI/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Project/JSONAPI/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JSONAPI 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Project/JSONAPITests/JSONAPITests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.joshholtz.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Project/JSONAPITests/JSONAPITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSONAPITests.m 3 | // JSONAPITests 4 | // 5 | // Created by Josh Holtz on 12/23/13. 6 | // Copyright (c) 2013 Josh Holtz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "JSONAPI.h" 12 | #import "JSONAPIResourceDescriptor.h" 13 | #import "JSONAPIErrorResource.h" 14 | #import "JSONAPIResourceParser.h" 15 | #import "NSDateFormatter+JSONAPIDateFormatter.h" 16 | 17 | #import "CommentResource.h" 18 | #import "PeopleResource.h" 19 | #import "ArticleResource.h" 20 | 21 | #import "NewsFeedPostResource.h" 22 | #import "UserResource.h" 23 | #import "SocialCommunityResource.h" 24 | #import "MediaResource.h" 25 | #import "WebPageResource.h" 26 | 27 | @interface JSONAPITests : XCTestCase 28 | 29 | @end 30 | 31 | @implementation JSONAPITests 32 | 33 | - (void)setUp { 34 | [super setUp]; 35 | 36 | [JSONAPIResourceDescriptor addResource:[CommentResource class]]; 37 | [JSONAPIResourceDescriptor addResource:[PeopleResource class]]; 38 | [JSONAPIResourceDescriptor addResource:[ArticleResource class]]; 39 | [JSONAPIResourceDescriptor addResource:[UserResource class]]; 40 | [JSONAPIResourceDescriptor addResource:[SocialCommunityResource class]]; 41 | [JSONAPIResourceDescriptor addResource:[MediaResource class]]; 42 | [JSONAPIResourceDescriptor addResource:[WebPageResource class]]; 43 | [JSONAPIResourceDescriptor addResource:[NewsFeedPostResource class]]; 44 | } 45 | 46 | - (void)tearDown { 47 | [super tearDown]; 48 | } 49 | 50 | - (void)testMeta { 51 | NSDictionary *json = [self mainExampleJSON]; 52 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 53 | 54 | XCTAssertNotNil(jsonAPI.meta, @"Meta should not be nil"); 55 | XCTAssertEqualObjects(jsonAPI.meta[@"hehe"], @"hoho", @"Meta's 'hehe' should equal 'hoho'"); 56 | } 57 | 58 | - (void)testDataArticles { 59 | NSDictionary *json = [self mainExampleJSON]; 60 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 61 | 62 | XCTAssertNotNil(jsonAPI.resource, @"Resource should not be nil"); 63 | XCTAssertNotNil(jsonAPI.resources, @"Resources should not be nil"); 64 | XCTAssertEqual(jsonAPI.resources.count, 1, @"Resources should contain 1 resource"); 65 | 66 | ArticleResource *article = jsonAPI.resource; 67 | 68 | XCTAssertNotNil(article.meta, @"Meta should not be nil"); 69 | XCTAssertEqualObjects(article.meta[@"hehe"], @"hoho", @"Meta's 'hehe' should equal 'hoho'"); 70 | 71 | XCTAssert([article isKindOfClass:[ArticleResource class]], @"Article should be a ArticleResource"); 72 | XCTAssertEqualObjects(article.ID, @"1", @"Article id should be 1"); 73 | XCTAssertTrue([article.selfLink isEqualToString:@"http://example.com/articles/1"], @"Article selfLink should be 'http://example.com/articles/1'"); 74 | XCTAssertEqualObjects(article.title, @"JSON API paints my bikeshed!", @"Article title should be 'JSON API paints my bikeshed!'"); 75 | 76 | NSArray *dates = @[[[NSDateFormatter RFC3339DateFormatter] dateFromString:@"2015-09-01T12:15:00.000Z"], 77 | [[NSDateFormatter RFC3339DateFormatter] dateFromString:@"2015-08-01T06:15:00.000Z"]]; 78 | XCTAssertEqualObjects(article.versions, dates, @"Article versions should contain an array of date objects"); 79 | } 80 | 81 | - (void)testIncludedPeopleAndComments { 82 | NSDictionary *json = [self mainExampleJSON]; 83 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 84 | 85 | XCTAssertNotNil(jsonAPI.includedResources, @"Included resources should not be nil"); 86 | XCTAssertEqual(jsonAPI.includedResources.count, 2, @"Included resources should contain 2 types"); 87 | 88 | } 89 | 90 | - (void)testDataArticleAuthorAndComments { 91 | NSDictionary *json = [self mainExampleJSON]; 92 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 93 | 94 | ArticleResource *article = jsonAPI.resource; 95 | CommentResource *firstComment = article.comments.firstObject; 96 | 97 | XCTAssertNotNil(article.author, @"Article's author should not be nil"); 98 | XCTAssertNotNil(article.comments, @"Article's comments should not be nil"); 99 | XCTAssertEqual(article.comments.count, 2, @"Article should contain 2 comments"); 100 | XCTAssertEqualObjects(article.author.firstName, @"Dan", @"Article's author firstname should be 'Dan'"); 101 | XCTAssertEqualObjects(firstComment.text, @"First!", @"Article's first comment should be 'First!'"); 102 | XCTAssertEqualObjects(firstComment.author.firstName, @"Dan", @"Article's first comment author should be 'Dan'"); 103 | } 104 | 105 | - (void)testIncludedCommentIsLinked { 106 | NSDictionary *json = [self mainExampleJSON]; 107 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 108 | 109 | CommentResource *comment = [jsonAPI includedResource:@"5" withType:@"comments"]; 110 | XCTAssertNotNil(comment.author, @"Comment's author should not be nil"); 111 | XCTAssertEqualObjects(comment.author.ID, @"9", @"Comment's author's ID should be 2"); 112 | } 113 | 114 | - (void)testNoError { 115 | NSDictionary *json = [self mainExampleJSON]; 116 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 117 | 118 | XCTAssertFalse([jsonAPI hasErrors], @"JSON API should not have errors"); 119 | } 120 | 121 | - (void)testError { 122 | NSDictionary *json = [self errorExampleJSON]; 123 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 124 | 125 | XCTAssertTrue([jsonAPI hasErrors], @"JSON API should have errors"); 126 | 127 | JSONAPIErrorResource *error = jsonAPI.errors.firstObject; 128 | XCTAssertEqualObjects(error.ID, @"123456", @"Error id should be 123456"); 129 | } 130 | 131 | - (void)testSerializeSimple { 132 | PeopleResource *newAuthor = [[PeopleResource alloc] init]; 133 | 134 | newAuthor.firstName = @"Karl"; 135 | newAuthor.lastName = @"Armstrong"; 136 | 137 | NSDictionary *json = [JSONAPIResourceParser dictionaryFor:newAuthor]; 138 | XCTAssertEqualObjects(json[@"type"], @"people", @"Did not create person!"); 139 | XCTAssertEqualObjects(json[@"attributes"][@"first-name"], @"Karl", @"Wrong first name!"); 140 | XCTAssertNil(json[@"attributes"][@"twitter"], @"Wrong Twitter!."); 141 | } 142 | 143 | - (void)testSerializeWithFormat { 144 | ArticleResource *newArticle = [[ArticleResource alloc] init]; 145 | newArticle.title = @"Title"; 146 | newArticle.date = [NSDate date]; 147 | 148 | NSDictionary *json = [JSONAPIResourceParser dictionaryFor:newArticle]; 149 | XCTAssertEqualObjects(json[@"type"], @"articles", @"Did not create article!"); 150 | XCTAssertNotNil(json[@"attributes"][@"date"], @"Wrong date!"); 151 | XCTAssertTrue([json[@"attributes"][@"date"] isKindOfClass:[NSString class]], @"Date should be string!."); 152 | } 153 | 154 | - (void)testSerializeComplex { 155 | PeopleResource *newAuthor = [[PeopleResource alloc] init]; 156 | 157 | newAuthor.ID = [NSUUID UUID]; 158 | newAuthor.firstName = @"Karl"; 159 | newAuthor.lastName = @"Armstrong"; 160 | 161 | CommentResource *firstComment = [[CommentResource alloc] init]; 162 | firstComment.ID = [NSUUID UUID]; 163 | firstComment.author = newAuthor; 164 | firstComment.text = @"First!"; 165 | 166 | CommentResource *secondComment = [[CommentResource alloc] init]; 167 | secondComment.ID = [NSUUID UUID]; 168 | secondComment.author = newAuthor; 169 | secondComment.text = @"Second!"; 170 | 171 | ArticleResource *newArticle = [[ArticleResource alloc] init]; 172 | newArticle.title = @"Title"; 173 | newArticle.author = newAuthor; 174 | newArticle.date = [NSDate date]; 175 | newArticle.comments = [[NSArray alloc] initWithObjects:firstComment, secondComment, nil]; 176 | 177 | NSDictionary *json = [JSONAPIResourceParser dictionaryFor:newArticle]; 178 | XCTAssertEqualObjects(json[@"type"], @"articles", @"Did not create Article!"); 179 | XCTAssertNotNil(json[@"relationships"], @"Did not create links!"); 180 | XCTAssertNotNil(json[@"relationships"][@"author"], @"Did not create links!"); 181 | XCTAssertNotNil(json[@"relationships"][@"author"][@"data"], @"Did not create links!"); 182 | XCTAssertEqualObjects(json[@"relationships"][@"author"][@"data"][@"id"], newAuthor.ID, @"Wrong link ID!."); 183 | XCTAssertNil(json[@"relationships"][@"author"][@"first-name"], @"Bad link!"); 184 | 185 | XCTAssertNotNil(json[@"relationships"][@"comments"], @"Did not create links!"); 186 | XCTAssertTrue([json[@"relationships"][@"comments"][@"data"] isKindOfClass:[NSArray class]], @"Comments data should be array!."); 187 | XCTAssertEqual([json[@"relationships"][@"comments"][@"data"] count], 2, @"Comments should have 2 elements!."); 188 | } 189 | 190 | - (void)testCreate { 191 | NSDictionary *json = [self mainExampleJSON]; 192 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 193 | 194 | ArticleResource *article = jsonAPI.resource; 195 | 196 | jsonAPI = [JSONAPI jsonAPIWithResource:article]; 197 | NSDictionary *dictionary = [jsonAPI dictionary]; 198 | XCTAssertEqualObjects(dictionary[@"data"][@"type"], @"articles", @"Did not create article!"); 199 | XCTAssertEqualObjects(dictionary[@"data"][@"attributes"][@"title"], @"JSON API paints my bikeshed!", @"Did not parse title!"); 200 | XCTAssertEqual([dictionary[@"data"][@"relationships"][@"comments"][@"data"] count], 2, @"Did not parse relationships!"); 201 | XCTAssertEqual([dictionary[@"included"] count], 3, @"Did not parse included resources!"); 202 | XCTAssertEqualObjects(dictionary[@"included"][0][@"type"], @"people", @"Did not parse included people object!"); 203 | XCTAssertEqualObjects(dictionary[@"included"][0][@"id"], @"9", @"Did not parse ID!"); 204 | XCTAssertEqualObjects(dictionary[@"included"][1][@"type"], @"comments", @"Did not parse included comments object!"); 205 | XCTAssertEqualObjects(dictionary[@"included"][1][@"relationships"][@"author"][@"data"][@"type"], @"people", @"Did not parse included comments author!"); 206 | } 207 | 208 | #pragma mark - Generic relationships tests 209 | 210 | - (void)testGenericMappingAndParsing { 211 | NSDictionary *json = [self genericRelationshipsExampleJSON]; 212 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 213 | 214 | NSArray *posts = jsonAPI.resources; 215 | 216 | NewsFeedPostResource *firstPost = posts.firstObject; 217 | UserResource *firstPostAuthor = firstPost.publisher; 218 | 219 | NewsFeedPostResource *secondPost = posts.lastObject; 220 | SocialCommunityResource *secondPostAuthor = secondPost.publisher; 221 | 222 | XCTAssertNotNil(firstPostAuthor, @"First post's author should not be nil"); 223 | XCTAssertTrue(firstPostAuthor.class == UserResource.class, @"First post's author should be of class UserResource"); 224 | XCTAssertEqualObjects(firstPostAuthor.name, @"Sam", @"First post's author name should be 'Sam'"); 225 | 226 | XCTAssertNotNil(secondPostAuthor, @"Second post's author should not be nil"); 227 | XCTAssertTrue(secondPostAuthor.class == SocialCommunityResource.class, @"First post's author should be of class SocialCommunityResource"); 228 | XCTAssertEqualObjects(secondPostAuthor.title, @"Testing Social Community", @"Second post's author title should be 'Testing Social Community'"); 229 | 230 | XCTAssertNotNil(firstPost.attachments, @"First post's attachments should not be nil"); 231 | XCTAssertEqual(firstPost.attachments.count, 2, @"First post's attachments should contain 2 objects"); 232 | 233 | XCTAssertTrue(((JSONAPIResourceBase *)firstPost.attachments.firstObject).class == MediaResource.class, @"First attachment should be of class MediaResource"); 234 | XCTAssertEqualObjects(((MediaResource *)firstPost.attachments.firstObject).mimeType, @"image/jpg", @"Media mime type should be 'image/jpg'"); 235 | 236 | XCTAssertTrue(((JSONAPIResourceBase *)firstPost.attachments.lastObject).class == WebPageResource.class, @"Second attachment should be of class WebPageResource"); 237 | XCTAssertEqualObjects(((WebPageResource *)firstPost.attachments.lastObject).pageUrl, @"http://testingservice.com/content/testPage.html", @"Web page url should be 'http://testingservice.com/content/testPage.html'"); 238 | } 239 | 240 | - (void)testGenericSerialization { 241 | NSDictionary *json = [self genericRelationshipsExampleJSON]; 242 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 243 | 244 | NSArray *posts = jsonAPI.resources; 245 | 246 | NSDictionary *serializedFirstPost = [JSONAPIResourceParser dictionaryFor:posts.firstObject]; 247 | NSDictionary *serializedSecondPost = [JSONAPIResourceParser dictionaryFor:posts.lastObject]; 248 | 249 | XCTAssertNotNil(serializedFirstPost[@"relationships"][@"attachments"][@"data"][0], @"Media attachment should not be nil"); 250 | XCTAssertNotNil(serializedFirstPost[@"relationships"][@"attachments"][@"data"][1], @"Web page url attachment should not be nil"); 251 | 252 | XCTAssertEqualObjects(serializedFirstPost[@"relationships"][@"attachments"][@"data"][0][@"id"], @15, @"Media id should be '15'"); 253 | XCTAssertEqualObjects(serializedFirstPost[@"relationships"][@"attachments"][@"data"][0][@"type"], @"Media", @"Media type should be 'Media'"); 254 | 255 | XCTAssertEqualObjects(serializedFirstPost[@"relationships"][@"publisher"][@"data"][@"id"], @45, @"User id should be '45'"); 256 | XCTAssertEqualObjects(serializedFirstPost[@"relationships"][@"publisher"][@"data"][@"type"], @"User", @"User type should be 'User'"); 257 | 258 | XCTAssertEqualObjects(serializedSecondPost[@"relationships"][@"publisher"][@"data"][@"id"], @23, @"Social community id should be '23'"); 259 | XCTAssertEqualObjects(serializedSecondPost[@"relationships"][@"publisher"][@"data"][@"type"], @"SocialCommunity", @"SocialCommunity type should be 'SocialCommunity'"); 260 | } 261 | 262 | #pragma mark - Empty relationship tests 263 | 264 | - (void)testEmptyRelationship { 265 | NSDictionary *json = [self emptyRelationshipsExampleJSON]; 266 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 267 | 268 | NewsFeedPostResource *testPost = jsonAPI.resource; 269 | 270 | XCTAssertNil(testPost.publisher, @"Test post's publisher should be nil"); 271 | 272 | XCTAssertNotNil(testPost.attachments, @"Test post's attachments should not be nil"); 273 | XCTAssertEqual(testPost.attachments.count, 1, @"Test post's attachments should contain 1 object"); 274 | XCTAssertTrue(((JSONAPIResourceBase *)testPost.attachments.firstObject).class == MediaResource.class, @"First attachment should be of class MediaResource"); 275 | } 276 | 277 | #pragma mark - Private 278 | 279 | - (NSDictionary*)emptyRelationshipsExampleJSON { 280 | return [self jsonFor:@"empty_relationship_example" ofType:@"json"]; 281 | } 282 | 283 | - (NSDictionary*)genericRelationshipsExampleJSON { 284 | return [self jsonFor:@"generic_relationships_example" ofType:@"json"]; 285 | } 286 | 287 | - (NSDictionary*)mainExampleJSON { 288 | return [self jsonFor:@"main_example" ofType:@"json"]; 289 | } 290 | 291 | - (NSDictionary*)errorExampleJSON { 292 | return [self jsonFor:@"error_example" ofType:@"json"]; 293 | } 294 | 295 | - (NSDictionary*)jsonFor:(NSString*)resource ofType:(NSString*)type { 296 | NSString *path = [[NSBundle mainBundle] pathForResource:resource ofType:type]; 297 | NSString *jsonStr = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; 298 | 299 | return [NSJSONSerialization JSONObjectWithData:[jsonStr dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; 300 | } 301 | 302 | @end 303 | -------------------------------------------------------------------------------- /Project/JSONAPITests/empty_relationship_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "attributes": { 4 | "createdAt": "2015-10-28T19:35:28.194Z", 5 | "title": "Empty relationship test post", 6 | "text": "Test text" 7 | }, 8 | "id": 1, 9 | "type": "NewsFeedPost", 10 | "relationships": { 11 | "publisher": { 12 | "data": null 13 | }, 14 | "attachments" : { 15 | "data" : [ 16 | { 17 | "id" : 15, 18 | "type" : "Media" 19 | } 20 | ] 21 | } 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Project/JSONAPITests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Project/JSONAPITests/error_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "hehe": "hoho" 4 | }, 5 | "links": { 6 | "self": "http://example.com/posts", 7 | "next": "http://example.com/posts?page[offset]=2", 8 | "last": "http://example.com/posts?page[offset]=10" 9 | }, 10 | "data": [], 11 | "errors": [{ 12 | "id": "123456", 13 | "href": "/internet/is/fun", 14 | "status": "400", 15 | "code": "123abc", 16 | "title": "some title", 17 | "detail": "some detail", 18 | "links": ["a_link"], 19 | "source": ["a_path"] 20 | }] 21 | } -------------------------------------------------------------------------------- /Project/JSONAPITests/generic_relationships_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "data" : [ 3 | { 4 | "attributes" : { 5 | "createdAt" : "2015-10-28T19:35:28.194Z", 6 | "title" : "Test Post", 7 | "text" : "Long text of post example" 8 | }, 9 | "id" : 1, 10 | "type" : "NewsFeedPost", 11 | "relationships" : { 12 | "publisher" : { 13 | "data" : { 14 | "id" : 45, 15 | "type" : "User" 16 | } 17 | }, 18 | "attachments" : { 19 | "data" : [ 20 | { 21 | "id" : 15, 22 | "type" : "Media" 23 | }, 24 | { 25 | "id" : 10, 26 | "type" : "WebPage" 27 | } 28 | ] 29 | } 30 | } 31 | }, 32 | { 33 | "attributes" : { 34 | "createdAt" : "2015-11-28T19:35:26.194Z", 35 | "title" : "Second Test Post", 36 | "text" : "Long text of second post example" 37 | }, 38 | "id" : 2, 39 | "type" : "NewsFeedPost", 40 | "relationships" : { 41 | "publisher" : { 42 | "data" : { 43 | "id" : 23, 44 | "type" : "SocialCommunity" 45 | } 46 | }, 47 | "attachments" : { 48 | "data" : [] 49 | } 50 | } 51 | } 52 | ], 53 | "included" : [ 54 | { 55 | "attributes" : { 56 | "name" : "Sam", 57 | "email" : "sam@gmail.com" 58 | }, 59 | "id" : 45, 60 | "type" : "User", 61 | "relationships" : {} 62 | }, 63 | { 64 | "attributes" : { 65 | "title" : "Testing Social Community", 66 | "homePage" : "http://testCommunity.com" 67 | }, 68 | "id" : 23, 69 | "type" : "SocialCommunity", 70 | "relationships" : {} 71 | }, 72 | { 73 | "attributes" : { 74 | "mimeType" : "image/jpg", 75 | "fileUrl" : "http://testingservice.com/media/15.jpg" 76 | }, 77 | "id" : 15, 78 | "type" : "Media", 79 | "relationships" : {} 80 | }, 81 | { 82 | "attributes" : { 83 | "pageUrl" : "http://testingservice.com/content/testPage.html" 84 | }, 85 | "id" : 10, 86 | "type" : "WebPage", 87 | "relationships" : {} 88 | } 89 | ] 90 | } 91 | -------------------------------------------------------------------------------- /Project/JSONAPITests/main_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "hehe": "hoho" 4 | }, 5 | "links": { 6 | "self": "http://example.com/articles", 7 | "next": "http://example.com/articles?page[offset]=2", 8 | "last": "http://example.com/articles?page[offset]=10" 9 | }, 10 | "data": [{ 11 | "type": "articles", 12 | "id": "1", 13 | "meta": { 14 | "hehe": "hoho" 15 | }, 16 | "attributes": { 17 | "title": "JSON API paints my bikeshed!", 18 | "versions": [ 19 | "2015-09-01T12:15:00.000Z", 20 | "2015-08-01T06:15:00.000Z" 21 | ] 22 | }, 23 | "relationships": { 24 | "author": { 25 | "links": { 26 | "self": "http://example.com/articles/1/relationships/author", 27 | "related": "http://example.com/articles/1/author" 28 | }, 29 | "data": { "type": "people", "id": "9" } 30 | }, 31 | "comments": { 32 | "links": { 33 | "self": "http://example.com/articles/1/relationships/comments", 34 | "related": "http://example.com/articles/1/comments" 35 | }, 36 | "data": [ 37 | { "type": "comments", "id": "5" }, 38 | { "type": "comments", "id": "12" } 39 | ] 40 | } 41 | }, 42 | "links": { 43 | "self": "http://example.com/articles/1" 44 | } 45 | }], 46 | "included": [{ 47 | "type": "people", 48 | "id": "9", 49 | "attributes": { 50 | "first-name": "Dan", 51 | "last-name": "Gebhardt", 52 | "twitter": "dgeb" 53 | }, 54 | "links": { 55 | "self": "http://example.com/people/9" 56 | } 57 | }, { 58 | "type": "comments", 59 | "id": "5", 60 | "attributes": { 61 | "body": "First!" 62 | }, 63 | "relationships": { 64 | "author": { 65 | "data": { "type": "people", "id": "9" } 66 | } 67 | }, 68 | "links": { 69 | "self": "http://example.com/comments/5" 70 | } 71 | }, { 72 | "type": "comments", 73 | "id": "12", 74 | "attributes": { 75 | "body": "I like XML better" 76 | }, 77 | "relationships": { 78 | "author": { 79 | "data": { "type": "people", "id": "9" } 80 | } 81 | }, 82 | "links": { 83 | "self": "http://example.com/comments/12" 84 | } 85 | }] 86 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSONAPI - iOS 2 | 3 | [![Build Status](https://travis-ci.org/joshdholtz/jsonapi-ios.png?branch=master)](https://travis-ci.org/joshdholtz/jsonapi-ios) 4 | ![](https://cocoapod-badges.herokuapp.com/v/JSONAPI/badge.png) 5 | [![Join the chat at https://gitter.im/joshdholtz/jsonapi-ios](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/joshdholtz/jsonapi-ios?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | 7 | A library for loading data from a [JSON API](http://jsonapi.org) datasource. Parses JSON API data into models with support for linking of properties and other resources. 8 | 9 | ### Quick Usage 10 | ```objc 11 | NSDictionary *json = [self responseFromAPIRequest]; 12 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 13 | 14 | ArticleResource *article = jsonAPI.resource; 15 | NSLog(@"Title: %@", article.title); 16 | ``` 17 | 18 | For some full examples on how to use everything, please see the tests - https://github.com/joshdholtz/jsonapi-ios/blob/master/Project/JSONAPITests/JSONAPITests.m 19 | 20 | ### Updates 21 | 22 | Version | Changes 23 | --- | --- 24 | **1.0.7** | Added `meta` and `setMeta` to `JSONAPIResource` and `JSONAPIResourceBase` (https://github.com/joshdholtz/jsonapi-ios/pull/43). 25 | **1.0.6** | Improved resource parsing and added parsing of `selfLinks` (https://github.com/joshdholtz/jsonapi-ios/pull/35). Thanks to [ artcom](https://github.com/ artcom) for helping! Also removed the need to define `setIdProperty` and `setSelfLinkProperty` in every resource (automatically mapped in the init of `JSONAPIResourceDescriptor`) 26 | **1.0.5** | Fix 1-to-many relationships serialization according to JSON API v1.0 (https://github.com/joshdholtz/jsonapi-ios/pull/34). Thanks to [RafaelKayumov](https://github.com/RafaelKayumov) for helping! 27 | **1.0.4** | Add support for empty to-one relationship according to JSON API v1.0 (https://github.com/joshdholtz/jsonapi-ios/pull/33). Thanks to [RafaelKayumov](https://github.com/RafaelKayumov) for helping! 28 | **1.0.3** | Add ability to map different types of objects (https://github.com/joshdholtz/jsonapi-ios/pull/32). Thanks to [ealeksandrov](https://github.com/ealeksandrov) for helping! 29 | **1.0.2** | Just some bug fixes. Thanks to [christianklotz](https://github.com/christianklotz) for helping again! 30 | **1.0.1** | Now safely checks for `NSNull` in the parsed JSON. Thanks to [christianklotz](https://github.com/christianklotz) for that fix! 31 | **1.0.0** | We did it team! We are at the `JSON API 1.0 final` spec. Resources now use `JSONAPIResourceDescriptor` for more explicit definitions. **HUGE** thanks to [jkarmstr](https://github.com/jkarmstr) for doing all the dirty work. Also thanks to [edopelawi ](https://github.com/edopelawi ), [BenjaminDigeon](https://github.com/BenjaminDigeon), and [christianklotz](https://github.com/christianklotz) for some bug fixes! 32 | **1.0.0-rc1** | Rewrote core of `JSONAPI` and `JSONAPIResource` and all unit tests to be up to spec with JSON API spec 1.0.0-rc3. Removed `JSONAPIResourceLinker`. Added `JSONAPIErrorResource` 33 | **0.2.0** | Added `NSCopying` and `NSCoded` to `JSONAPIResource`; Added `JSONAPIResourceFormatter` to format values before getting mapped - [more info](#formatter) 34 | **0.1.2** | `JSONAPIResource` IDs can either be numbers or strings (thanks [danylhebreux](https://github.com/danylhebreux)); `JSONAPIResource` subclass can have mappings defined to set JSON values into properties automatically - [more info](#resource-mappings) 35 | **0.1.1** | Fixed linked resources with links so they actually link to other linked resources 36 | **0.1.0** | Initial release 37 | 38 | ### Features 39 | - Allows resource types to be created into subclasses of `JSONAPIResource` 40 | - Set mapping for `JSONAPIResource` subclass to set JSON values and relationships into properties 41 | 42 | ## Installation 43 | 44 | ### Drop-in Classes 45 | Clone the repository and drop in the .h and .m files from the "Classes" directory into your project. 46 | 47 | ### CocoaPods 48 | 49 | JSONAPI is available through [CocoaPods](http://cocoapods.org), to install 50 | it simply add the following line to your Podfile: 51 | 52 | pod 'JSONAPI', '~> 1.0.7' 53 | 54 | ## Classes/protocols 55 | For some full examples on how to use everything, please see the tests - https://github.com/joshdholtz/jsonapi-ios/blob/master/Project/JSONAPITests/JSONAPITests.m 56 | 57 | ### JSONAPI 58 | `JSONAPI` parses and validates a JSON API document into a usable object. This object holds the response as an NSDictionary but provides methods to accomdate the JSON API format such as `meta`, `errors`, `linked`, `resources`, and `includedResources`. 59 | 60 | ### JSONAPIResource 61 | Protocol of an object that is available for JSON API serialization. When developing model classes for use with JSON-API, it is suggested that classes be derived from `JSONAPIResourceBase`, but that is not required. An existing model class can be adapted for JSON-API by implementing this protocol. 62 | 63 | ### JSONAPIResourceBase 64 | `JSONAPIResourceBase` is an object (that gets subclassed) that holds data for each resource in a JSON API document. This objects holds the "id" as `ID` and link for self as `selfLink` as well as attributes and relationships defined by descriptors (see below) 65 | 66 | ### JSONAPIResourceDescriptor 67 | `+ (JSONAPIResourceDescriptor*)descriptor` should be overwritten to define descriptors for mapping of JSON keys and relationships into properties of a subclassed JSONAPIResource. 68 | 69 | ## Full Example 70 | 71 | ### ViewController.m 72 | 73 | ```objc 74 | NSDictionary *json = [self responseFromAPIRequest]; 75 | JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; 76 | 77 | ArticleResource *article = jsonAPI.resource; 78 | NSLog(@"Title: %@", article.title); 79 | NSLog(@"Author: %@ %@", article.author.firstName, article.author.lastName); 80 | NSLog(@"Comment Count: %ld", article.comments.count); 81 | ``` 82 | 83 | ### ArticleResource.h 84 | 85 | ```objc 86 | 87 | @interface ArticleResource : JSONAPIResourceBase 88 | 89 | @property (nonatomic, strong) NSString *title; 90 | @property (nonatomic, strong) PeopleResource *author; 91 | @property (nonatomic, strong) NSDate *date; 92 | @property (nonatomic, strong) NSArray *comments; 93 | 94 | @end 95 | ``` 96 | 97 | ### ArticleResource.m 98 | 99 | ```objc 100 | @implementation ArticleResource 101 | 102 | static JSONAPIResourceDescriptor *__descriptor = nil; 103 | 104 | + (JSONAPIResourceDescriptor*)descriptor { 105 | static dispatch_once_t onceToken; 106 | dispatch_once(&onceToken, ^{ 107 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"articles"]; 108 | 109 | [__descriptor addProperty:@"title"]; 110 | [__descriptor addProperty:@"date" 111 | withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"date" withFormat:[NSDateFormatter RFC3339DateFormatter]]]; 112 | 113 | [__descriptor hasOne:[PeopleResource class] withName:@"author"]; 114 | [__descriptor hasMany:[CommentResource class] withName:@"comments"]; 115 | }); 116 | 117 | return __descriptor; 118 | } 119 | 120 | @end 121 | 122 | ``` 123 | 124 | ### PeopleResource.h 125 | 126 | ```objc 127 | @interface PeopleResource : JSONAPIResourceBase 128 | 129 | @property (nonatomic, strong) NSString *firstName; 130 | @property (nonatomic, strong) NSString *lastName; 131 | @property (nonatomic, strong) NSString *twitter; 132 | 133 | @end 134 | ``` 135 | 136 | ### PeopleResource.m 137 | 138 | ```objc 139 | @implementation PeopleResource 140 | 141 | static JSONAPIResourceDescriptor *__descriptor = nil; 142 | 143 | + (JSONAPIResourceDescriptor*)descriptor { 144 | static dispatch_once_t onceToken; 145 | dispatch_once(&onceToken, ^{ 146 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"people"]; 147 | 148 | [__descriptor addProperty:@"firstName" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"first-name"]]; 149 | [__descriptor addProperty:@"lastName" withJsonName:@"last-name"]; 150 | [__descriptor addProperty:@"twitter"]; 151 | }); 152 | 153 | return __descriptor; 154 | } 155 | 156 | @end 157 | ``` 158 | 159 | ### CommentResource.h 160 | 161 | ```objc 162 | @interface CommentResource : JSONAPIResourceBase 163 | 164 | @property (nonatomic, strong) NSString *text; 165 | @property (nonatomic, strong) PeopleResource *author; 166 | 167 | @end 168 | ``` 169 | 170 | ### CommentResource.m 171 | 172 | ```objc 173 | @implementation CommentResource 174 | 175 | static JSONAPIResourceDescriptor *__descriptor = nil; 176 | 177 | + (JSONAPIResourceDescriptor*)descriptor { 178 | static dispatch_once_t onceToken; 179 | dispatch_once(&onceToken, ^{ 180 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"comments"]; 181 | 182 | [__descriptor addProperty:@"text" withJsonName:@"body"]; 183 | 184 | [__descriptor hasOne:[PeopleResource class] withName:@"author"]; 185 | }); 186 | 187 | return __descriptor; 188 | } 189 | 190 | @end 191 | ``` 192 | 193 | ## Advanced 194 | 195 | ### How to do custom "sub-resources" mappings 196 | Sometimes you may have parts of a resource that need to get mapped to something more specific than just an `NSDictionary`. Below is an example on how to map an `NSDictionary` to non-JSONAPIResource models. 197 | 198 | We are essentially creating a property descriptor that maps to a private property on the resource. We then override that properties setter and do our custom mapping there. 199 | 200 | #### Resource part of JSON API Response 201 | ```js 202 | "attributes":{ 203 | "title": "Something something blah blah blah" 204 | "image": { 205 | "large": "http://someimageurl.com/large", 206 | "medium": "http://someimageurl.com/medium", 207 | "small": "http://someimageurl.com/small" 208 | } 209 | } 210 | ``` 211 | 212 | #### ArticleResource.h 213 | 214 | ```objc 215 | 216 | @interface ArticleResource : JSONAPIResourceBase 217 | 218 | @property (nonatomic, strong) NSString *title; 219 | 220 | // The properties we are pulling out of a a "images" dictionary 221 | @property (nonatomic, storng) NSString *largeImageUrl; 222 | @property (nonatomic, storng) NSString *mediumImageUrl; 223 | @property (nonatomic, storng) NSString *smallImageUrl; 224 | 225 | @end 226 | ``` 227 | 228 | #### ArticleResource.m 229 | 230 | ```objc 231 | @interface ArticleResource() 232 | 233 | // Private variable used to store raw NSDictionary 234 | // We will override the setter and set our custom properties there 235 | @property (nonatomic, strong) NSDictionary *rawImage; 236 | 237 | @end 238 | 239 | @implementation ArticleResource 240 | 241 | static JSONAPIResourceDescriptor *__descriptor = nil; 242 | 243 | + (JSONAPIResourceDescriptor*)descriptor { 244 | static dispatch_once_t onceToken; 245 | dispatch_once(&onceToken, ^{ 246 | __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"articles"]; 247 | 248 | [__descriptor addProperty:@"title"]; 249 | [__descriptor addProperty:@"rawImage" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"image"]]; 250 | 251 | }); 252 | 253 | return __descriptor; 254 | } 255 | 256 | - (void)setRawImage:(NSDictionary*)rawImage { 257 | _rawImage = rawImage; 258 | 259 | // Pulling the large, medium, and small urls out when 260 | // this property gets set by the JSON API parser 261 | _largeImageUrl = _rawImage[@"large"]; 262 | _mediumImageUrl = _rawImage[@"medium"]; 263 | _smallImageUrl = _rawImage[@"small"]; 264 | } 265 | 266 | @end 267 | 268 | ``` 269 | 270 | ## Author 271 | 272 | Josh Holtz, me@joshholtz.com, [@joshdholtz](https://twitter.com/joshdholtz) 273 | 274 | ## License 275 | 276 | JSONAPI is available under the MIT license. See the LICENSE file for more info. 277 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | desc "Runs the specs [EMPTY]" 2 | task :spec do 3 | # Provide your own implementation 4 | end 5 | 6 | task :version do 7 | git_remotes = `git remote`.strip.split("\n") 8 | 9 | if git_remotes.count > 0 10 | puts "-- fetching version number from github" 11 | sh 'git fetch' 12 | 13 | remote_version = remote_spec_version 14 | end 15 | 16 | if remote_version.nil? 17 | puts "There is no current released version. You're about to release a new Pod." 18 | version = "0.0.1" 19 | else 20 | puts "The current released version of your pod is " + remote_spec_version.to_s() 21 | version = suggested_version_number 22 | end 23 | 24 | puts "Enter the version you want to release (" + version + ") " 25 | new_version_number = $stdin.gets.strip 26 | if new_version_number == "" 27 | new_version_number = version 28 | end 29 | 30 | replace_version_number(new_version_number) 31 | end 32 | 33 | desc "Release a new version of the Pod" 34 | task :release do 35 | 36 | puts "* Running version" 37 | sh "rake version" 38 | 39 | unless ENV['SKIP_CHECKS'] 40 | if `git symbolic-ref HEAD 2>/dev/null`.strip.split('/').last != 'master' 41 | $stderr.puts "[!] You need to be on the `master' branch in order to be able to do a release." 42 | exit 1 43 | end 44 | 45 | if `git tag`.strip.split("\n").include?(spec_version) 46 | $stderr.puts "[!] A tag for version `#{spec_version}' already exists. Change the version in the podspec" 47 | exit 1 48 | end 49 | 50 | puts "You are about to release `#{spec_version}`, is that correct? [y/n]" 51 | exit if $stdin.gets.strip.downcase != 'y' 52 | end 53 | 54 | puts "* Running specs" 55 | sh "rake spec" 56 | 57 | puts "* Linting the podspec" 58 | sh "pod lib lint" 59 | 60 | # Then release 61 | sh "git commit #{podspec_path} CHANGELOG.md -m 'Release #{spec_version}'" 62 | sh "git tag -a #{spec_version} -m 'Release #{spec_version}'" 63 | sh "git push origin master" 64 | sh "git push origin --tags" 65 | sh "pod push master #{podspec_path}" 66 | end 67 | 68 | # @return [Pod::Version] The version as reported by the Podspec. 69 | # 70 | def spec_version 71 | require 'cocoapods' 72 | spec = Pod::Specification.from_file(podspec_path) 73 | spec.version 74 | end 75 | 76 | # @return [Pod::Version] The version as reported by the Podspec from remote. 77 | # 78 | def remote_spec_version 79 | require 'cocoapods-core' 80 | 81 | if spec_file_exist_on_remote? 82 | remote_spec = eval(`git show origin/master:#{podspec_path}`) 83 | remote_spec.version 84 | else 85 | nil 86 | end 87 | end 88 | 89 | # @return [Bool] If the remote repository has a copy of the podpesc file or not. 90 | # 91 | def spec_file_exist_on_remote? 92 | test_condition = `if git rev-parse --verify --quiet origin/master:#{podspec_path} >/dev/null; 93 | then 94 | echo 'true' 95 | else 96 | echo 'false' 97 | fi` 98 | 99 | 'true' == test_condition.strip 100 | end 101 | 102 | # @return [String] The relative path of the Podspec. 103 | # 104 | def podspec_path 105 | podspecs = Dir.glob('*.podspec') 106 | if podspecs.count == 1 107 | podspecs.first 108 | else 109 | raise "Could not select a podspec" 110 | end 111 | end 112 | 113 | # @return [String] The suggested version number based on the local and remote version numbers. 114 | # 115 | def suggested_version_number 116 | if spec_version != remote_spec_version 117 | spec_version.to_s() 118 | else 119 | next_version(spec_version).to_s() 120 | end 121 | end 122 | 123 | # @param [Pod::Version] version 124 | # the version for which you need the next version 125 | # 126 | # @note It is computed by bumping the last component of the versino string by 1. 127 | # 128 | # @return [Pod::Version] The version that comes next after the version supplied. 129 | # 130 | def next_version(version) 131 | version_components = version.to_s().split("."); 132 | last = (version_components.last.to_i() + 1).to_s 133 | version_components[-1] = last 134 | Pod::Version.new(version_components.join(".")) 135 | end 136 | 137 | # @param [String] new_version_number 138 | # the new version number 139 | # 140 | # @note This methods replaces the version number in the podspec file with a new version number. 141 | # 142 | # @return void 143 | # 144 | def replace_version_number(new_version_number) 145 | text = File.read(podspec_path) 146 | text.gsub!(/(s.version( )*= ")#{spec_version}(")/, "\\1#{new_version_number}\\3") 147 | File.open(podspec_path, "w") { |file| file.puts text } 148 | end --------------------------------------------------------------------------------