You can use Hyperdrive with any JSON API which is described by an API Blueprint. You are not limited to Hypermedia APIs, any API Blueprint can be introspected to determine the hypermedia controls that an API may afford to the user.
Instead of entering the API as you would for a Hypermedia API you will need
to enter it via the HyperBlueprint
extension of Hyperdrive.
If our API Blueprint is hosted on Apiary, you may
enter it by providing the Apiary API domain, for example
pollsapp
.
HyperBlueprint.enter(apiary: "pollsapp") { result in
switch result {
case .Success(let hyperdrive, let representor):
println("Success.")
case .Failure(let error):
println("Failed to enter API \(error)")
}
}
You may also enter an API Blueprint that you've hosted yourself:
HyperBlueprint.enter(blueprintURL: "https://raw.githubusercontent.com/apiaryio/polls-app/master/apiary.apib")
By default, Hyperdrive will use the HOST
URL for your API configured in
the API Blueprint. You may also overide this in your client by providing
a custom base URL:
let root = NSURL(string: "https://polls.apiblueprint.org/")
HyperBlueprint.enter(apiary: "pollsapp", baseURL: root)
HyperBlueprint makes extensive use of relations and MSON descriptions in a blueprint to provide hypermedia controls.
By default, any safe idempotent (GET) actions which do not have any required parameters or attributes are available as the initial transitions. For example, the following action will be shown when we enter our API:
## Question Collection [/questions]
+ Attributes (array[Question])
### List All Questions [GET]
+ Relation: questions
Which can be used via the following:
if let questions = representor.transitions["questions"] {
// We have a transition to questions
hyperdrive.request(questions) { result in
// We've retreived the questions or received an error while trying to
}
}
The resultant Representor for the questions collection will contain a
list of representors to the questions included. Since we declared that
the questions collection is of the type array[Question]
,
Hyperdrive will pair this to the Question resource which looks as follows.
## Question [/questions/{question_id}]
+ Parameters
+ question_id: 1 (required, number) - ID of the Question in form of an integer
+ Attributes
+ question: `Favourite programming language?` (string, required)
+ choices (array[Choice], required) - An array of Choice objects
Again, Hyperdrive can determine that the choices
attribute of our
Question
resource is an array of Choice
resources. So given our questions
collection representor, we can retrieve the questions resources, and
each questions choices as follows:
if let questions = representor.representors["questions"] {
for question in questions {
println("Question: \(question.attributes["question"])")
if let choices = question.representors["choices"] {
for choice in choices {
println("Choice: \(choice.attributes["choice"])")
}
}
}
}
The Choice
resource in our API Blueprint looks as follows:
## Choice [/questions/{question_id}/choices/{choice_id}]
+ Parameters
+ question_id (required, number, `1`) ... ID of the Question in form of an integer
+ choice_id (required, number, `1`) ... ID of the Choice in form of an integer
+ Attributes
+ choice: Swift (string, required)
+ votes: 0 (number, required)
### Vote on a Choice [POST]
+ Relation: vote
You can see we have an action with the relation of vote
on our Choice
resource. This is exposed in our Representor of the Choice resource. We
can follow this transition to perform this action without our client ever
knowing about the implementation details such as any URIs or HTTP methods
used in our API.
if let vote = choice.transitions["vote"] {
hyperdrive.request(vote)
}
Our actions may also provide attributes, such as our action to create a new question in our API. In the blueprint it looks as follows:
### Create a New Question [POST]
+ Relation: create
+ Attributes
+ question (string, required) - The question
+ choices (array[string]) - A collection of choices.
+ Response 201 (application/json)
+ Attributes (Question)
We have declared that this action takes two attributes, question and choices. This allows us to introspect these attributes in our transition as follows:
if let create = representor.transitions["create"] {
println("We can create a new question with the following attributes:")
for attribute in create.attributes {
println(attribute)
}
}
This can allow you to generate user interface based on the blueprint, or even remove fields when they are no longer used in our API. Along with using the attribute for user input validation.