Slide 1

Slide 1 text

Effective Networking with Swift and iOS 8

Slide 2

Slide 2 text

Ben Scheirman @subdigital

Slide 3

Slide 3 text

ChaiOne

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

NSURLConnection was replaced by NSURLSession in iOS 7

Slide 10

Slide 10 text

NSURLSession

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

The New Family

Slide 15

Slide 15 text

NSURLSessionConfiguration NSURLSession NSURLSessionTask NSURLSessionDelegate

Slide 16

Slide 16 text

Start with NSURLSessionConfiguration

Slide 17

Slide 17 text

Default Sessions + defaultSessionConfiguration

Slide 18

Slide 18 text

Ephemeral Sessions + ephemeralSessionConfiguration

Slide 19

Slide 19 text

Ephemeral Sessions + ephemeralSessionConfiguration ?

Slide 20

Slide 20 text

"private browsing"

Slide 21

Slide 21 text

Background Sessions + backgroundSessionConfiguration:

Slide 22

Slide 22 text

Background Sessions + backgroundSessionConfiguration: • Takes an NSString identifier • Causes uploads / downloads to occur in background

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Well, what can you do with it?

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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 text

Disable Cellular config.allowsCellular = false

Slide 28

Slide 28 text

Set custom caching behavior config.URLCache = MyCustomCache() config.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad

Slide 29

Slide 29 text

Inject your own custom protocols!

Slide 30

Slide 30 text

ben://awww.yeah

Slide 31

Slide 31 text

(actually quite useful for mocking requests)

Slide 32

Slide 32 text

Next Step: build an NSURLSession to make reuqests

Slide 33

Slide 33 text

var session = NSURLSession(configuration: config)

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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 text

NSURLSessionTask

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

NSURLSessionDataTask • Represents GET, PUT, POST, DELETE • Handle Completion Callback • If error is present, request failed

Slide 39

Slide 39 text

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 text

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 text

Building a simple GET Request • Build an instance of NSURLSessionDataTask • (optionally) give it a block callback* • Call resume

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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 text

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 text

NSURLSessionDelegate Provide a delegate if you need more advanced control over: • Download Progress • Authentication Challenges • Connection Failure

Slide 47

Slide 47 text

NSURLSessionDelegate

Slide 48

Slide 48 text

Case Study: Requesting Images

Slide 49

Slide 49 text

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 text

No content

Slide 51

Slide 51 text

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 text

These are blocking calls ! NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; ! UIImage *image = [UIImage imageWithData:imageData];

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

Demo: Downloading Images

Slide 55

Slide 55 text

• 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 text

Demo: Searching the iTunes Store

Slide 57

Slide 57 text

HTTP

Slide 58

Slide 58 text

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 text

HTTP Caching

Slide 60

Slide 60 text

HTTP Caching Techniques • If-Modified-Since • If-None-Match • Cache-Control

Slide 61

Slide 61 text

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 text

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 text

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 text

What does it look like on the client?

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

What does it look like on the server?

Slide 69

Slide 69 text

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 text

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 text

Cache-Control sounds perfect • Not everything is inherently cacheable • Only helpful for a single client, after the initial request

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

Reverse-Proxy Cache to the Rescue

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

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 text

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 text

Reason About Your Data • List of US States • User’s Profile • Customer’s Order • Activity Timeline • Yesterday’s stats

Slide 78

Slide 78 text

Tradeoff between fresh data and user experience

Slide 79

Slide 79 text

Caching with NSURLSession

Slide 80

Slide 80 text

• Uses NSURLCache out of the box • Customize on the NSURLSessionConfiguration instance • Use ephemeralSessionConfiguration to disable caching.

Slide 81

Slide 81 text

NSURLCache Gotchas

Slide 82

Slide 82 text

NSURLCache Gotchas • will not cache on disk if max-age is less than 5 minutes

Slide 83

Slide 83 text

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 text

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 text

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 text

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 text

Default Cache Location • ~/Library/Caches/com.acme.app/Cache.db

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

What do I have to do?

Slide 90

Slide 90 text

Maybe nothing! • Server should return appropriate cache headers • Tweak caching behavior in willCacheResponse delegate method

Slide 91

Slide 91 text

The Content Flicker Problem

Slide 92

Slide 92 text

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 text

Resolving the Content Flicker Problem

Slide 94

Slide 94 text

1. Return cached data immediately (if we have it) 2. Proceed with Request 3. Update callback (again) with fresh data

Slide 95

Slide 95 text

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 text

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 text

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 text

Bonus Round API Tips

Slide 99

Slide 99 text

Don't Expose your Internal Model

Slide 100

Slide 100 text

Version Your API

Slide 101

Slide 101 text

Send Device Info as Headers

Slide 102

Slide 102 text

Turn on Gzip Compression

Slide 103

Slide 103 text

Measure Response Times

Slide 104

Slide 104 text

Page Unbounded Data Sets

Slide 105

Slide 105 text

Thank You! [email protected] @subdigital • @nsscreencast benscheirman.com • nsscreencast.com speakerdeck.com/subdigital/ ios8-networking