GraphQL + iOS

GraphQL + iOS

Talk at 12° CocoaHeads Fortaleza

F380535da59d6cdd5754e2e31bda8a0e?s=128

Bruno Macabeus

November 23, 2017
Tweet

Transcript

  1. GraphQL + iOS @macabeus #12cocoaHeadsFortaleza

  2. Who am I?

  3. My name is Bruno Macabeus I’m student at IFCE &

    Apple Developer Academy github.com/macabeus
  4. Problem!!

  5. You are making an app to many devices…

  6. You are making an app to many devices… … and

    each device have a very different UI!
  7. You are making an app to many devices… … and

    each device have a very different UI!
  8. Title Short description Title Short description Title Title Title Title

    /posts title description userId /users/:id name age imageUrl Overfetch and Underfetch Title Title Title
  9. /posts?include=title,userId title description userId /users/:id?include=imageUrl name age imageUrl Overfetch and

    Underfetch Title Short description Title Short description Title Title Title Title Title Title Title
  10. /posts?include=title,user.imageUrl title description userId user.imageUrl Overfetch and Underfetch Title Short

    description Title Short description Title Title Title Title Title Title Title
  11. /posts?include=title,description,user.avatar,data &filter=notRead&sort=newest&description=long title description userId user.avatar data Validate of parameters

    Title Short description Title Short description Title Title Title Title Title Title Title
  12. /posts?include=title,description,user.avatar,data &filter=notRead&sort=newest&description=long title description (now is deprecated!) shortDescription userId user.avatar

    data Notifications of deprecated Title Short description Title Short description Title Title Title Title Title Title Title
  13. Can GraphQL help me?

  14. Yes! Because with GraphQL you can be more expressive in

    your query! query GetPosts() { posts { title description user { imageUrl } } }
  15. Okay… But, is someone using GraphQL already?

  16. None
  17. How does GraphQL work?

  18. None
  19. query GetPosts() { posts { title description user { imageUrl

    } } } It’s a sample of GraphQL language
  20. { "data": { "posts": [ { "user": { "name": "Bruno

    Macabeus” }, "title": “My amazing post!”, "body": “Loren ipsun" }, { "user": { "name": "Ryan Swapp” }, "title": “My blog post", "body": “Foo bar baz” } ] } } Server can respond with JSON or other format
  21. Query Mutation Subscription query FetchPosts() { posts { id title

    body user { name } } } Query is an operation to get data from the server. For example: a list of posts. Operations
  22. Query Mutation Subscription mutation CreatePost($title: String!, $body: String!) { createPost(title:

    $title, body: $body) { id } } Mutation is an operation to perform side-effects on the underlying data system. For example: create a new post, or update one. Operations
  23. Query Mutation Subscription subscription NewComment($postId: Int!) { newComment(roomId: $postId) {

    sender text } } Subscription is an operation to start a new event stream. For example: check for new comments in a post in realtime. Operations
  24. query FetchPokemonBattle() { pokemonBattle { id pokemonLeft { id name

    level hp }, pokemonRight { id name level hp } } } Fragments
  25. query FetchPokemonBattle() { pokemonBattle() { id pokemonLeft { ...PokemonDetail },

    pokemonRight { ...PokemonDetail } } } fragment PokemonDetail on Pokemon { id name level hp } Fragments allow the reuse of common repeated selections of fields. Fragments query FetchPokemonBattle() { pokemonBattle { id pokemonLeft { id name level hp }, pokemonRight { id name level hp } } }
  26. How to use GraphQL in server? (briefly)

  27. I wrote a server using the Elixir language, a functional

    language that run in the BEAM virtual machine, the same virtual machine of Erlang. I chose this language because it’s great for applications that need a lot parallelism, such as a server. And, to create the server-side code for GraphQL, I used the Absinthe package.
  28. I wrote a server using the Elixir language, a functional

    language that run in virtual machine BEAM, the same virtual machine of Erlang. I chose this language because is great for applications that need a lot parallelism, such a server. And, to create the server-side code about GraphQL, I used using the package Absinthe. github.com/macabeus/graphql-example
  29. How to use GraphQL in iOS?

  30. We could use the Apollo GraphQL tool for iOS Apollo

    is a community building flexible open source tools for GraphQL.
  31. We could use the Apollo GraphQL tool for iOS Apollo

    is a community building flexible open source tools for GraphQL. Great features for iOS package: Strongly-typed & caching
  32. Add the Apollo package Configuration 1/5

  33. Download the scheme.json Configuration 2/5

  34. Add a new build phase Configuration 3/5

  35. And add the new files schema.json and API.swift on the

    project Configuration 4/5
  36. Initialize the ApolloClient somewhere, for example, AppDelegate.swift Configuration 5/5 import

    Apollo let graphlQLEndpointURL = "http://localhost:4000/api" let apollo = ApolloClient(url: URL(string: graphlQLEndpointURL)!)
  37. Coding Create a new .graphql file, that Apollo will compile

    to Swift
  38. Coding class TableViewControllerPosts: UITableViewController { override func viewDidAppear(_ animated: Bool)

    { fetchPosts() } func fetchPosts() { let query = GetPostsQuery(userId: 1) apollo.fetch(query: query) { result, error in // Check for errors if let error = error { print(error) return } guard let posts = result?.data?.posts else { print("Without data!") return } // Parse self.tableDataSource = posts.flatMap { post -> Post? in return Post( id: post?.id, name: post?.title, body: post?.body, countLikes: post?.countLikes, liked: post?.liked, author: User( name: post?.user?.name ) ) } } }
  39. class TableViewControllerPosts: UITableViewController { override func viewDidAppear(_ animated: Bool) {

    fetchPosts() } func fetchPosts() { let query = GetPostsQuery(userId: 1) apollo.fetch(query: query) { result, error in // Check for errors if let error = error { print(error) return } guard let posts = result?.data?.posts else { print("Without data!") return } // Parse self.tableDataSource = posts.flatMap { post -> Post? i return Post( id: post?.id, name: post?.title, body: post?.body, countLikes: post?.countLikes, liked: post?.liked, author: User( name: post?.user?.name ) ) } } query GetPosts($userId: Int) { posts(userId: $userId) { id title body countLikes liked user { name } } }
  40. class TableViewControllerPosts: UITableViewController { override func viewDidAppear(_ animated: Bool) {

    fetchPosts() } func fetchPosts() { let query = GetPostsQuery(userId: 1) apollo.fetch(query: query) { result, error in // Check for errors if let error = error { print(error) return } guard let posts = result?.data?.posts else { print("Without data!") return } // Parse self.tableDataSource = posts.flatMap { post -> Post? i return Post( id: post?.id, name: post?.title, body: post?.body, countLikes: post?.countLikes, liked: post?.liked, author: User( name: post?.user?.name ) ) } } query GetPosts($userId: Int) { posts(userId: $userId) { id title body countLikes liked user { name } } }
  41. class TableViewControllerPosts: UITableViewController { override func viewDidAppear(_ animated: Bool) {

    fetchPosts() } func fetchPosts() { let query = GetPostsQuery(userId: 1) apollo.fetch(query: query) { result, error in // Check for errors if let error = error { print(error) return } guard let posts = result?.data?.posts else { print("Without data!") return } // Parse self.tableDataSource = posts.flatMap { post -> Post? i return Post( id: post?.id, name: post?.title, body: post?.body, countLikes: post?.countLikes, liked: post?.liked, author: User( name: post?.user?.name ) ) } } query GetPosts($userId: Int) { posts(userId: $userId) { id title body countLikes liked user { name } } }
  42. class TableViewControllerPosts: UITableViewController { override func viewDidAppear(_ animated: Bool) {

    fetchPosts() } func fetchPosts() { let query = GetPostsQuery(userId: 1) apollo.fetch(query: query) { result, error in // Check for errors if let error = error { print(error) return } guard let posts = result?.data?.posts else { print("Without data!") return } // Parse self.tableDataSource = posts.flatMap { post -> Post? i return Post( id: post?.id, name: post?.title, body: post?.body, countLikes: post?.countLikes, liked: post?.liked, author: User( name: post?.user?.name ) ) } } query GetPosts($userId: Int) { posts(userId: $userId) { id title body countLikes liked user { name } } }
  43. class TableViewControllerPosts: UITableViewController { override func viewDidAppear(_ animated: Bool) {

    fetchPosts() } func fetchPosts() { let query = GetPostsQuery(userId: 1) apollo.fetch(query: query) { result, error in // Check for errors if let error = error { print(error) return } guard let posts = result?.data?.posts else { print("Without data!") return } // Parse self.tableDataSource = posts.flatMap { post -> Post? i return Post( id: post?.id, name: post?.title, body: post?.body, countLikes: post?.countLikes, liked: post?.liked, author: User( name: post?.user?.name ) ) } } query GetPosts($userId: Int) { posts(userId: $userId) { id title body countLikes liked user { name } } } “fetch" if is query “perform" if is mutation
  44. class TableViewControllerPosts: UITableViewController { override func viewDidAppear(_ animated: Bool) {

    fetchPosts() } func fetchPosts() { let query = GetPostsQuery(userId: 1) apollo.fetch(query: query) { result, error in // Check for errors if let error = error { print(error) return } guard let posts = result?.data?.posts else { print("Without data!") return } // Parse self.tableDataSource = posts.flatMap { post -> Post? i return Post( id: post?.id, name: post?.title, body: post?.body, countLikes: post?.countLikes, liked: post?.liked, author: User( name: post?.user?.name ) ) } } query GetPosts($userId: Int) { posts(userId: $userId) { id title body countLikes liked user { name } } }
  45. class TableViewControllerPosts: UITableViewController { override func viewDidAppear(_ animated: Bool) {

    fetchPosts() } func fetchPosts() { let query = GetPostsQuery(userId: 1) apollo.fetch(query: query) { result, error in // Check for errors if let error = error { print(error) return } guard let posts = result?.data?.posts else { print("Without data!") return } // Parse self.tableDataSource = posts.flatMap { post -> Post? i return Post( id: post?.id, name: post?.title, body: post?.body, countLikes: post?.countLikes, liked: post?.liked, author: User( name: post?.user?.name ) ) } } query GetPosts($userId: Int) { posts(userId: $userId) { id title body countLikes liked user { name } } }
  46. Hey… wait!!!

  47. Now we have a problem, called Massive View Controller!

  48. Network layer & unit tests

  49. github.com/macabeus/graphql-example

  50. GraphQL isn't a silver bullet!

  51. GraphQL is designed to build client applications. Depending on the

    project needs, maybe is better use Rest instead of GraphQL. Or, you could use both, Rest and GraphQL, in same project. One situation that GraphQL is good is when you need a highly query-able API.
  52. and beyond…

  53. { page(url:"http://news.ycombinator.com") { items: query(selector:"tr.athing") { rank: text(selector:"td span.rank") title:

    text(selector:"td.title a") sitebit: text(selector:"span.comhead a") url: attr(selector:"td.title a", name:"href") attrs: next { score: text(selector:"span.score") user: text(selector:"a:eq(0)") comments: text(selector:"a:eq(2)") } } } } GDOM GraphQL-like syntax to create webcrawlers
  54. Distributed graph database

  55. { brCharacters(func: eq(name@en, "Blade Runner")) { name@en initial_release_date starring {

    performance.actor { name@en # actor name } performance.character { name@en # character name } } } } Distributed graph database , that has a query language inspired by GraphQL, called GraphQL+-
  56. To learn more

  57. https://www.howtographql.com

  58. https://ryanswapp.com/2016/11/29/phoenix-graphql- tutorial-with-absinthe/

  59. https://github.com/macabeus/graphql-example

  60. http://macalogs.com.br