SlideShare a Scribd company logo
Networked APIs with Swift
Tim Burks, Google
Use Swift to connect to powerful
cloud services and even create
some of your own.
We'll use a Google Cloud Shell and
a Google API in our examples.
Sign up here:
https://cloud.google.com/free
What is a Networked API?
From the Google Cloud APIs Design Guide:
“Application Programming Interfaces that operate across a network of
computers. They communicate using network protocols including HTTP,
and are frequently produced by different organizations than the ones that
consume them.”
API Styles
1. Remote Procedure Call (RPC)
2. Representational State Transfer (REST)
What is a REST API?
From the Richardson Maturity Model (as described by Martin Fowler):
Level 3 Hypermedia Controls
Level 2 HTTP Verbs
Level 1 Resources
Level 0 HTTP
Hypermedia Controls???
HATEOAS (Hypertext As The Engine Of Application State)
<appointment>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
<link rel = "/linkrels/appointment/cancel" uri = "/slots/1234/appointment"/>
<link rel = "/linkrels/appointment/addTest" uri = "/slots/1234/appointment/tests"/>
<link rel = "self" uri = "/slots/1234/appointment"/>
<link rel = "/linkrels/appointment/changeTime" uri = "/doctors/mjones/slots?date=20100104@status=open"/>
<link rel = "/linkrels/appointment/updateContactInfo" uri = "/patients/jsmith/contactInfo"/>
<link rel = "/linkrels/help" uri = "/help/appointment"/>
</appointment>
(source: Martin Fowler)
REST
Requirements
- Roy Fielding
● A REST API should not be dependent on any single
communication protocol.
● A REST API should not contain any changes to the
communication protocols aside from filling-out or fixing the
details of underspecified bits of standard protocols.
● A REST API should spend almost all of its descriptive effort
in defining the media type(s) used for representing
resources and driving application state, or in defining
extended relation names and/or hypertext-enabled markup
for existing standard media types.
● A REST API must not define fixed resource names or
hierarchies (an obvious coupling of client and server).
● A REST API should never have “typed” resources that are
significant to the client.
● A REST API should be entered with no prior knowledge
beyond the initial URI (bookmark) and set of standardized
media types that are appropriate for the intended audience
(i.e., expected to be understood by any client that might
use the API).
1. HTTP/HTTPS
a. Paths describe resources (nouns)
b. HTTP verbs describe actions
2. JSON Payloads
REST in
Practice
The OpenAPI Specification
The OpenAPI Specification (OAS) defines a
standard, programming language-agnostic
interface description for REST APIs, which allows
both humans and computers to discover and
understand the capabilities of a service without
requiring access to source code, additional
documentation, or inspection of network traffic.
Find OpenAPI descriptions on the web
Generate API support code from OpenAPI.
Make HTTP requests with Swift
1. Prepare a URL.
2. Prepare a request.
3. Perform the request.
1. Prepare a URL
// build URLs with the URLComponents class
var urlComponents = URLComponents(string:urlString)!
// if necessary, add query parameters
var queryItems : [URLQueryItem] = []
for (key, value) in parameters { // [String:String] of parameters
queryItems.append(URLQueryItem(name: key, value: value))
}
urlComponents.queryItems = queryItems
// build the URL
let url = urlComponents.url!
2. Prepare a request
// represent the request with the URLRequest class
var request = URLRequest(url:url)
// add any needed request headers
request.setValue(authorization, forHTTPHeaderField:"Authorization")
// if necessary, set the request method (“GET” is the default)
request.httpMethod = method
3. Perform the request
// use the URLSession class to manage request activity
let session = URLSession(configuration: URLSessionConfiguration.default)
// a URLSessionDataTask handles a specific request
let task: URLSessionDataTask = session.dataTask(with:request)
{(data, response, error) -> Void in
// This block gets called when the request completes.
// NOTE: It will run on a separate thread.
callback(data, response, error)
}
// do something else...
4. Make synchronous calls with URLSessionDataTask and DispatchSemaphore
// Usually we want to handle network calls asynchronously.
// But sometimes (e.g. command-line tools) we want to wait for their results.
// DispatchSemaphore helps us safely communicate between threads
let sem = DispatchSemaphore(value: 0)
let task: URLSessionDataTask = session.dataTask(with:request)
{(data, response, error) -> Void in
callback(data, response, error)
// Use this to signal the end of the task
sem.signal()
}
// This blocks until signal() is called
_ = sem.wait(timeout: DispatchTime.distantFuture)
The Authorization Problem
How do we tell an API server that
it’s ok to respond to our requests?
Authorization
We need to provide a token:
var request = URLRequest(url:url)
request.httpMethod = method
request.httpBody = ...
// add any needed request headers
request.setValue(authorization, forHTTPHeaderField:"Authorization")
How do we get that?
OAuth2 Authorization Flow
For an implementation in Swift, see BrowserTokenProvider.swift
Client Service
Authorization URL
Sign-In Page (HTML)
Human Confirmation
Browser Redirect w/ Code
Token Request (Code)
Token
Web
Browser
Easier ways to get tokens (1 of 2)
If you’re running inside a VM on Google Cloud Platform, you can get a token
from the Google Cloud Metadata Service.
% curl http://metadata/computeMetadata/v1/instance/service-accounts/default/token
{"access_token":"ya29.GqUBUgXcBmIt7vfHsWJT4qVzdhWxwEb2f3tamcA6ykrIsEANZfQnoH0HDCBnlCztLw
cD47w7YENghIucNUIIypLId4C5dXc4H8D93e17MrSbGRe4ipfoQhxPCIhIU3KJsvFjel0HcN2iwf8xURv2z1lWiN
2jkZjzLiMRWPKfSvtBVzuWkIo5uZ5u25IXle3tJ4SICh0-516sU84DFu0wkPO-q1xGpiff","expires_in":179
9,"token_type":"Bearer"}
Then pass “Bearer “ + access_token as the Authorization header.
See GoogleCloudMetadataTokenProvider.swift.
Easier ways to get tokens (2 of 2)
If you’re calling a Google API from anywhere, you can use a Service Account.
1. Create and download the account credentials.
2. Create a JWT token and sign it with the account credentials.
3. POST the signed token to the Google Account Service and get a token!
See ServiceAccountTokenProvider.swift.
Aside: Build an HTTP server with swift-server/http
What about RPC?
It’s just a function call.
service Echo {
// Immediately returns an echo of a request.
rpc Get(EchoRequest) returns (EchoResponse) {}
}
Networked APIs with swift
Networked APIs with swift
Networked APIs with swift
Networked APIs with swift
Networked APIs with swift
Networked APIs with swift
10 billion+
API calls
every second
Protocol Buffers are a language-neutral, platform-neutral, extensible
mechanism for serializing structured data.
“Protocol Buffers” means several things
1. A serialization mechanism
2. An interface description language
3. A methodology
Protocol Buffer Serialization
It’s just a stream of bytes
[field_number<<3 + wire_type] [length if necessary] [data]...
$ hexdump /tmp/request.bin
0000000 0a 05 68 65 6c 6c 6f
0a is “0000 1010”, so
field_number = 1 and wire_type = 2
Protocol Buffers aren’t just for networking
class UserData {
static let sharedInstance = UserData()
public var user : Models_User
init() {
// read info from UserDefaults
if let userdata = UserDefaults.standard.data(forKey:"user") {
do {
user = try Models_User(serializedData:userdata)
} catch {
user = Models_User()
}
} else {
user = Models_User()
}
}
func save() {
DispatchQueue.main.async {
do {
try UserDefaults.standard.set(self.user.serializedData(), forKey:"user")
} catch (let error) {
print("SAVE ERROR (error)")
}
}
}
A Data Definition Language
syntax = "proto3";
package models;
message PlantList {
string id = 1;
string name = 2;
repeated PlantListItem plants = 11;
}
message PlantListItem {
string id = 1;
string botanical_name = 2;
}
message User {
string id = 1;
string name = 2;
repeated PlantList plantLists = 3;
}
Networked APIs with swift
Networked APIs with swift
A Methodology
% protoc models.proto --swift_out=.
#
# This runs a plugin called protoc-gen-swift
#
# The plugin generates a Swift source file that implements
# the data structures defined in models.proto and code
# for reading and writing them as serialized bytes.
#
Networked APIs with swift
Build a Swift client
and service with gRPC
echo.proto
package echo;
service Echo {
// Immediately returns an echo of a request.
rpc Get(EchoRequest) returns (EchoResponse) {}
// Splits a request into words and returns each word in a stream of messages.
rpc Expand(EchoRequest) returns (stream EchoResponse) {}
// Collects a stream of messages and returns them concatenated when the caller closes.
rpc Collect(stream EchoRequest) returns (EchoResponse) {}
// Streams back messages as they are received in an input stream.
rpc Update(stream EchoRequest) returns (stream EchoResponse) {}
}
message EchoRequest {
// The text of a message to be echoed.
string text = 1;
}
message EchoResponse {
// The text of an echo response.
string text = 1;
}
Let’s try it!
https://goo.gl/ux4Txh
https://github.com/grpc/grpc-swift/wiki/
Try-gRPC-Swift-in-a-Google-Compute-Engin
e-VM
EchoService.swift (1/3)
class EchoProvider : Echo_EchoProvider {
// get returns requests as they were received.
func get(request : Echo_EchoRequest, session : Echo_EchoGetSession) throws -> Echo_EchoResponse {
var response = Echo_EchoResponse()
response.text = "Swift echo get: " + request.text
return response
}
// expand splits a request into words and returns each word in a separate message.
func expand(request : Echo_EchoRequest, session : Echo_EchoExpandSession) throws -> Void {
let parts = request.text.components(separatedBy: " ")
var i = 0
for part in parts {
var response = Echo_EchoResponse()
response.text = "Swift echo expand ((i)): (part)"
try session.send(response)
i += 1
sleep(1)
}
}
EchoService.swift (2/3)
// collect collects a sequence of messages and returns them concatenated when the caller closes.
func collect(session : Echo_EchoCollectSession) throws -> Void {
var parts : [String] = []
while true {
do {
let request = try session.receive()
parts.append(request.text)
} catch Echo_EchoServerError.endOfStream {
break
} catch (let error) {
print("(error)")
}
}
var response = Echo_EchoResponse()
response.text = "Swift echo collect: " + parts.joined(separator: " ")
try session.sendAndClose(response)
}
EchoService.swift (3/3)
// update streams back messages as they are received in an input stream.
func update(session : Echo_EchoUpdateSession) throws -> Void {
var count = 0
while true {
do {
let request = try session.receive()
count += 1
var response = Echo_EchoResponse()
response.text = "Swift echo update ((count)): (request.text)"
try session.send(response)
} catch Echo_EchoServerError.endOfStream {
break
} catch (let error) {
print("(error)")
}
}
try session.close()
}
}
main.swift (1/3)
// Unary
if client == "get" {
var requestMessage = Echo_EchoRequest()
requestMessage.text = message
print("Sending: " + requestMessage.text)
let responseMessage = try service.get(requestMessage)
print("get received: " + responseMessage.text)
}
// Server streaming
if client == "expand" {
var requestMessage = Echo_EchoRequest()
requestMessage.text = message
print("Sending: " + requestMessage.text)
let expandCall = try service.expand(requestMessage) {result in }
var running = true
while running {
do {
let responseMessage = try expandCall.receive()
print("Received: (responseMessage.text)")
} catch Echo_EchoClientError.endOfStream {
print("expand closed")
running = false
}
}
}
main.swift (2/3)
// Client streaming
if client == "collect" {
let collectCall = try service.collect() {result in }
let parts = message.components(separatedBy:" ")
for part in parts {
var requestMessage = Echo_EchoRequest()
requestMessage.text = part
print("Sending: " + part)
try collectCall.send(requestMessage) {error in print(error)}
sleep(1)
}
let responseMessage = try collectCall.closeAndReceive()
print("Received: (responseMessage.text)")
}
main.swift (3/3)
// Bidirectional streaming
if client == "update" {
let updateCall = try service.update() {result in}
DispatchQueue.global().async {
var running = true
while running {
do {
let responseMessage = try updateCall.receive()
print("Received: (responseMessage.text)")
} catch Echo_EchoClientError.endOfStream {
print("update closed")
latch.signal()
break
} catch (let error) {
print("error: (error)")
}
}
}
...
...
let parts = message.components(separatedBy:" ")
for part in parts {
var requestMessage = Echo_EchoRequest()
requestMessage.text = part
print("Sending: " + requestMessage.text)
try updateCall.send(requestMessage) {error in print(error)}
sleep(1)
}
try updateCall.closeSend()
// Wait for the call to complete.
latch.wait()
}
}

