In this tutorial we’re going to create a set of schemas (User
, Board
and Pin
) that are specified in the Pinterest Developer API and generate
them using Plank. By the end of this exercise you will have the foundation to write
complex schemas for your own application.
Schemas are defined as a JSON document. In this tutorial we’ll create the schemas necessary to represent the core User
, Board
and Pin
model objects.
Here are the fields for User
Attribute | Type | Description |
---|---|---|
id | string | The unique string of numbers and letters that identifies the user on Pinterest. |
username | string | The user’s Pinterest username. |
first_name | string | The user’s first name. |
last_name | string | The user’s last name. |
bio | string | The text in the user’s “About you” section in their profile. |
created_at | string in ISO 8601 format | The date the user created their account. |
counts | map<string,int> | The user’s stats, including how many Pins, follows, boards and likes they have. |
image | map<string,image> | The user’s profile image. The response returns the image’s URL, width and height. |
User
schema (user.json
)
{
"id": "user.json",
"title": "user",
"description" : "Schema definition of Pinterest User",
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"id" : { "type": "string" },
"username" : { "type": "string" },
"first_name" : { "type": "string" },
"last_name" : { "type": "string" },
"bio" : { "type": "string" },
"created_at" : {
"type": "string",
"format": "date-time"
},
"counts": {
"type": "object",
"additionalProperties": { "type": "integer" }
},
"image": { "$ref": "image.json" }
}
}
A couple things to make a note of from our first schema. We have modeled a
number of different types including String
, DateTime
, Map<String, Int>
and a
reference to an Image
object.
created_at
) can represent more concrete types like DateTime and URI through the use of the format
key. This allows Plank to enforce greater validation and generate a more specific type in your code. For example, in Objective-C the date-time
and uri
formats are represented as NSDate
and NSURL
respectively instead of NSString
.counts
) represents a object of key, value pairs. The key is always a String type in accordance with the JSON specification and the value is specified using additionalProperties
. The value of additionalProperties
is an object that describes the type and can be any of the types Plank supports. This will be represented as NSDictionary<NSString *, NSNumber *>
for Objective-C.image
) allow you to specify another schema as a type. This reference is declared using the JSON Pointer keyword $ref
. Plank will determine the file path to the specified schema relative to the value of id
. When Plank generates this schema it will also generate any references schemas recursively.Now that we’ve got our User
schema written lets move on to Board
!
Here are the fields for Board
Attribute | Type | Description |
---|---|---|
id | string | The unique string of numbers and letters that identifies the board on Pinterest. |
name | string | The name of the board. |
url | string | The link to the board. |
description | string | The user-entered description of the board. |
creator | map<string,string> | The first and last name, ID and profile URL of the user who created the board. |
created_at | string in ISO 8601 format | The date the user created the board. |
counts | map<string,int> | The board’s stats, including how many Pins, followers, user’s following and collaborators it has. |
image | map<string,image> | The user’s profile image. The response returns the image’s URL, width and height. |
Board
schema (board.json
)
{
"id": "board.json",
"title": "board",
"description" : "Schema definition of Pinterest Board",
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"id" : { "type": "string" },
"name" : { "type": "string" },
"url" : {
"type": "string",
"format": "uri"
},
"description" : { "type": "string" },
"creator": {
"type": "object",
"additionalProperties": { "type": "string" }
},
"created_at" : {
"type": "string",
"format": "date-time"
},
"counts": {
"type": "object",
"additionalProperties": { "type": "integer" }
},
"image": { "$ref": "image.json" }
},
"required": []
}
Our Board
schema is fairly straightforward and shows a different usage of the format
key for the String
type to specify that url
is a URI
formatted string..
Last but not least, the we need to create Pin
(pin.json
)
Attribute | Type | Description |
---|---|---|
id | string | The unique string of numbers and letters that identifies the Pin on Pinterest. |
link | string | The URL of the webpage where the Pin was created. |
url | string | The URL of the Pin on Pinterest. |
creator | map<string,string> | The first and last name, ID and profile URL of the user who created the board. |
board | board | The board that the Pin is on. |
created_at | string in ISO 8601 format | The date the Pin was created. |
note | string | The user-entered description of the Pin. |
color | string | The dominant color of the Pin’s image in hex code format. |
counts | map<string,int> | The Pin’s stats, including the number of repins, comments and likes. |
media | map<string,string> | The media type of the Pin (image or video). |
attribution | map<string,string> | The source data for videos, including the title, URL, provider, author name, author URL and provider name. |
image | map<string,image> | The Pin’s image. The default response returns the image’s URL, width and height. |
metadata | map<string,object> | Extra information about the Pin for Rich Pins. Includes the Pin type (e.g., article, recipe) and related information (e.g., ingredients, author). |
Pin
schema (pin.json
)
{
"id": "pin.json",
"title": "pin",
"description" : "Schema definition of Pinterest Pin",
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"id" : { "type": "string" },
"link" : {
"type": "string",
"format": "uri"
},
"url" : {
"type": "string",
"format": "uri"
},
"creator": {
"type": "object",
"additionalProperties": { "$ref": "user.json" }
},
"board": { "$ref": "board.json" },
"created_at" : {
"type": "string",
"format": "date-time"
},
"note" : { "type": "string" },
"color" : { "type": "string" },
"counts": {
"type": "object",
"additionalProperties": { "type": "integer" }
},
"media": {
"type": "object",
"additionalProperties": { "type": "string" }
},
"attribution": {
"type": "object",
"additionalProperties": { "type": "string" }
},
"description" : { "type": "string" },
"image": { "$ref": "image.json" }
},
"required": []
}
Although it’s not listed in the developer.pinterest.com documentation, our schemas reference an image type that contains a URI, width and height. Lets actually create a schema to structure this type.
Attribute | Type | Description |
---|---|---|
url | string | The URI for the image |
width | int | The width of the image |
height | int | The height of the image |
{
"id": "image.json",
"title": "image",
"description" : "Schema definition of Pinterest image",
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"url" : {
"type": "string",
"format": "uri"
},
"width": { "type": "integer" },
"height": { "type": "integer" }
}
}
To generate the models, you can run Plank with all the schema files specified.
$ plank pin.json user.json board.json image.json
Plank automatically generates all referenced schemas recursively so the above
command has the same output as if we just generated pin.json
. The generator will not only generate this class but it will generate its superclass (if defined) and any other class that is referenced by a JSON Pointer. The classes will be available for you in the current working directory.
$ plank pin.json
After we run these commands we should have Objective-C header and implementation files generated for each schema.
$ ls
Pin.h Pin.m Board.h Board.m User.h User.m Image.h Image.m
Since we’re generating Objective-C files, a common convention is to create a
common prefix for your applications classes. Plank allows you to do this
through the --objc_class_prefix
option.
generated for each schema.
$ plank --objc_class_prefix PDK pin.json
$ ls
PDKPin.h PDKPin.m PDKBoard.h PDKBoard.m PDKUser.h PDKUser.m PDKImage.h PDKImage.m
Now we can view our files and integrate them into our project. Here is the header we’ve generated for user.json
.
//
// PDKUser.h
// Autogenerated by Plank (https://pinterest.github.io/plank/)
//
// DO NOT EDIT - EDITS WILL BE OVERWRITTEN
// @generated
//
#import <Foundation/Foundation.h>
@class PDKImage;
@class PDKUserBuilder;
NS_ASSUME_NONNULL_BEGIN
@interface PDKUser : NSObject<NSCopying, NSSecureCoding>
@property (nullable, nonatomic, copy, readonly) NSString * lastName;
@property (nullable, nonatomic, copy, readonly) NSString * identifier;
@property (nullable, nonatomic, copy, readonly) NSString * firstName;
@property (nullable, nonatomic, strong, readonly) PDKImage * image;
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, NSNumber *> * counts;
@property (nullable, nonatomic, strong, readonly) NSDate * createdAt;
@property (nullable, nonatomic, copy, readonly) NSString * username;
@property (nullable, nonatomic, copy, readonly) NSString * bio;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dictionary;
- (instancetype)initWithModelDictionary:(NSDictionary *)modelDictionary;
- (instancetype)initWithBuilder:(PDKUserBuilder *)builder;
- (instancetype)initWithBuilder:(PDKUserBuilder *)builder initType:(PIModelInitType)initType;
- (instancetype)copyWithBlock:(void (^)(PDKUserBuilder *builder))block;
- (BOOL)isEqualToUser:(PDKUser *)anObject;
- (instancetype)mergeWithModel:(PDKUser *)modelObject;
- (instancetype)mergeWithModel:(PDKUser *)modelObject initType:(PIModelInitType)initType;
@end
@interface PDKUserBuilder : NSObject
@property (nullable, nonatomic, copy, readwrite) NSString * lastName;
@property (nullable, nonatomic, copy, readwrite) NSString * identifier;
@property (nullable, nonatomic, copy, readwrite) NSString * firstName;
@property (nullable, nonatomic, strong, readwrite) PDKImage * image;
@property (nullable, nonatomic, strong, readwrite) NSDictionary<NSString *, NSNumber *> * counts;
@property (nullable, nonatomic, strong, readwrite) NSDate * createdAt;
@property (nullable, nonatomic, copy, readwrite) NSString * username;
@property (nullable, nonatomic, copy, readwrite) NSString * bio;
- (instancetype)initWithModel:(PDKUser *)modelObject;
- (PDKUser *)build;
- (void)mergeWithModel:(PDKUser *)modelObject;
@end
NS_ASSUME_NONNULL_END
Now you have experience modeling data models and generating code using Plank. There are many more topics in this example we didn’t get to including Algebraic Data Types and Schema inheritance but these are all detailed in the JSON Schema reference and Objective-C reference.