Edit on GitHub

Tutorial

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.

Creating a Schema

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.

  • Strings (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.
  • Map (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.
  • Schema references (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" }
	}
}

Generate the Model

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

Conclusion

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.