More Related Content

What's hot (20)

PPTX
Microservices for building an IDE – The innards of JetBrains Rider - TechDays...
Maarten Balliauw
 
RTF
Hack language
Ravindra Bhadane
 
PPTX
Modern webservices using gRPC and Protocol Buffers in Golang
OmidHojabri1
 
PDF
PHP Reactive Programming at Medan Tech Day 2018
Dolly Aswin Harahap
 
PDF
How to reverse engineer Android applications
hubx
 
PDF
Improving DroidBox
Kelwin Yang
 
PDF
WEB PROGRAMMING UNIT VIII BY BHAVSINGH MALOTH
Bhavsingh Maloth
 
PDF
(COSCUP 2015) A Beginner's Journey to Mozilla SpiderMonkey JS Engine
ZongXian Shen
 
PPT
Mixing Python and Java
Andreas Schreiber
 
PPT
Unit 8 Java
arnold 7490
 
PPT
Smoothing Your Java with DSLs
intelliyole
 
PPTX
Python Streaming Pipelines with Beam on Flink
Aljoscha Krettek
 
PDF
Refactoring to Java 8 (Devoxx UK)
Trisha Gee
 
PPT
Java Networking
Ankit Desai
 
PDF
Eclipse pdt indigo release review
Giang Nguyễn
 
