Effective Networking with Swift and iOS 8 Ben Scheirman - - PowerPoint PPT Presentation

effective networking
SMART_READER_LITE
LIVE PREVIEW

Effective Networking with Swift and iOS 8 Ben Scheirman - - PowerPoint PPT Presentation

Effective Networking with Swift and iOS 8 Ben Scheirman @subdigital ChaiOne Agenda Old and Crusty NSURLConnection New Hotness Live Demos ! HTTP Caching Bonus Round: API Tips NSURLConnection NSURLConnection


slide-1
SLIDE 1

Effective Networking

with Swift and iOS 8

slide-2
SLIDE 2

Ben Scheirman

@subdigital

slide-3
SLIDE 3

ChaiOne

slide-4
SLIDE 4
slide-5
SLIDE 5

Agenda

  • Old and Crusty NSURLConnection
  • New Hotness
  • Live Demos !
  • HTTP
  • Caching
  • Bonus Round: API Tips

NSURLConnection

slide-6
SLIDE 6

NSURLConnection

Invented for Safari ~2000 Made public in 2003 Poor separation of settings, config, cache

slide-7
SLIDE 7
slide-8
SLIDE 8
slide-9
SLIDE 9

NSURLConnection was replaced by NSURLSession in iOS 7

slide-10
SLIDE 10

NSURLSession

slide-11
SLIDE 11

Still use NSURLRequest Configurable Container (isolation w/ 3rd party libraries) Improved auth Rich Callbacks

slide-12
SLIDE 12
slide-13
SLIDE 13
slide-14
SLIDE 14

The New Family

slide-15
SLIDE 15

NSURLSessionConfiguration NSURLSession NSURLSessionTask NSURLSessionDelegate

slide-16
SLIDE 16

Start with NSURLSessionConfiguration

slide-17
SLIDE 17

Default Sessions

+ defaultSessionConfiguration

slide-18
SLIDE 18

Ephemeral Sessions

+ ephemeralSessionConfiguration

slide-19
SLIDE 19

Ephemeral Sessions

+ ephemeralSessionConfiguration

?

slide-20
SLIDE 20

"private browsing"

slide-21
SLIDE 21

Background Sessions

+ backgroundSessionConfiguration:

slide-22
SLIDE 22

Background Sessions

+ backgroundSessionConfiguration:

  • Takes an NSString identifier
  • Causes uploads / downloads to occur in background
slide-23
SLIDE 23

Customize it further

All of these class methods return a copy, tweak from there...

slide-24
SLIDE 24

Well, what can you do with it?

slide-25
SLIDE 25

Set a default header

var config = NSURLSessionConfiguration.defaultSessionConfiguration() config.HTTPAdditionalHeaders = [ "auth_token" : "1234abcd" ]

slide-26
SLIDE 26

Mark Requests as low-priority

config.discretionary = true

  • Note: This has benefits, such as retrying when connection

terminates, avoiding request if user is low on battery, or if Wi- Fi performance is not good enough.

slide-27
SLIDE 27

Disable Cellular

config.allowsCellular = false

slide-28
SLIDE 28

Set custom caching behavior

config.URLCache = MyCustomCache() config.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad

slide-29
SLIDE 29

Inject your own custom protocols!

slide-30
SLIDE 30

ben://awww.yeah

slide-31
SLIDE 31

(actually quite useful for mocking requests)

slide-32
SLIDE 32

Next Step: build an NSURLSession to make reuqests

slide-33
SLIDE 33

var session = NSURLSession(configuration: config)

slide-34
SLIDE 34

var session = NSURLSession(configuration: config)

  • r with a delegate...

var delegate: NSURLSessionDelegate = self var session = NSURLSession(configuration: config, delegate: delegate, delegateQueue: NSOperationQueue.mainQueue())

slide-35
SLIDE 35

NSURLSession

  • Construct without the delegate if

you want to use block callbacks

  • Construct with a delegate for

advanced control (or background sessions)

  • If you pass completion handler

blocks, delegate methods are not called

slide-36
SLIDE 36

NSURLSessionTask

slide-37
SLIDE 37
slide-38
SLIDE 38

NSURLSessionDataTask

  • Represents GET, PUT, POST, DELETE
  • Handle Completion Callback
  • If error is present, request failed
slide-39
SLIDE 39

What Constitutes an Error?

  • Connection failed
  • Timeouts
  • Host invalid
  • Bad URL
  • Too many redirects
  • ... dozens more (check URL Loading System Error Codes)
slide-40
SLIDE 40

NSURLSessionDownloadTask

  • Downloading a file / resource
  • Streams to disk
  • Useful when size is large and can't fit in memory
  • Temp file path is provided in completion block
  • MUST move it somewhere if you want it to stick around
