00:01 Let's talk about the networking layer of the Swift talk app. 00:06 We think it's an interesting example to look at because we designed it differently than in previous Objective-C projects. 00:16 Typically, we would have created some kind of a Webservice class with individual methods that perform calls to particular endpoints. These methods return the data that we get back from these endpoints via a callback. 00:32 For example, we could have a loadEpisodes method, which makes the network call, parses the result, instantiates some Episode objects, and returns an array with the episodes. 00:44 We could also have a similar loadMedia method, which goes through the same steps to load the media for a particular episode:

final class Webservice { func loadEpisodes ( completion : ([ Episode ]?) -> ()) { } func loadMedia ( episode : Episode , completion : ( Media ?) -> ()) { } }

00:50 In Objective-C, the advantage of this pattern is that the result in the callback has the correct type. 00:58 For example, we would get back an array of episodes and not just something of type id simply because it's a method that loads just any data from the network. 01:07 The disadvantage of this pattern is that each method performs a complex task behind the scenes: it makes a network call, parses the data, instantiates some model objects, and finally returns them via the callback. There are a lot of places where it can go wrong, and because of this, it's hard to test. 01:29 These methods are also asynchronous, which makes them even harder to test. Also, we would need to have a network stack set up or mocked, which makes the tests complicated. 01:39 In Swift, there are other patterns we can use to make this simpler.

The Resource Struct

01:51 We create a Resource struct, which is generic over the result type. This struct has two properties: the URL of the endpoint, and a parse function. The parse function tries to convert some data into the result:

struct Resource < A > { let url : NSURL let parse : NSData -> A ? }

02:12 The parse function's return type is optional because the parsing might fail. Instead of making it optional, we could also use a Result type or make it throws in order to pass on more detailed information about what went wrong. 02:27 Additionally, if we wanted to deal only with JSON, the parse function could take an AnyObject instead of NSData . 02:37 However, using AnyObject would prevent us from using our Resource for anything but JSON — for example, images.

02:59 Let's create the episodesResource . It's just a simple resource that returns NSData :

let episodesResource = Resource < NSData >( url : url , parse : { data in return data })

03:33 In the end, this resource should have a result type of [Episode] . We'll refactor the parse function in several steps to get from a result of NSData to a result of [Episode] .

The Webservice Class

03:58 To load a resource from the network, we create a Webservice class with just one method: load . This method is generic and takes the resource as its first parameter. 04:32 The second parameter is a completion handler, which takes an A? because the request might fail or something else could go wrong. 04:48 In the load method, we use NSURLSession.sharedSession() to make the call. 04:54 We create a data task with the URL, which we get from the resource. 05:07 The resource bundles all the information we need to make a request. Currently, it only contains the URL, but there could be more properties in the future. 05:13 In the data task's completion handler, we get the data as the first parameter, but we'll ignore the other two parameters. 05:26 Finally, to start the data task, we have to call resume :

final class Webservice { func load < A >( resource : Resource < A >, completion : ( A ?) -> ()) { NSURLSession . sharedSession (). dataTaskWithURL ( resource . url ) { data , _ , _ in if let data = data { completion ( resource . parse ( data )) } else { completion ( nil ) } }. resume () } }

05:38 To call the completion handler, we have to transform the data into the resource's result type by applying the parse function. 05:53 Since the data is optional, we use optional binding. If the data is nil , we call the completion handler with nil . 06:10 If the data isn't nil , we call the completion handler with the result of the parse function.

06:22 Because we're working in a playground, we have to make it execute indefinitely; otherwise, it'll stop as soon as the main queue is done:

import XCPlayground XCPlaygroundPage . currentPage . needsIndefiniteExecution = true

07:00 Let's create a Webservice instance and call its load method with the episodesResource . In the completion handler, we'll print the result:

Webservice (). load ( episodesResource ) { result in print ( result ) }

07:18 In the console, we see that we get back some raw binary data. 07:31 Before we continue, we'll refactor the load method — we don't like the double call to completion . 07:51 We can try using a guard let . 08:02 However, then we still have two calls to completion, and we also have to add an extra return statement:

final class Webservice { func load < A >( resource : Resource < A >, completion : ( A ?) -> ()) { NSURLSession . sharedSession (). dataTaskWithURL ( resource . url ) { data , _ , _ in guard let data = data else { completion ( nil ) return } completion ( resource . parse ( data )) }. resume () } }

08:07 Another approach is to use flatMap . 08:20 First, we can try map . However, map gives us an A?? , instead of the A? we're looking for. 08:42 Using flatMap will remove the double optional:

final class Webservice { func load < A >( resource : Resource < A >, completion : ( A ?) -> ()) { NSURLSession . sharedSession (). dataTaskWithURL ( resource . url ) { data , _ , _ in let result = data . flatMap ( resource . parse ) completion ( result ) }. resume () } }

Parsing JSON

08:58 As the next step, we'll change the episodesResource in order to parse the NSData into a JSON object. 09:08 For this, we'll use the built-in JSON parsing. 09:23 Since JSON parsing is a throwing operation, we call the parsing method with try? :

let episodesResource = Resource < AnyObject >( url : url , parse : { data in let json = try ? NSJSONSerialization . JSONObjectWithData ( data , options : []) return json })

09:40 In the sidebar, we see that the binary data gets parsed. It's an array of dictionaries, so we could make the result type more specific. 09:52 A JSON dictionary contains String s as the keys and AnyObject s as the values. 10:05 If we change the result type to an array of JSONDictionary s, we need to add a cast as well:

typealias JSONDictionary = [ String : AnyObject ] let episodesResource = Resource JSONDictionary]>( url : url , parse : { data in let json = try ? NSJSONSerialization . JSONObjectWithData ( data , options : []) return json as ? [ JSONDictionary ] })

