Interfacing with GraphQL in Swift
Sommer Panage • @sommer
1 — Sommer Panage • @sommer • Swift Summit 2016
Hello!
2 — Sommer Panage • @sommer • Swift Summit 2016
What is GraphQL?
3 — Sommer Panage • @sommer • Swift Summit 2016
The GraphQL Schema
type User {
name: String!
id: Int!
email: String
twitter: String
}
4 — Sommer Panage • @sommer • Swift Summit 2016
Instead of hitting a REST endpoint like...
https://mybackend.com/api/user?id=1
5 — Sommer Panage • @sommer • Swift Summit 2016
I write a query like...
{
user(id: 1) {
name
email
twitter
}
}
And send it to my GraphQL endpoint
https://mybackend.com/graphql
6 — Sommer Panage • @sommer • Swift Summit 2016
And I get back a JSON response like...
{
"data": {
"name": "Sommer Panage",
"email": "sommer@panage.org",
"twitter": "@sommer"
}
}
7 — Sommer Panage • @sommer • Swift Summit 2016
Why is GraphQL such a
big deal for mobile?
8 — Sommer Panage • @sommer • Swift Summit 2016
Let's write an app
9 — Sommer Panage • @sommer • Swift Summit 2016
In a REST world, we'd hit this endpoint:
http://swapi.co/api/people/1/
And we'd get back...
10 — Sommer Panage • @sommer • Swift Summit 2016
{
"name": "Luke Skywalker",
"height": "1.72 m",
"mass": "77 Kg",
"hair_color": "Blond",
"skin_color": "Caucasian",
"eye_color": "Blue",
"birth_year": "19 BBY",
"gender": "Male",
"homeworld": "http://swapi.co/api/planets/1/",
"films": [
"http://swapi.co/api/films/1/",
"http://swapi.co/api/films/2/",
"http://swapi.co/api/films/3/"
],
"species": [
"http://swapi.co/api/species/1/"
],
"vehicles": [
"http://swapi.co/api/vehicles/14/",
"http://swapi.co/api/vehicles/30/"
],
"starships": [
"http://swapi.co/api/starships/12/",
"http://swapi.co/api/starships/22/"
],
"created": "2014-12-09T13:50:51.644000Z",
"edited": "2014-12-10T13:52:43.172000Z",
"url": "http://swapi.co/api/people/1/",
"image_url": "http://static.srcdn.com/wp-content/uploads/luke-skywalker-star-wars-a-new-hope.jpg"
}
11 — Sommer Panage • @sommer • Swift Summit 2016
And then...we'd make two more calls to...
http://swapi.co/api/starships/12/
and
http://swapi.co/api/starships/22/
12 — Sommer Panage • @sommer • Swift Summit 2016
{
"name": "X-wing",
"model": "T-65 X-wing",
"manufacturer": "Incom Corporation",
"cost_in_credits": "149999",
"length": "12.5",
"max_atmosphering_speed": "1050",
"crew": "1",
"passengers": "0",
"cargo_capacity": "110",
"consumables": "1 week",
"hyperdrive_rating": "1.0",
"MGLT": "100",
"starship_class": "Starfighter",
"pilots": [
"http://swapi.co/api/people/1/",
"http://swapi.co/api/people/9/",
"http://swapi.co/api/people/18/",
"http://swapi.co/api/people/19/"
],
"films": [
"http://swapi.co/api/films/3/",
"http://swapi.co/api/films/2/",
"http://swapi.co/api/films/1/"
],
"created": "2014-12-12T11:19:05.340000Z",
"edited": "2014-12-22T17:35:44.491233Z",
"url": "http://swapi.co/api/starships/12/"
}
13 — Sommer Panage • @sommer • Swift Summit 2016
{
"name": "Imperial shuttle",
"model": "Lambda-class T-4a shuttle",
"manufacturer": "Sienar Fleet Systems",
"cost_in_credits": "240000",
"length": "20",
"max_atmosphering_speed": "850",
"crew": "6",
"passengers": "20",
"cargo_capacity": "80000",
"consumables": "2 months",
"hyperdrive_rating": "1.0",
"MGLT": "50",
"starship_class": "Armed government transport",
"pilots": [
"http://swapi.co/api/people/1/",
"http://swapi.co/api/people/13/",
"http://swapi.co/api/people/14/"
],
"films": [
"http://swapi.co/api/films/3/",
"http://swapi.co/api/films/2/"
],
"created": "2014-12-15T13:04:47.235000Z",
"edited": "2014-12-22T17:35:44.795405Z",
"url": "http://swapi.co/api/starships/22/"
}
14 — Sommer Panage • @sommer • Swift Summit 2016
That's 3 calls and a whole lot of data for 1 little VC
15 — Sommer Panage • @sommer • Swift Summit 2016
Now in GraphQL, instead we'd write the following query:
{
person(personID: 1) {
name,
height,
mass,
hairColor,
eyeColor,
imageURL,
starshipConnection {
edges {
node {
name
}
}
}
}
}
16 — Sommer Panage • @sommer • Swift Summit 2016
And, I'd get back exactly what I wanted!
{
"data": {
"person": {
"name": "Luke Skywalker",
"height": 172,
"mass": 77,
"hairColor": "blond",
"eyeColor": "blue",
"imageURL": "http://static.srcdn.com/wp-content/uploads/luke-skywalker-star-wars-a-new-hope.jpg",
"starshipConnection": {
"edges": [
{
"node": {
"name": "X-wing"
}
},
{
"node": {
"name": "Imperial shuttle"
}
}
]
}
}
}
}
17 — Sommer Panage • @sommer • Swift Summit 2016
So, why is GraphQL such a big deal for mobile?
1. Ask and ye shall receive!
2. Fewer round trips!
3. Less client logic around data!
18 — Sommer Panage • @sommer • Swift Summit 2016
Wait just a minute...
19 — Sommer Panage • @sommer • Swift Summit 2016
Our Star Wars Character model
struct SWCharacter {
let name: String
let heightInCm: Int
let massInKg: Int
let hairColorDescriptor: String
let skinColorDescriptor: String
let eyeColorDescriptor: String
let birthYear: StarWarsUniverse.Year
let gender: StarWarsUniverse.Gender
let homeworld: StarWarsUniverse.Planet
let films: [SWFilm]
let species: [StarWarsUniverse.Species]
let vehicles: [StarWarsUniverse.Vehicle]
let starships: [StarWarsUniverse.Starship]
let imageURL: URL?
}
20 — Sommer Panage • @sommer • Swift Summit 2016
Our Star Wars Character model
struct SWCharacter {
let name: String?
let heightInCm: Int?
let massInKg: Int?
let hairColorDescriptor: String?
let skinColorDescriptor: String?
let eyeColorDescriptor: String
let birthYear: StarWarsUniverse.Year?
let gender: StarWarsUniverse.Gender?
let homeworld: StarWarsUniverse.Planet?
let films: [SWFilm]?
let species: [StarWarsUniverse.Species]?
let vehicles: [StarWarsUniverse.Vehicle]?
let starships: [StarWarsUniverse.Starship]?
let imageURL: URL?
}
21 — Sommer Panage • @sommer • Swift Summit 2016
Swift, No!!!!
struct SWCharacter {
let name: String?
let heightInCm: Int?
let massInKg: Int?
let hairColorDescriptor: String?
let skinColorDescriptor: String?
let eyeColorDescriptor: String
let birthYear: StarWarsUniverse.Year?
let gender: StarWarsUniverse.Gender?
let homeworld: StarWarsUniverse.Planet?
let films: [SWFilm]?
let species: [StarWarsUniverse.Species]?
let vehicles: [StarWarsUniverse.Vehicle]?
let starships: [StarWarsUniverse.Starship]?
let imageURL: URL?
}
22 — Sommer Panage • @sommer • Swift Summit 2016
23 — Sommer Panage • @sommer • Swift Summit 2016
Let's model the data how we want it, not how it is
→ 1 View Controller
→ 1 Query
→ 1 Data Model
24 — Sommer Panage • @sommer • Swift Summit 2016
25 — Sommer Panage • @sommer • Swift Summit 2016
query AllCharacters {
allPeople {
edges {
node {
id
name
homeworld {
name
}
}
}
}
}
26 — Sommer Panage • @sommer • Swift Summit 2016
struct AllCharactersData {
let people: [Person]
struct Person {
let id: String
let name: String
let homeworld: Homeworld
struct Homeworld {
let name: String
}
}
}
27 — Sommer Panage • @sommer • Swift Summit 2016
query Character($id: ID) {
person (id: $id) {
id
height,
mass,
hairColor,
eyeColor,
imageURL,
starshipConnection {
edges {
node {
name
}
}
}
}
}
28 — Sommer Panage • @sommer • Swift Summit 2016
struct CharacterData {
struct Person {
let id: String
let height: Int
let mass: Int
let hairColor: String
let eyeColor: String
let imageURL: URL?
let starshipConnect: [Starship]
struct Starship {
let name: String
}
}
}
29 — Sommer Panage • @sommer • Swift Summit 2016
By having query / view-
data based models, we
no longer need optionals
everywhere!
30 — Sommer Panage • @sommer • Swift Summit 2016
But our parsing code still looks like...
let parsedData = try JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
self.id = parsedData["id"] as! String
self.name = parsedData["name"] as! String
self.homeworld = Homeworld(dict: parsedData["homeworld"] as! [String : Any])
Ugh!
31 — Sommer Panage • @sommer • Swift Summit 2016
Code Gen!
32 — Sommer Panage • @sommer • Swift Summit 2016
33 — Sommer Panage • @sommer • Swift Summit 2016
Apollo's iOS GraphQL Client for Swift
→ Compile-time safety
→ Inline validation errors for GraphQL
→ No wasted cycles on query-gen
34 — Sommer Panage • @sommer • Swift Summit 2016
What about...?
35 — Sommer Panage • @sommer • Swift Summit 2016
Versioning
→ "Version-Free"
→ Easy to test for backwards compatability
36 — Sommer Panage • @sommer • Swift Summit 2016
Caching
37 — Sommer Panage • @sommer • Swift Summit 2016
Simple Caching
Query-based, not object based
38 — Sommer Panage • @sommer • Swift Summit 2016
Advanced Caching
Flattened results, id-record mapped
39 — Sommer Panage • @sommer • Swift Summit 2016
Aren't you supposed to
use GraphQL with React
Native?
40 — Sommer Panage • @sommer • Swift Summit 2016
GraphQL + Swift
-> GraphQL shines on mobile
-> Swift gives us types and compile time safety
-> React Native is new and exciting, but that comes
with challenges too
41 — Sommer Panage • @sommer • Swift Summit 2016
In
Conclusion...
42 — Sommer Panage • @sommer • Swift Summit 2016
Pros to GraphQL + Swift
→ Fewer requests for data
→ Getting the exact data you need
→ Code gen -> no janky parsing code
→ Models that are reflective of your views
→ A strongly typed backend schema
43 — Sommer Panage • @sommer • Swift Summit 2016
Cons to GraphQL + Swift
→ Not a lot of tooling yet
→ Best practices still emerging
→ Poorly defined schema becomes a big client
problem
→ Not as good for endpoints requiring heavy logic
44 — Sommer Panage • @sommer • Swift Summit 2016
tldr: It's worth
it!
45 — Sommer Panage • @sommer • Swift Summit 2016
Check out
→ Apollo iOS Client: Documentation and info on
Apollo's awesome Swift iOS GraphQL client
→ 5 benefits of static GraphQL Queries: Blog post
from Apollo
→ Bringing GraphQL to iOS: Blog post from Apollo
→ GraphQL First: A better way to build modern apps:
Blog post from Apollo
46 — Sommer Panage • @sommer • Swift Summit 2016
Check out...con’t
→ GraphQL for mobile: Blog post from Artsy
→ Relay: Thinking in GraphQL: Post from FB on
Caching
47 — Sommer Panage • @sommer • Swift Summit 2016
Thank you!!
Come ask me questions about GraphQL + Swift
or about Accessibility!
48 — Sommer Panage • @sommer • Swift Summit 2016
Credits
Images
GediminasTurbo Baltaduonis, Danil Polshin, Apple,
Facebook, Apollo, Lucasfilm
General
Chorus Fitness, Apollo, Facebook
49 — Sommer Panage • @sommer • Swift Summit 2016