slide-41
SLIDE 41

Building a simple GET Request

  • Build an instance of NSURLSessionDataTask
  • (optionally) give it a block callback*
  • Call resume
slide-42
SLIDE 42

var config = NSURLSessionConfiguration.defaultSessionConfiguration() var session = NSURLSession(configuration: config)

slide-43
SLIDE 43

var config = NSURLSessionConfiguration.defaultSessionConfiguration() var session = NSURLSession(configuration: config) var url = NSURL(string: "https://www.nsscreencast.com/api/episodes.json")

slide-44
SLIDE 44

var config = NSURLSessionConfiguration.defaultSessionConfiguration() var session = NSURLSession(configuration: config) var url = NSURL(string: "https://www.nsscreencast.com/api/episodes.json") var task = session.dataTaskWithURL(url) { (let data, let response, let error) in // ... }

slide-45
SLIDE 45

var config = NSURLSessionConfiguration.defaultSessionConfiguration() var session = NSURLSession(configuration: config) var url = NSURL(string: "https://www.nsscreencast.com/api/episodes.json") var task = session.dataTaskWithURL(url) { (let data, let response, let error) in // ... } // don't forget to trigger the request task.resume()

slide-46
SLIDE 46

NSURLSessionDelegate

Provide a delegate if you need more advanced control over:

  • Download Progress
  • Authentication Challenges
  • Connection Failure
slide-47
SLIDE 47

NSURLSessionDelegate

slide-48
SLIDE 48

Case Study: Requesting Images

slide-49
SLIDE 49

Have you ever seen this?

NSURL *imageUrl = [NSURL URLWithString:@”http://i.imgur.com/kwpjYwQ.jpg”]; NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; UIImage *image = [UIImage imageWithData:imageData]; [imageView setImage:image];

slide-50
SLIDE 50
slide-51
SLIDE 51

What is wrong here?

NSURL *imageUrl = [NSURL URLWithString:@”http://i.imgur.com/kwpjYwQ.jpg”]; NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; UIImage *image = [UIImage imageWithData:imageData]; [imageView setImage:image];

slide-52
SLIDE 52

These are blocking calls

! NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; ! UIImage *image = [UIImage imageWithData:imageData];

slide-53
SLIDE 53
slide-54
SLIDE 54

Demo: Downloading Images

slide-55
SLIDE 55
  • Images should probably be requested with download tasks
  • Block-based callbacks can be unwieldy (especially in

Objective-C)

  • Use the delegate if you want to report download progress
slide-56
SLIDE 56

Demo: Searching the iTunes Store

slide-57
SLIDE 57

HTTP

slide-58
SLIDE 58

Status Code Cheat Sheet

1xx - Informational / Transient 2xx - A-OK ! 3xx - It's over there " 4xx - You Messed up ✋ 5xx - We Messed Up $

slide-59
SLIDE 59

HTTP Caching

slide-60
SLIDE 60

HTTP Caching Techniques

  • If-Modified-Since
  • If-None-Match
  • Cache-Control
slide-61
SLIDE 61

If-Modified-Since

  • Client requests a resource
  • Server responds, includes a Date response header
  • Client Caches the data, including the date
  • Client sends request header If-Modified-Since
  • Server compares, can return 304 (Not Modified) with no

body

slide-62
SLIDE 62

If-None-Match

  • Client requests a resource
  • Server responds, includes a E-Tag header
  • Client Caches the data, including the E-Tag
  • Client sends request header If-None-Match
  • Server compares, can return 304 (Not Modified) with no

body

slide-63
SLIDE 63

Cache-Control

  • Server indicates when the resource expires
  • Client caches the data until that time
  • Client will immediately return local cache data if still fresh
slide-64
SLIDE 64

What does it look like on the client?

slide-65
SLIDE 65
slide-66
SLIDE 66
slide-67
SLIDE 67
slide-68
SLIDE 68

What does it look like on the server?

slide-69
SLIDE 69

def show @band = Band.find(params[:id]) fresh_when(:etag => @band, :last_modified => @band, :public => true) expires_in 10.minutes, :public => true end

slide-70
SLIDE 70

Observations

  • Server still executes a query to compute E-Tag and Modified

Date

  • No body is transfered for a matching E-Tag or Date
  • Client doesn't even make request if Cache-Control is used

and content is still fresh

slide-71
SLIDE 71

Cache-Control sounds perfect

  • Not everything is inherently cacheable
  • Only helpful for a single client, after the initial request
slide-72
SLIDE 72
slide-73
SLIDE 73

Reverse-Proxy Cache to the Rescue

slide-74
SLIDE 74
slide-75
SLIDE 75

Pros / Cons of Cache-Control

  • Client doesn't wait for a network request
  • Server spends zero resources
  • Clients May Render Stale Data
slide-76
SLIDE 76

Pros / Cons of E-Tag & IMS

  • Server skips rendering
  • Miniscule Data Transfer
  • Can appear instantaneous*
  • Server still spending resources computing & comparing E-

Tag & dates

slide-77
SLIDE 77

Reason About Your Data

  • List of US States
  • User’s Profile
  • Customer’s Order
  • Activity Timeline
  • Yesterday’s stats
slide-78
SLIDE 78

Tradeoff between fresh data and user experience

slide-79
SLIDE 79

Caching with NSURLSession

slide-80
SLIDE 80
  • Uses NSURLCache out of the box
  • Customize on the NSURLSessionConfiguration instance
  • Use ephemeralSessionConfiguration to disable caching.
slide-81
SLIDE 81

NSURLCache Gotchas

slide-82
SLIDE 82

NSURLCache Gotchas

  • will not cache on disk if max-age is less than 5 minutes
slide-83
SLIDE 83

NSURLCache Gotchas

  • will not cache on disk if max-age is less than 5 minutes
  • might not cache at all if Cache-Control isn't passed
slide-84
SLIDE 84

NSURLCache Gotchas

  • will not cache on disk if max-age is less than 5 minutes
  • might not cache at all if Cache-Control isn't passed
  • might choose an arbitrarily large max-age if none provided

*

slide-85
SLIDE 85

NSURLCache Gotchas

  • will not cache on disk if max-age is less than 5 minutes
  • might not cache at all if Cache-Control isn't passed
  • might choose an arbitrarily large max-age if none provided

*

  • might not cache if size > 5% of available capacity
slide-86
SLIDE 86

Tuning the built-in cache

let MB = 1024 * 1024 var cache = NSURLCache(memoryCapacity: 10 * MB, diskCapacity: 50 * MB, diskPath: nil) sessionConfiguration.URLCache = cache

slide-87
SLIDE 87

Default Cache Location

  • ~/Library/Caches/com.acme.app/Cache.db
slide-88
SLIDE 88
slide-89
SLIDE 89

What do I have to do?

slide-90
SLIDE 90

Maybe nothing!

  • Server should return appropriate cache headers
  • Tweak caching behavior in willCacheResponse delegate

method

slide-91
SLIDE 91

The Content Flicker Problem

slide-92
SLIDE 92

The Content Flicker Problem

  • Data is already cached and fresh with E-Tag / IMS info
  • Client must validate content is still fresh and wait for a 304
  • Response is fast, but not fast to avoid drawing empty screen
  • ...flicker
slide-93
SLIDE 93

Resolving the Content Flicker Problem

slide-94
SLIDE 94
  • 1. Return cached data immediately (if we have it)
  • 2. Proceed with Request
  • 3. Update callback (again) with fresh data
slide-95
SLIDE 95

let url = NSURL(string: "http://cache-tester.herokuapp.com/contacts.json") let request = NSURLRequest(URL: url) var task = session.dataTaskWithRequest(request) task.resume()

slide-96
SLIDE 96

let url = NSURL(string: "http://cache-tester.herokuapp.com/contacts.json") let request = NSURLRequest(URL: url) if let cachedResponse = config.URLCache.cachedResponseForRequest(request) { // update UI processData(cachedResponse.data) } var task = session.dataTaskWithRequest(request) task.resume()

slide-97
SLIDE 97

New in iOS 8: getCachedResponseForTask:

  • Provides asynchronous cache fetch:
let url = NSURL(string: "http://localhost:3000/contacts.json") let request = NSURLRequest(URL: url) var task = session.dataTaskWithRequest(request) config.URLCache.getCachedResponseForDataTask(task) { (let cachedResponse: NSCachedURLResponse?) in if cachedResponse != nil { self.processData(cachedResponse!.data) } task.resume() }
slide-98
SLIDE 98

Bonus Round

API Tips

slide-99
SLIDE 99

Don't Expose your Internal Model

slide-100
SLIDE 100

Version Your API

slide-101
SLIDE 101

Send Device Info as Headers

slide-102
SLIDE 102

Turn on Gzip Compression

slide-103
SLIDE 103

Measure Response Times

slide-104
SLIDE 104

Page Unbounded Data Sets

slide-105
SLIDE 105

Thank You!

ben@scheirman.com @subdigital • @nsscreencast benscheirman.com • nsscreencast.com speakerdeck.com/subdigital/ ios8-networking