10:23 The next step is to return an array of Episode s, so we need to turn each JSON dictionary into an Episode . 10:37 We can do this in an initializer on Episode that takes a dictionary. 10:53 Before we write this initializer though, we'll first add some properties on Episode : id and title , which are both String s. In the real project, there are many more properties:

struct Episode { let id : String let title : String }

11:13 We can now write a failable initializer in an extension. By writing it in an extension, we keep the default memberwise initializer. 11:55 Within this initializer, we first need to check if the dictionary contains all the data we need. 12:02 We use a guard statement for that, and then we check if the dictionary contains an id and if it's a String . Extracting the title works the same way. 12:24 If the guard fails, we immediately return nil . 12:32 If it succeeds, we can assign the id and the title :

extension Episode { init ?( dictionary : JSONDictionary ) { guard let id = dictionary [ "id" ] as ? String , title = dictionary [ "title" ] as ? String else { return nil } self . id = id self . title = title } }

12:48 Now we can refactor the episodesResource to return an array of Episode s. 13:17 First, we check if we have JSON dictionaries. Otherwise, we immediately return nil . 13:39 To convert the dictionaries to episodes, we can map over them and use the failable Episode.init as our transform function. 13:55 However, the initializer returns an optional, so the result of the map is [Episode?] . But we don't want the nil s in there; the result's type should be [Episode] . 14:12 Again, we can fix that by using flatMap .

14:18 In our project, we used a different version of flatMap . flatMap will silently ignore the dictionaries that couldn't be parsed, and we want to fail completely in case any of the dictionaries are invalid. Not ignoring the invalid dictionaries is a domain-specific decision:

extension SequenceType { public func failingFlatMap < T >( transform : ( Self . Generator . Element ) throws -> T ?) rethrows -> [ T ]? { var result : [ T ] = [] for element in self { guard let transformed = try transform ( element ) else { return nil } result . append ( transformed ) } return result } }

14:52 We can refactor our parse function to remove the double return statements. 15:01 First, we could try using guard again, but this doesn't remove the two return statements. 15:18 However, guard allows us to get rid of one level of nesting, and the early exit is clearer:

let episodesResource = Resource Episode]>( url : url , parse : { data in let json = try ? NSJSONSerialization . JSONObjectWithData ( data , options : []) guard let dictionaries = json as ? [ JSONDictionary ] else { return nil } return dictionaries . flatMap ( Episode . init ) })

15:28 We can try to get rid of the double return by using optional chaining on dictionaries :

let episodesResource = Resource Episode]>( url : url , parse : { data in let json = try ? NSJSONSerialization . JSONObjectWithData ( data , options : []) let dictionaries = json as ? [ JSONDictionary ] return dictionaries ?. flatMap ( Episode . init ) })

15:44 This starts to get hard to understand. We have an optional dictionaries , and we use optional chaining to call flatMap , which has a failable initializer as its argument. In this case, we would probably go for the guard version, as it's clearer. 16:02 However, you could make an argument for either solution.

JSON Resources

16:07 Once we create more resources, it's necessary to duplicate the JSON parsing in each resource. 16:19 To remove the duplication, we could create a different kind of resource. However, we can also extend the existing resource with another initializer. 16:34 This initializer also takes a URL, but the type of the parse function is AnyObject -> A? instead of NSData -> A? . 17:09 We wrap this parse function in another function of type NSData -> A? and move the JSON parsing from our episodesResource into this wrapper. 17:33 Because the parsed JSON is an optional, we can use flatMap to call parseJSON :

extension Resource { init ( url : NSURL , parseJSON : AnyObject -> A ?) { self . url = url self . parse = { data in let json = try ? NSJSONSerialization . JSONObjectWithData ( data , options : []) return json . flatMap ( parseJSON ) } } }

18:00 Now we can change our episodesResource to use the new initializer:

let episodesResource = Resource Episode]>( url : url , parseJSON : { json in guard let dictionaries = json as ? [ JSONDictionary ] else { return nil } return dictionaries . flatMap ( Episode . init ) })

Naming the Resources

18:17 Another thing we don't like is that this episodesResource is in the global namespace. We're also not fond of its name. 18:30 We can move the episodesResource into an extension on Episode as a type property. 18:50 We could rename it to allEpisodesResource , a descriptive and verbose name. 19:03 However, we don't really like that. Looking at the type, it's already clear that it belongs to Episode . From the type, it's also clear that it's a resource, so why don't we just call it all ? 19:20 At the call site, it'll be clear:

Webservice (). load ( Episode . all ) { result in print ( result ) }

19:40 Looking at the call site really convinced us that this is a good idea. 19:45 However, at first we thought it was a dangerous name, as you might confuse this with a collection. 19:53 We don't think that's a problem though, because it would immediately fail if you try to use it as a collection.

20:09 In the extension on Episode , we can also add other resources that depend on the episode's properties — for example, a media resource, which fetches the media for a particular episode. 20:47 In the media resource, we can use string interpolation to construct a URL:

extension Episode { var media : Resource < Media > { let url = NSURL ( string : "http://localhost:8000/episodes/ \ ( id ) .json" )! } }

21:18 If we need more parameters that aren't available in the Episode struct, we can change the resource property to a method and pass the parameters in directly.

21:27 What we like about this approach to networking is that almost all the code is synchronous. It's simple, it's easy to test, and we don't need to set up a networking stack or something to test it. The only asynchronous code we have is the Webservice.load method. 21:53 This architecture is a good example of something that comes naturally out of Swift; Swift's generics and structs make it easy to design it like this. The same design wouldn't have had the same advantages in Objective-C, and it would have felt out of place.