DOCX
Notes of java first unit
gowher172236
 
PDF
Flink Forward Berlin 2018: Thomas Weise & Aljoscha Krettek - "Python Streamin...
Flink Forward
 
PDF
1..Net Framework Architecture-(c#)
Shoaib Ghachi
 
PPTX
Net serialization
Greg Sohl
 
PPTX
Java Profiling
zeroproductionincidents
 
Microservices for building an IDE – The innards of JetBrains Rider - TechDays...
Maarten Balliauw
 
Hack language
Ravindra Bhadane
 
Modern webservices using gRPC and Protocol Buffers in Golang
OmidHojabri1
 
PHP Reactive Programming at Medan Tech Day 2018
Dolly Aswin Harahap
 
How to reverse engineer Android applications
hubx
 
Improving DroidBox
Kelwin Yang
 
WEB PROGRAMMING UNIT VIII BY BHAVSINGH MALOTH
Bhavsingh Maloth
 
(COSCUP 2015) A Beginner's Journey to Mozilla SpiderMonkey JS Engine
ZongXian Shen
 
Mixing Python and Java
Andreas Schreiber
 
Unit 8 Java
arnold 7490
 
Smoothing Your Java with DSLs
intelliyole
 
Python Streaming Pipelines with Beam on Flink
Aljoscha Krettek
 
Refactoring to Java 8 (Devoxx UK)
Trisha Gee
 
Java Networking
Ankit Desai
 
Eclipse pdt indigo release review
Giang Nguyễn
 
Notes of java first unit
gowher172236
 
Flink Forward Berlin 2018: Thomas Weise & Aljoscha Krettek - "Python Streamin...
Flink Forward
 
1..Net Framework Architecture-(c#)
Shoaib Ghachi
 
Net serialization
Greg Sohl
 
Java Profiling
zeroproductionincidents
 

Similar to Networked APIs with swift (20)

PDF
Taming Cloud APIs with Swift
Tim Burks
 
PPTX
CocoaConf: The Language of Mobile Software is APIs
Tim Burks
 
PPTX
Api 101
DrSimoneDiCola
 
PDF
apidays LIVE Helsinki - Implementing OpenAPI and GraphQL Services with gRPC b...
apidays
 
PDF
RESTful Web Applications with Google Go
Frank Müller
 
PDF
Protocol-Oriented Networking
Mostafa Amer
 
PDF
Facebook & Twitter API
Fabrice Delhoste
 
PDF
Middleware in Golang: InVision's Rye
Cale Hoopes
 
PPTX
Restful api
Anurag Srivastava
 
PPTX
Jones "Working with Scholarly APIs: A NISO Training Series, Session One: Foun...
National Information Standards Organization (NISO)
 
PPTX
What I learned about APIs in my first year at Google
Tim Burks
 
PDF
Building REST APIs using gRPC and Go
Alvaro Viebrantz
 
PDF
Api FUNdamentals #MHA2017
JoEllen Carter
 
PPTX
The new (is it really ) api stack
Red Hat
 
PDF
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2
 
PDF
Zyncro rest api feb 2013
Zyncro
 
PDF
Build Great Networked APIs with Swift, OpenAPI, and gRPC
Tim Burks
 
PDF
Building sustainable RESTFul services
Ortus Solutions, Corp
 
PDF
Api fundamentals
AgileDenver
 
Taming Cloud APIs with Swift
Tim Burks
 
CocoaConf: The Language of Mobile Software is APIs
Tim Burks
 
apidays LIVE Helsinki - Implementing OpenAPI and GraphQL Services with gRPC b...
apidays
 
RESTful Web Applications with Google Go
Frank Müller
 
Protocol-Oriented Networking
Mostafa Amer
 
Facebook & Twitter API
Fabrice Delhoste
 
Middleware in Golang: InVision's Rye
Cale Hoopes
 
Restful api
Anurag Srivastava
 
Jones "Working with Scholarly APIs: A NISO Training Series, Session One: Foun...
National Information Standards Organization (NISO)
 
What I learned about APIs in my first year at Google
Tim Burks
 
Building REST APIs using gRPC and Go
Alvaro Viebrantz
 
Api FUNdamentals #MHA2017
JoEllen Carter
 
The new (is it really ) api stack
Red Hat
 
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2
 
Zyncro rest api feb 2013
Zyncro
 
Build Great Networked APIs with Swift, OpenAPI, and gRPC
Tim Burks
 
Building sustainable RESTFul services
Ortus Solutions, Corp
 
Api fundamentals
AgileDenver
 
Ad

More from Tim Burks (6)

PDF
Governing APIs at Scale
Tim Burks
 
PDF
Build your next REST API with gRPC
Tim Burks
 
PDF
OpenAPI and gRPC Side by-Side
Tim Burks
 
PDF
Interpreting Objective C
Tim Burks
 
PDF
Deep Geek Diving into the iPhone OS and Frameworks
Tim Burks
 
PDF
Building Open Radar
Tim Burks
 
Governing APIs at Scale
Tim Burks
 
Build your next REST API with gRPC
Tim Burks
 
OpenAPI and gRPC Side by-Side
Tim Burks
 
Interpreting Objective C
Tim Burks
 
Deep Geek Diving into the iPhone OS and Frameworks
Tim Burks
 
Building Open Radar
Tim Burks
 
Ad

Recently uploaded (20)

PPTX
Home Care Tools: Benefits, features and more
Third Rock Techkno
 
PPTX
Milwaukee Marketo User Group - Summer Road Trip: Mapping and Personalizing Yo...
bbedford2
 
PDF
Download Canva Pro 2025 PC Crack Full Latest Version
bashirkhan333g
 
PDF
NEW-Viral>Wondershare Filmora 14.5.18.12900 Crack Free
sherryg1122g
 
PDF
TheFutureIsDynamic-BoxLang witch Luis Majano.pdf
Ortus Solutions, Corp
 
PDF
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
PDF
iTop VPN With Crack Lifetime Activation Key-CODE
utfefguu
 
PDF
SAP Firmaya İade ABAB Kodları - ABAB ile yazılmıl hazır kod örneği
Salih Küçük
 
PDF
MiniTool Partition Wizard Free Crack + Full Free Download 2025
bashirkhan333g
 
PDF
Generic or Specific? Making sensible software design decisions
Bert Jan Schrijver
 
PDF
Top Agile Project Management Tools for Teams in 2025
Orangescrum
 
PDF
Driver Easy Pro 6.1.1 Crack Licensce key 2025 FREE
utfefguu
 
PPTX
AEM User Group: India Chapter Kickoff Meeting
jennaf3
 
PDF
Add Background Images to Charts in IBM SPSS Statistics Version 31.pdf
Version 1 Analytics
 
PPTX
Agentic Automation: Build & Deploy Your First UiPath Agent
klpathrudu
 
PPTX
ChiSquare Procedure in IBM SPSS Statistics Version 31.pptx
Version 1 Analytics
 
PDF
Automate Cybersecurity Tasks with Python
VICTOR MAESTRE RAMIREZ
 
PDF
IDM Crack with Internet Download Manager 6.42 Build 43 with Patch Latest 2025
bashirkhan333g
 
PDF
How to Hire AI Developers_ Step-by-Step Guide in 2025.pdf
DianApps Technologies
 
PDF
IObit Driver Booster Pro 12.4.0.585 Crack Free Download
henryc1122g
 
Home Care Tools: Benefits, features and more
Third Rock Techkno
 
Milwaukee Marketo User Group - Summer Road Trip: Mapping and Personalizing Yo...
bbedford2
 
Download Canva Pro 2025 PC Crack Full Latest Version
bashirkhan333g
 
NEW-Viral>Wondershare Filmora 14.5.18.12900 Crack Free
sherryg1122g
 
TheFutureIsDynamic-BoxLang witch Luis Majano.pdf
Ortus Solutions, Corp
 
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
iTop VPN With Crack Lifetime Activation Key-CODE
utfefguu
 
SAP Firmaya İade ABAB Kodları - ABAB ile yazılmıl hazır kod örneği
Salih Küçük
 
MiniTool Partition Wizard Free Crack + Full Free Download 2025
bashirkhan333g
 
Generic or Specific? Making sensible software design decisions
Bert Jan Schrijver
 
Top Agile Project Management Tools for Teams in 2025
Orangescrum
 
Driver Easy Pro 6.1.1 Crack Licensce key 2025 FREE
utfefguu
 
AEM User Group: India Chapter Kickoff Meeting
jennaf3
 
Add Background Images to Charts in IBM SPSS Statistics Version 31.pdf
Version 1 Analytics
 
Agentic Automation: Build & Deploy Your First UiPath Agent
klpathrudu
 
ChiSquare Procedure in IBM SPSS Statistics Version 31.pptx
Version 1 Analytics
 
Automate Cybersecurity Tasks with Python
VICTOR MAESTRE RAMIREZ
 
IDM Crack with Internet Download Manager 6.42 Build 43 with Patch Latest 2025
bashirkhan333g
 
How to Hire AI Developers_ Step-by-Step Guide in 2025.pdf
DianApps Technologies
 
IObit Driver Booster Pro 12.4.0.585 Crack Free Download
henryc1122g
 

Networked APIs with swift

  • 1. Networked APIs with Swift Tim Burks, Google
  • 2. Use Swift to connect to powerful cloud services and even create some of your own.
  • 3. We'll use a Google Cloud Shell and a Google API in our examples. Sign up here: https://cloud.google.com/free
  • 4. What is a Networked API? From the Google Cloud APIs Design Guide: “Application Programming Interfaces that operate across a network of computers. They communicate using network protocols including HTTP, and are frequently produced by different organizations than the ones that consume them.”
  • 5. API Styles 1. Remote Procedure Call (RPC) 2. Representational State Transfer (REST)
  • 6. What is a REST API? From the Richardson Maturity Model (as described by Martin Fowler): Level 3 Hypermedia Controls Level 2 HTTP Verbs Level 1 Resources Level 0 HTTP
  • 7. Hypermedia Controls??? HATEOAS (Hypertext As The Engine Of Application State) <appointment> <slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/> <patient id = "jsmith"/> <link rel = "/linkrels/appointment/cancel" uri = "/slots/1234/appointment"/> <link rel = "/linkrels/appointment/addTest" uri = "/slots/1234/appointment/tests"/> <link rel = "self" uri = "/slots/1234/appointment"/> <link rel = "/linkrels/appointment/changeTime" uri = "/doctors/mjones/slots?date=20100104@status=open"/> <link rel = "/linkrels/appointment/updateContactInfo" uri = "/patients/jsmith/contactInfo"/> <link rel = "/linkrels/help" uri = "/help/appointment"/> </appointment> (source: Martin Fowler)
  • 8. REST Requirements - Roy Fielding ● A REST API should not be dependent on any single communication protocol. ● A REST API should not contain any changes to the communication protocols aside from filling-out or fixing the details of underspecified bits of standard protocols. ● A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled markup for existing standard media types. ● A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). ● A REST API should never have “typed” resources that are significant to the client. ● A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the API).
  • 9. 1. HTTP/HTTPS a. Paths describe resources (nouns) b. HTTP verbs describe actions 2. JSON Payloads REST in Practice
  • 10. The OpenAPI Specification The OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface description for REST APIs, which allows both humans and computers to discover and understand the capabilities of a service without requiring access to source code, additional documentation, or inspection of network traffic.
  • 12. Generate API support code from OpenAPI.
  • 13. Make HTTP requests with Swift 1. Prepare a URL. 2. Prepare a request. 3. Perform the request.
  • 14. 1. Prepare a URL // build URLs with the URLComponents class var urlComponents = URLComponents(string:urlString)! // if necessary, add query parameters var queryItems : [URLQueryItem] = [] for (key, value) in parameters { // [String:String] of parameters queryItems.append(URLQueryItem(name: key, value: value)) } urlComponents.queryItems = queryItems // build the URL let url = urlComponents.url!
  • 15. 2. Prepare a request // represent the request with the URLRequest class var request = URLRequest(url:url) // add any needed request headers request.setValue(authorization, forHTTPHeaderField:"Authorization") // if necessary, set the request method (“GET” is the default) request.httpMethod = method
  • 16. 3. Perform the request // use the URLSession class to manage request activity let session = URLSession(configuration: URLSessionConfiguration.default) // a URLSessionDataTask handles a specific request let task: URLSessionDataTask = session.dataTask(with:request) {(data, response, error) -> Void in // This block gets called when the request completes. // NOTE: It will run on a separate thread. callback(data, response, error) } // do something else...
  • 17. 4. Make synchronous calls with URLSessionDataTask and DispatchSemaphore // Usually we want to handle network calls asynchronously. // But sometimes (e.g. command-line tools) we want to wait for their results. // DispatchSemaphore helps us safely communicate between threads let sem = DispatchSemaphore(value: 0) let task: URLSessionDataTask = session.dataTask(with:request) {(data, response, error) -> Void in callback(data, response, error) // Use this to signal the end of the task sem.signal() } // This blocks until signal() is called _ = sem.wait(timeout: DispatchTime.distantFuture)
  • 18. The Authorization Problem How do we tell an API server that it’s ok to respond to our requests?
  • 19. Authorization We need to provide a token: var request = URLRequest(url:url) request.httpMethod = method request.httpBody = ... // add any needed request headers request.setValue(authorization, forHTTPHeaderField:"Authorization") How do we get that?
  • 20. OAuth2 Authorization Flow For an implementation in Swift, see BrowserTokenProvider.swift Client Service Authorization URL Sign-In Page (HTML) Human Confirmation Browser Redirect w/ Code Token Request (Code) Token Web Browser
  • 21. Easier ways to get tokens (1 of 2) If you’re running inside a VM on Google Cloud Platform, you can get a token from the Google Cloud Metadata Service. % curl http://metadata/computeMetadata/v1/instance/service-accounts/default/token {"access_token":"ya29.GqUBUgXcBmIt7vfHsWJT4qVzdhWxwEb2f3tamcA6ykrIsEANZfQnoH0HDCBnlCztLw cD47w7YENghIucNUIIypLId4C5dXc4H8D93e17MrSbGRe4ipfoQhxPCIhIU3KJsvFjel0HcN2iwf8xURv2z1lWiN 2jkZjzLiMRWPKfSvtBVzuWkIo5uZ5u25IXle3tJ4SICh0-516sU84DFu0wkPO-q1xGpiff","expires_in":179 9,"token_type":"Bearer"} Then pass “Bearer “ + access_token as the Authorization header. See GoogleCloudMetadataTokenProvider.swift.
  • 22. Easier ways to get tokens (2 of 2) If you’re calling a Google API from anywhere, you can use a Service Account. 1. Create and download the account credentials. 2. Create a JWT token and sign it with the account credentials. 3. POST the signed token to the Google Account Service and get a token! See ServiceAccountTokenProvider.swift.
  • 23. Aside: Build an HTTP server with swift-server/http
  • 25. It’s just a function call. service Echo { // Immediately returns an echo of a request. rpc Get(EchoRequest) returns (EchoResponse) {} }
  • 33. Protocol Buffers are a language-neutral, platform-neutral, extensible mechanism for serializing structured data.
  • 34. “Protocol Buffers” means several things 1. A serialization mechanism 2. An interface description language 3. A methodology
  • 35. Protocol Buffer Serialization It’s just a stream of bytes [field_number<<3 + wire_type] [length if necessary] [data]... $ hexdump /tmp/request.bin 0000000 0a 05 68 65 6c 6c 6f 0a is “0000 1010”, so field_number = 1 and wire_type = 2
  • 36. Protocol Buffers aren’t just for networking class UserData { static let sharedInstance = UserData() public var user : Models_User init() { // read info from UserDefaults if let userdata = UserDefaults.standard.data(forKey:"user") { do { user = try Models_User(serializedData:userdata) } catch { user = Models_User() } } else { user = Models_User() } } func save() { DispatchQueue.main.async { do { try UserDefaults.standard.set(self.user.serializedData(), forKey:"user") } catch (let error) { print("SAVE ERROR (error)") } } }
  • 37. A Data Definition Language syntax = "proto3"; package models; message PlantList { string id = 1; string name = 2; repeated PlantListItem plants = 11; } message PlantListItem { string id = 1; string botanical_name = 2; } message User { string id = 1; string name = 2; repeated PlantList plantLists = 3; }
  • 40. A Methodology % protoc models.proto --swift_out=. # # This runs a plugin called protoc-gen-swift # # The plugin generates a Swift source file that implements # the data structures defined in models.proto and code # for reading and writing them as serialized bytes. #
  • 42. Build a Swift client and service with gRPC
  • 43. echo.proto package echo; service Echo { // Immediately returns an echo of a request. rpc Get(EchoRequest) returns (EchoResponse) {} // Splits a request into words and returns each word in a stream of messages. rpc Expand(EchoRequest) returns (stream EchoResponse) {} // Collects a stream of messages and returns them concatenated when the caller closes. rpc Collect(stream EchoRequest) returns (EchoResponse) {} // Streams back messages as they are received in an input stream. rpc Update(stream EchoRequest) returns (stream EchoResponse) {} } message EchoRequest { // The text of a message to be echoed. string text = 1; } message EchoResponse { // The text of an echo response. string text = 1; }
  • 45. EchoService.swift (1/3) class EchoProvider : Echo_EchoProvider { // get returns requests as they were received. func get(request : Echo_EchoRequest, session : Echo_EchoGetSession) throws -> Echo_EchoResponse { var response = Echo_EchoResponse() response.text = "Swift echo get: " + request.text return response } // expand splits a request into words and returns each word in a separate message. func expand(request : Echo_EchoRequest, session : Echo_EchoExpandSession) throws -> Void { let parts = request.text.components(separatedBy: " ") var i = 0 for part in parts { var response = Echo_EchoResponse() response.text = "Swift echo expand ((i)): (part)" try session.send(response) i += 1 sleep(1) } }
  • 46. EchoService.swift (2/3) // collect collects a sequence of messages and returns them concatenated when the caller closes. func collect(session : Echo_EchoCollectSession) throws -> Void { var parts : [String] = [] while true { do { let request = try session.receive() parts.append(request.text) } catch Echo_EchoServerError.endOfStream { break } catch (let error) { print("(error)") } } var response = Echo_EchoResponse() response.text = "Swift echo collect: " + parts.joined(separator: " ") try session.sendAndClose(response) }
  • 47. EchoService.swift (3/3) // update streams back messages as they are received in an input stream. func update(session : Echo_EchoUpdateSession) throws -> Void { var count = 0 while true { do { let request = try session.receive() count += 1 var response = Echo_EchoResponse() response.text = "Swift echo update ((count)): (request.text)" try session.send(response) } catch Echo_EchoServerError.endOfStream { break } catch (let error) { print("(error)") } } try session.close() } }
  • 48. main.swift (1/3) // Unary if client == "get" { var requestMessage = Echo_EchoRequest() requestMessage.text = message print("Sending: " + requestMessage.text) let responseMessage = try service.get(requestMessage) print("get received: " + responseMessage.text) } // Server streaming if client == "expand" { var requestMessage = Echo_EchoRequest() requestMessage.text = message print("Sending: " + requestMessage.text) let expandCall = try service.expand(requestMessage) {result in } var running = true while running { do { let responseMessage = try expandCall.receive() print("Received: (responseMessage.text)") } catch Echo_EchoClientError.endOfStream { print("expand closed") running = false } } }
  • 49. main.swift (2/3) // Client streaming if client == "collect" { let collectCall = try service.collect() {result in } let parts = message.components(separatedBy:" ") for part in parts { var requestMessage = Echo_EchoRequest() requestMessage.text = part print("Sending: " + part) try collectCall.send(requestMessage) {error in print(error)} sleep(1) } let responseMessage = try collectCall.closeAndReceive() print("Received: (responseMessage.text)") }
  • 50. main.swift (3/3) // Bidirectional streaming if client == "update" { let updateCall = try service.update() {result in} DispatchQueue.global().async { var running = true while running { do { let responseMessage = try updateCall.receive() print("Received: (responseMessage.text)") } catch Echo_EchoClientError.endOfStream { print("update closed") latch.signal() break } catch (let error) { print("error: (error)") } } } ... ... let parts = message.components(separatedBy:" ") for part in parts { var requestMessage = Echo_EchoRequest() requestMessage.text = part print("Sending: " + requestMessage.text) try updateCall.send(requestMessage) {error in print(error)} sleep(1) } try updateCall.closeSend() // Wait for the call to complete. latch.wait() } }