Swift + GraphQL

  • 1.
    Interfacing with GraphQLin Swift Sommer Panage • @sommer 1 — Sommer Panage • @sommer • Swift Summit 2016
  • 2.
    Hello! 2 — SommerPanage • @sommer • Swift Summit 2016
  • 3.
    What is GraphQL? 3— Sommer Panage • @sommer • Swift Summit 2016
  • 4.
    The GraphQL Schema typeUser { name: String! id: Int! email: String twitter: String } 4 — Sommer Panage • @sommer • Swift Summit 2016
  • 5.
    Instead of hittinga REST endpoint like... https://mybackend.com/api/user?id=1 5 — Sommer Panage • @sommer • Swift Summit 2016
  • 6.
    I write aquery like... { user(id: 1) { name email twitter } } And send it to my GraphQL endpoint https://mybackend.com/graphql 6 — Sommer Panage • @sommer • Swift Summit 2016
  • 7.
    And I getback a JSON response like... { "data": { "name": "Sommer Panage", "email": "sommer@panage.org", "twitter": "@sommer" } } 7 — Sommer Panage • @sommer • Swift Summit 2016
  • 8.
    Why is GraphQLsuch a big deal for mobile? 8 — Sommer Panage • @sommer • Swift Summit 2016
  • 9.
    Let's write anapp 9 — Sommer Panage • @sommer • Swift Summit 2016
  • 10.
    In a RESTworld, we'd hit this endpoint: http://swapi.co/api/people/1/ And we'd get back... 10 — Sommer Panage • @sommer • Swift Summit 2016
  • 11.
    { "name": "Luke Skywalker", "height":"1.72 m", "mass": "77 Kg", "hair_color": "Blond", "skin_color": "Caucasian", "eye_color": "Blue", "birth_year": "19 BBY", "gender": "Male", "homeworld": "http://swapi.co/api/planets/1/", "films": [ "http://swapi.co/api/films/1/", "http://swapi.co/api/films/2/", "http://swapi.co/api/films/3/" ], "species": [ "http://swapi.co/api/species/1/" ], "vehicles": [ "http://swapi.co/api/vehicles/14/", "http://swapi.co/api/vehicles/30/" ], "starships": [ "http://swapi.co/api/starships/12/", "http://swapi.co/api/starships/22/" ], "created": "2014-12-09T13:50:51.644000Z", "edited": "2014-12-10T13:52:43.172000Z", "url": "http://swapi.co/api/people/1/", "image_url": "http://static.srcdn.com/wp-content/uploads/luke-skywalker-star-wars-a-new-hope.jpg" } 11 — Sommer Panage • @sommer • Swift Summit 2016
  • 12.
    And then...we'd maketwo more calls to... http://swapi.co/api/starships/12/ and http://swapi.co/api/starships/22/ 12 — Sommer Panage • @sommer • Swift Summit 2016
  • 13.
    { "name": "X-wing", "model": "T-65X-wing", "manufacturer": "Incom Corporation", "cost_in_credits": "149999", "length": "12.5", "max_atmosphering_speed": "1050", "crew": "1", "passengers": "0", "cargo_capacity": "110", "consumables": "1 week", "hyperdrive_rating": "1.0", "MGLT": "100", "starship_class": "Starfighter", "pilots": [ "http://swapi.co/api/people/1/", "http://swapi.co/api/people/9/", "http://swapi.co/api/people/18/", "http://swapi.co/api/people/19/" ], "films": [ "http://swapi.co/api/films/3/", "http://swapi.co/api/films/2/", "http://swapi.co/api/films/1/" ], "created": "2014-12-12T11:19:05.340000Z", "edited": "2014-12-22T17:35:44.491233Z", "url": "http://swapi.co/api/starships/12/" } 13 — Sommer Panage • @sommer • Swift Summit 2016
  • 14.
    { "name": "Imperial shuttle", "model":"Lambda-class T-4a shuttle", "manufacturer": "Sienar Fleet Systems", "cost_in_credits": "240000", "length": "20", "max_atmosphering_speed": "850", "crew": "6", "passengers": "20", "cargo_capacity": "80000", "consumables": "2 months", "hyperdrive_rating": "1.0", "MGLT": "50", "starship_class": "Armed government transport", "pilots": [ "http://swapi.co/api/people/1/", "http://swapi.co/api/people/13/", "http://swapi.co/api/people/14/" ], "films": [ "http://swapi.co/api/films/3/", "http://swapi.co/api/films/2/" ], "created": "2014-12-15T13:04:47.235000Z", "edited": "2014-12-22T17:35:44.795405Z", "url": "http://swapi.co/api/starships/22/" } 14 — Sommer Panage • @sommer • Swift Summit 2016
  • 15.
    That's 3 callsand a whole lot of data for 1 little VC 15 — Sommer Panage • @sommer • Swift Summit 2016
  • 16.
    Now in GraphQL,instead we'd write the following query: { person(personID: 1) { name, height, mass, hairColor, eyeColor, imageURL, starshipConnection { edges { node { name } } } } } 16 — Sommer Panage • @sommer • Swift Summit 2016
  • 17.
    And, I'd getback exactly what I wanted! { "data": { "person": { "name": "Luke Skywalker", "height": 172, "mass": 77, "hairColor": "blond", "eyeColor": "blue", "imageURL": "http://static.srcdn.com/wp-content/uploads/luke-skywalker-star-wars-a-new-hope.jpg", "starshipConnection": { "edges": [ { "node": { "name": "X-wing" } }, { "node": { "name": "Imperial shuttle" } } ] } } } } 17 — Sommer Panage • @sommer • Swift Summit 2016
  • 18.
    So, why isGraphQL such a big deal for mobile? 1. Ask and ye shall receive! 2. Fewer round trips! 3. Less client logic around data! 18 — Sommer Panage • @sommer • Swift Summit 2016
  • 19.
    Wait just aminute... 19 — Sommer Panage • @sommer • Swift Summit 2016
  • 20.
    Our Star WarsCharacter model struct SWCharacter { let name: String let heightInCm: Int let massInKg: Int let hairColorDescriptor: String let skinColorDescriptor: String let eyeColorDescriptor: String let birthYear: StarWarsUniverse.Year let gender: StarWarsUniverse.Gender let homeworld: StarWarsUniverse.Planet let films: [SWFilm] let species: [StarWarsUniverse.Species] let vehicles: [StarWarsUniverse.Vehicle] let starships: [StarWarsUniverse.Starship] let imageURL: URL? } 20 — Sommer Panage • @sommer • Swift Summit 2016
  • 21.
    Our Star WarsCharacter model struct SWCharacter { let name: String? let heightInCm: Int? let massInKg: Int? let hairColorDescriptor: String? let skinColorDescriptor: String? let eyeColorDescriptor: String let birthYear: StarWarsUniverse.Year? let gender: StarWarsUniverse.Gender? let homeworld: StarWarsUniverse.Planet? let films: [SWFilm]? let species: [StarWarsUniverse.Species]? let vehicles: [StarWarsUniverse.Vehicle]? let starships: [StarWarsUniverse.Starship]? let imageURL: URL? } 21 — Sommer Panage • @sommer • Swift Summit 2016
  • 22.
    Swift, No!!!! struct SWCharacter{ let name: String? let heightInCm: Int? let massInKg: Int? let hairColorDescriptor: String? let skinColorDescriptor: String? let eyeColorDescriptor: String let birthYear: StarWarsUniverse.Year? let gender: StarWarsUniverse.Gender? let homeworld: StarWarsUniverse.Planet? let films: [SWFilm]? let species: [StarWarsUniverse.Species]? let vehicles: [StarWarsUniverse.Vehicle]? let starships: [StarWarsUniverse.Starship]? let imageURL: URL? } 22 — Sommer Panage • @sommer • Swift Summit 2016
  • 23.
    23 — SommerPanage • @sommer • Swift Summit 2016
  • 24.
    Let's model thedata how we want it, not how it is → 1 View Controller → 1 Query → 1 Data Model 24 — Sommer Panage • @sommer • Swift Summit 2016
  • 25.
    25 — SommerPanage • @sommer • Swift Summit 2016
  • 26.
    query AllCharacters { allPeople{ edges { node { id name homeworld { name } } } } } 26 — Sommer Panage • @sommer • Swift Summit 2016
  • 27.
    struct AllCharactersData { letpeople: [Person] struct Person { let id: String let name: String let homeworld: Homeworld struct Homeworld { let name: String } } } 27 — Sommer Panage • @sommer • Swift Summit 2016
  • 28.
    query Character($id: ID){ person (id: $id) { id height, mass, hairColor, eyeColor, imageURL, starshipConnection { edges { node { name } } } } } 28 — Sommer Panage • @sommer • Swift Summit 2016
  • 29.
    struct CharacterData { structPerson { let id: String let height: Int let mass: Int let hairColor: String let eyeColor: String let imageURL: URL? let starshipConnect: [Starship] struct Starship { let name: String } } } 29 — Sommer Panage • @sommer • Swift Summit 2016
  • 30.
    By having query/ view- data based models, we no longer need optionals everywhere! 30 — Sommer Panage • @sommer • Swift Summit 2016
  • 31.
    But our parsingcode still looks like... let parsedData = try JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] self.id = parsedData["id"] as! String self.name = parsedData["name"] as! String self.homeworld = Homeworld(dict: parsedData["homeworld"] as! [String : Any]) Ugh! 31 — Sommer Panage • @sommer • Swift Summit 2016
  • 32.
    Code Gen! 32 —Sommer Panage • @sommer • Swift Summit 2016
  • 33.
    33 — SommerPanage • @sommer • Swift Summit 2016
  • 34.
    Apollo's iOS GraphQLClient for Swift → Compile-time safety → Inline validation errors for GraphQL → No wasted cycles on query-gen 34 — Sommer Panage • @sommer • Swift Summit 2016
  • 35.
    What about...? 35 —Sommer Panage • @sommer • Swift Summit 2016
  • 36.
    Versioning → "Version-Free" → Easyto test for backwards compatability 36 — Sommer Panage • @sommer • Swift Summit 2016
  • 37.
    Caching 37 — SommerPanage • @sommer • Swift Summit 2016
  • 38.
    Simple Caching Query-based, notobject based 38 — Sommer Panage • @sommer • Swift Summit 2016
  • 39.
    Advanced Caching Flattened results,id-record mapped 39 — Sommer Panage • @sommer • Swift Summit 2016
  • 40.
    Aren't you supposedto use GraphQL with React Native? 40 — Sommer Panage • @sommer • Swift Summit 2016
  • 41.
    GraphQL + Swift ->GraphQL shines on mobile -> Swift gives us types and compile time safety -> React Native is new and exciting, but that comes with challenges too 41 — Sommer Panage • @sommer • Swift Summit 2016
  • 42.
    In Conclusion... 42 — SommerPanage • @sommer • Swift Summit 2016
  • 43.
    Pros to GraphQL+ Swift → Fewer requests for data → Getting the exact data you need → Code gen -> no janky parsing code → Models that are reflective of your views → A strongly typed backend schema 43 — Sommer Panage • @sommer • Swift Summit 2016
  • 44.
    Cons to GraphQL+ Swift → Not a lot of tooling yet → Best practices still emerging → Poorly defined schema becomes a big client problem → Not as good for endpoints requiring heavy logic 44 — Sommer Panage • @sommer • Swift Summit 2016
  • 45.
    tldr: It's worth it! 45— Sommer Panage • @sommer • Swift Summit 2016
  • 46.
    Check out → ApolloiOS Client: Documentation and info on Apollo's awesome Swift iOS GraphQL client → 5 benefits of static GraphQL Queries: Blog post from Apollo → Bringing GraphQL to iOS: Blog post from Apollo → GraphQL First: A better way to build modern apps: Blog post from Apollo 46 — Sommer Panage • @sommer • Swift Summit 2016
  • 47.
    Check out...con’t → GraphQLfor mobile: Blog post from Artsy → Relay: Thinking in GraphQL: Post from FB on Caching 47 — Sommer Panage • @sommer • Swift Summit 2016
  • 48.
    Thank you!! Come askme questions about GraphQL + Swift or about Accessibility! 48 — Sommer Panage • @sommer • Swift Summit 2016
  • 49.
    Credits Images GediminasTurbo Baltaduonis, DanilPolshin, Apple, Facebook, Apollo, Lucasfilm General Chorus Fitness, Apollo, Facebook 49 — Sommer Panage • @sommer • Swift Summit 2016