Vandad Nahavandipoor
http://www.oreilly.com/pub/au/4596
Email: [email protected]
Blog: http://vandadnp.wordpress.com
Skype: vandad.np
A few weeks ago I started checking out some Wikipedia articles about various s/e design patterns and came across the Builder pattern which is a Creational GoF pattern. Then as you know, I cannot just read one article in one sitting. I have to click every link that the article leads to, so I stumbled upon the article about Fluent Interfaces and I could then see the possibilities.
Note: Fluent Interfaces have nothing to do with IB or a visual interface that is displayed on the screen at all. Fluent interfaces are the way that we can write our software to ensure they are... well... fluent. Read on to understand how this works.
I don't think fluent interfaces are the same as the builder pattern. I don't really think fluent interface is actually a pattern at all. I believe that fluent interfaces are a concept, and a kick ass one at that. I think mixing fluent interfaces and the builder pattern will allow us to build Swift classes that are amazingly simple to use, instead of the classic OOP designs that we see on pretty much every Apple class these days. I wish Apple could read this article and (ehem), just update their iOS SDK classes for instance to use fluent interfaces and the builder pattern.
If you want to write your Swift apps in the most kick ass way, continue reading. I think this article will help you a lot not only in learning more about Swift, but also writing some really crazy code that will make your life and those around you much easier.
Let's see an example of the Builder pattern:
import Foundation
class NameBuilder{
private var firstName = ""
private var lastName = ""
func setFirstName(f: String){
firstName = f
}
func setLastName(l: String){
lastName = l
}
func toString() -> String{
return "\(firstName) \(lastName)"
}
}
This is easy to understand. There is a class with first name and last name and you can use it like so:
let builder = NameBuilder()
builder.setFirstName("Vandad")
builder.setLastName("Nahavandipoor")
println(builder.toString())
okay we all can understand how the Builder pattern works in Swift. Let's move on.
Pay attention to this Boy
class:
class Boy{
func jump() -> Self{
println("Jumping...")
return self
}
func run() -> Self{
println("Running...")
return self
}
func restFor(seconds: Int) -> Self{
println("Resting for \(seconds) seconds")
return self
}
func stop() -> Self{
println("Stopping")
return self
}
}
"what the hell kind of sourcery is this?" you asked... well, pay attention to how every method returns self
. This allows us to chain our method calls. This is the foundation of fluent interfaces. Now how can we use this awesome code? With an even more awesome code:
Boy().run().run().jump().run().restFor(10).jump().stop()
And the output will be like this:
Running...
Running...
Jumping...
Running...
Resting for 10 seconds
Jumping...
Stopping
freaking amazing, right? Did the coin drop? awesome. Let's move on. I want let you know that if you have a method in your Swift code that returns Void
, please change it so that it returns Self
. No harm done if you don't use the return value, at least your code will be fluent.
I loved NSURLConnection
before I got into fluent interfaces. I think this class has so much potential and Apple wasted it by returning void
from many of the methods. This is just unacceptable. I was at work the other day and I realized that every time I do a call with NSURLConnection
I have to do so much boiler plate:
- I have to see if the data came back or not
- I have to see if the response is of type
NSURLHttpResponse
or not and then get the status code - I have to check the status code and see if it's the status code I wanted (e.g. 200)
- I have to see if an error came back. Was this a connection error. What error was this?
- I have to enable GZIP manually on the headers
- I have to post my header values manually in a freaking dictionary. Hello? Make this shit easier for us, Apple!
- And a tons more...
So why is this class so dumb? Well, the people who wrote it didn't know better. That's my answer. Let me clarify, I am not saying they are stupid or anything. Nobody wakes up in the morning saying "I'm going to write some shitty code at work today". No! People aren't stupid. People do the best they can and unfortunately the best they could do was not good enough.
So I am going to change that now and create a fluent interface on top of NSURLConnection
that will cut our boiler plate code to half, or less!
Let's begin by creating the interface of our class:
import Foundation
typealias Block = (sender: FluentUrlConnection) -> ()
enum FluentUrlConnectionType : String{
case POST = "POST"
case GET = "GET"
case PUT = "PUT"
case DELETE = "DELETE"
}
class FluentUrlConnection{
private var url: NSURL
private var type = FluentUrlConnectionType.GET
var connectionData: NSData?
var connectionError: NSError?
var connectionResponse: NSURLResponse?
init(url: NSURL){
self.url = url
}
convenience init(urlStr: String){
self.init(url: NSURL(string: urlStr)!)
}
func acceptGzip() -> Self{
return self
}
func onHttpCode(code: Int, handler: Block) -> Self{
return self
}
func onUnhandledHttpCode(handler: Block) -> Self{
return self
}
func setHttpBody(body: NSData) -> Self{
return self
}
func setHttpBody(body: String) -> Self{
return setHttpBody(body.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!)
}
func setHttpHeader(#value: String, forKey: String) -> Self{
return self
}
func start() -> Self{
return self
}
func onConnectionSuccess(handler: Block) -> Self{
return self
}
func onConnectionFailure(handler: Block) -> Self{
return self
}
func ofType(type: FluentUrlConnectionType) -> Self{
self.type = type
return self
}
}
So this is how I think Apple should have designed NSURLConnection
from the beginning. With our own FluentUrlConnection
class, we can do stuff like this:
FluentUrlConnection(urlStr: "http://vandadnp.wordpress.com")
.ofType(.GET)
.acceptGzip()
.setHttpBody("hello world")
.setHttpHeader(value: "Accept", forKey: "application/json")
.onHttpCode(200, handler: { (sender: FluentUrlConnection) -> () in
})
.onHttpCode(401, handler: { (sender: FluentUrlConnection) -> () in
})
.onUnhandledHttpCode { (sender: FluentUrlConnection) -> () in
}
.onConnectionSuccess { (sender: FluentUrlConnection) -> () in
}
.onConnectionFailure { (sender: FluentUrlConnection) -> () in
}
.start()
Holy jesus, right? What happened here?
- The instance of the
FluentUrlConnection
was created inline. No variable was used. - The
ofType()
function allows us to change the type of our request fromGET
, toPOST
and etc. This method returnsSelf
allowing us to chanin our calls inline. Awesome, agree? - The
acceptGzip()
function allows us to accept Gzipped content if the server supports it. See how I did this... I didn't write a property for the connection class to enable or disable this functionality. Think about it for a second, if a functionality is by default off, and you want to allow the user to turn it on when they want, why should you expose a property to the user? Just expose a fluent function that allows the user to turn the functionality on. After turning somethin on, of course they are not going to want to turn it off again. Think about it a bit... did it drop? what? the coin I mean! - The
setHttpBody()
function allows the programmer to set the body of the request whenever he wants to. Not at any specific point. This can be done even later, or you can do this call twice. It is completely up to you. - The
onHttpCode()
function is awesome itself. It will call the given function if the connection comes back with the given http status code. - The
onUnhandledHttpCode()
must be my favorite. The block that is passed to this function gets called if the http status code is not handled by one of the otheronHttpCode()
functions. - The
onConnectionSuccess()
function is called if the connection comes back successfully. This means that the error of the connection isnil
. - The
onConnectionFailure()
function is called if the connection came back with anerror
.
Please just take a few moments now and properly think about what this code is doing. Don't think about the syntax as much. Think about the fact that the fluent interface and the builder pattern used in this example code is completely abstracting the complication of writing code and is pretty much eliminating all the shitty if
statements that we may have had to write so far with NSURLConnection
.
Now time to implement this class, don't you agree?
-
Let's start with the implementation of the
onHttpCode()
function. This function takes in an http status code of typeInt
and a block object that should be called, if we receive the given status code. So we must save these block objects and their corresponding status codes in some sort of a dictionary where the keys to this dictionary are the status codes asInt
and the values are the block objects. Here is that variable in our class. Put this on top of the class:private lazy var httpCodeHandlers: [Int : Block] = { return [Int : Block]() }()
and then let's code the method:
func onHttpCode(code: Int, handler: Block) -> Self{ httpCodeHandlers[code] = handler return self }
-
Then let's code the
onUnhandledHttpCode()
function. This function takes in a closure of typeBlock
which we have already defined before, and will call this closure whenever an http status code which we have not handled yet is called. Let's keep a reference to this closure in a variable. So put this variable on top of your class:private var unhandledHttpCodeHandler : Block?
and then code the method itself:
func onUnhandledHttpCode(handler: Block) -> Self{ unhandledHttpCodeHandler = handler return self }
note how the variable is optional? this means that we may or may not have a block for unhandled http status codes.
-
It's time to implement the
setHttpBody()
function. This will just keep a reference to an object of typeNSData
to be sent to the server as part of the body of the request. So keep this in a varibale. Put this on top of your class code:private var httpBody: NSData?
and then code the method:
func setHttpBody(body: NSData) -> Self{ httpBody = body return self }
Keep in mind that we had an overloaded version of the
setHttpBody()
function that took in a raw string. Yeah, damn right. About time we stopped with rigidity in our code. Why just take in a freakingNSURL
if you know most developers just have theString
version of theNSURL
? -
How about the
setHttpHeader()
function now. For this, we need a dictionary that can keep track of the given header keys and values. Let's make this lazily allocated because we may not even use it eventually:private lazy var httpHeaders: [String : String] = { return [String : String]() }()
and then method itself of course:
func setHttpHeader(#value: String, forKey: String) -> Self{ httpHeaders[forKey] = value return self }
Note: I'm going to differ the implementation of the
start()
function for later as that's the biggest function of this class. -
The
onConnectionSuccess()
function is next up. This just keeps a reference to a block to be called if the connection doesn't give us an error back. Let's create a variable for this block object. Put this on top of your class:private var connectionSuccessHandler: Block?
and then implement the method:
func onConnectionSuccess(handler: Block) -> Self{ connectionSuccessHandler = handler return self }
-
Do the same for the
onConnectionFailure()
function. This function keeps a reference to a block object to be called if the connection comes back with an error. Let's start with the reference variable:private var connectionFailureHanlder: Block?
and then the function implementation:
func onConnectionFailure(handler: Block) -> Self{ connectionFailureHanlder = handler return self }
-
We also need to implement the
acceptGzip()
function. This function simply switches our variable on. Put the variable on top of the class:private var acceptsGzip = false
and then implement the function
func acceptGzip() -> Self{ acceptsGzip = true return self }
-
And now the implementation of the mother of them all... the
start()
function. This function has to put everything together that we have done so far and then do the actual creation of theNSURLConnection
://this attaches "gzip" to the end of the "Accept-Encoding" key of the httpHeaders if gzip encoding is enabled private func handleGzipFlag(){ if acceptsGzip{ var acceptEncodingKey = "Accept-Encoding" var acceptEncodingValue = "gzip" for (key, value) in httpHeaders{ if key == acceptEncodingKey{ acceptEncodingValue += ", " + value } } httpHeaders[acceptEncodingKey] = acceptEncodingValue } } func start() -> Self{ handleGzipFlag() let request = NSMutableURLRequest(URL: url) request.HTTPBody = httpBody request.HTTPMethod = type.rawValue request.allHTTPHeaderFields = httpHeaders NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) {(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in dispatch_async(dispatch_get_main_queue(), { () -> Void in self.connectionData = data self.connectionError = error self.connectionResponse = response if (error != nil){ //an error happened if let errorHandler = self.connectionFailureHanlder{ errorHandler(sender: self) } } else { //we succeeded, let the listener know if let successHandler = self.connectionSuccessHandler{ successHandler(sender: self) } //now see if we have a real http url response so that we can get the http code out of it if let httpResponse = response as? NSHTTPURLResponse{ var httpCodeIsHandled = false let statusCode = httpResponse.statusCode let handler = self.httpCodeHandlers[statusCode] if let handler = handler{ httpCodeIsHandled = true handler(sender: self) } if !httpCodeIsHandled{ if let unhandledCodeBlock = self.unhandledHttpCodeHandler{ unhandledCodeBlock(sender: self) } } } } }) } return self }
I've written some comments so that hopefully we will understand what is going on here. but I will explain:
- First we have the
handleGzipFlag()
function that goes through the http headers that we are given and if we want to enable gzip, it adds the string of"gzip"
to the end of the header with the key of "Accept-Encoding". You can read more about that on W3.org. - The
start()
function then usesNSURLConnection
in the usual manner to send an asynchronous request to the server. - As soon as we get the response, data and the error, we save them in our url connection instance so that the programmer that uses our class can read them too if he wants to.
- Then we go through our http code handlers and find the one that corresponds with the current http code and then call it if it exists. If the status code was not handled and we had a block object to execute in the case of unhandled-http-code, then we call that block.
- First we have the
So now if I want to for instance get the contents of a website with the GET
method I can just do this:
FluentUrlConnection(urlStr: "http://vandadnp.wordpress.com")
.ofType(.GET)
.acceptGzip()
.onConnectionSuccess { (sender: FluentUrlConnection) -> () in
if let data = sender.connectionData{
println("Got data = \(data)")
}
}
.onConnectionFailure { (sender: FluentUrlConnection) -> () in
println("Failed. \(sender.connectionError)")
}
.start()
So awesome, at least I think so. Much clearner than the NSURLConnection
equivalent of convoluted if
statements and useless NSURL
constructions:
Here is the same code but with pure NSURLConnection
. Kinda disgusting:
let url = NSURL(string: "http://vandadnp.wordpress.com")!
let request = NSMutableURLRequest(URL: url)
request.allHTTPHeaderFields = ["Accept-Encoding" : "gzip"]
let queue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue) { (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
if error != nil{
println("Error = \(error)")
} else {
// success
if let httpResponse = response as? NSHTTPURLResponse{
if (httpResponse == 200){
} else {
}
if data != nil{
// do something
}
}
}
}
I have packaged this class up in my open-source Swift library which is called Chorizo, under the name of ChorizoProUrlConnection
. Check it out. Have a look at that for more information and inspirations/ideas. I have also put the code inside the exampleCode
folder of this folder in this repo so that's another way of getting the whole code for this class.
I think fluent interfaces are amazing for chaining calls. that's what they are really good for. So let's say that you have a few tasks that you want to do serially, but some are on the main thread, some on background threads, etc. You can do this with GCD obviously but the syntax is, well, a bit weird and you will have to create serial queues and etc. Ergh, who would want to do that?
The other option is to use NSOperationQueue
and that's pretty decent. But then again, let's have a look at some of the functions available in this class:
class NSOperationQueue : NSObject {
func addOperation(op: NSOperation)
@availability(iOS, introduced=4.0)
func addOperations(ops: [AnyObject], waitUntilFinished wait: Bool)
@availability(iOS, introduced=4.0)
func addOperationWithBlock(block: () -> Void)
var operations: [AnyObject] { get }
@availability(iOS, introduced=4.0)
var operationCount: Int { get }
var maxConcurrentOperationCount: Int
var suspended: Bool
@availability(iOS, introduced=4.0)
var name: String?
@availability(iOS, introduced=8.0)
var qualityOfService: NSQualityOfService
@availability(iOS, introduced=8.0)
unowned(unsafe) var underlyingQueue: dispatch_queue_t /* actually retain */
func cancelAllOperations()
func waitUntilAllOperationsAreFinished()
@availability(iOS, introduced=4.0)
class func currentQueue() -> NSOperationQueue?
@availability(iOS, introduced=4.0)
class func mainQueue() -> NSOperationQueue
}
What a shame... so many functions that return Void
. So basicall you have to code your app like this:
queue.doThis()
queue.doSomethingElse()
queue.doBlaBla()
All this repitition makes me go crazy a bit. Why do I have to repeat queue
all the time? Why do I even freaking need a variable name? No. We can make this look better. Let's start with the interface of our queue:
import Foundation
typealias FluentQueueTask = () -> ()
class FluentSerialQueue{
func onBackround(task: FluentQueueTask) -> Self{
return self
}
func onMainThread(task: FluentQueueTask) -> Self{
return self
}
func start() -> Self{
return self
}
}
And we would like to be able to use this class to serially execute tasks on main and the background thread, one by one. This is how we will use it:
FluentSerialQueue().onBackround { () -> () in
println("Background 1, thread = \(NSThread.currentThread())")
}.onMainThread { () -> () in
println("Main 1, thread = \(NSThread.currentThread())")
}.onBackround { () -> () in
println("Background 2, thread = \(NSThread.currentThread())")
}.onMainThread { () -> () in
println("Main 2, thread = \(NSThread.currentThread())")
}.start()
Now do this with GCD, seriously. See your code, then look at this code. See your code again, and look at this code! Bear in mind that one cannot achieve the same type of elegance in code without fluent interfaces. God bless the fluent interfaces! Your code may achieve this same thing with GCD (obviously with more lines of code) but it will never be as flexible as this, in the exact same way. The calls on this class can be chained for ever basically and that's the beauty of the fluent interfaces. Now let's implement the class:
import Foundation
typealias FluentQueueTask = () -> ()
class FluentSerialQueue{
private struct FluentTask{
var block: FluentQueueTask
var onBackgroundThread: Bool
}
private lazy var tasks: [FluentTask] = {
return [FluentTask]()
}()
private lazy var queue = dispatch_queue_create("com.pixolity.serialQueue", DISPATCH_QUEUE_SERIAL)
func onBackround(task: FluentQueueTask) -> Self{
tasks.append(FluentTask(block: task, onBackgroundThread: true))
return self
}
func onMainThread(task: FluentQueueTask) -> Self{
tasks.append(FluentTask(block: task, onBackgroundThread: false))
return self
}
func start() -> Self{
for task in tasks{
if task.onBackgroundThread{
dispatch_async(queue, task.block)
}
else {
dispatch_async(queue, { () -> Void in
let sema = dispatch_semaphore_create(0)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
task.block()
dispatch_semaphore_signal(sema)
})
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)
})
}
}
return self
}
}
this is what I'm doing:
- For every task, I am defining the block object as
FluentQueueTask
. This is a block that takes no parameters and returns nothing. - Internally, the class uses the
FluentTask
structure to define its tasks. This structure encapsulates theFluentQueueTask
block and a boolean value that indicates whether the task has to be executed on a background thread or not. - We also have an array of tasks named
tasks
. This is just a privately and lazily allocated array of tasks. - The
queue
variable (it's notlet
because it is lazily allocated) is a serial GCD queue. - For the
onBackround()
and theonMainThread()
functions, we are just creating new instances of theFluentTask
structure and placing them in our array ready for execution later. - When the
start()
function is called and will serially execute our tasks. yes. that's a semaphore I'm using. if you know a better way of serially executing tasks on the main thread mixed with background threads, please fix this article and submit a pull request.
- Do not return
Void
from your methods if you want to create a fluent interface. Always returnSelf
as the return type and return the actualself
as the return value. - In a fluent interface, when a functionality is turned off by default (
false
), do not enable this functionality through a property. Rather, create a method that returnsSelf
and allows the programmer to enable the functionality. This allows for a fluent interface rather thanobj.property = value
type of old fashioned OOP programming. - Fluent interfaces created in the right way eliminate the need of creating a variable pointing to the original object. The object is passed to the completion blocks whenever needed.
- Fluent interfaces mixed with the Builder pattern allow a class or a structure to Swift to encapsulate its own work within itself, and only expose logical listeners/handlers to the outside world, cutting the workd of the programmer that uses your APIs in half, getting rid of clutter and repetition in their code, getting rid of
if
statements and whatnot. - One of the benefits of the Builder pattern is that the class that is the builder can be passed from one function to another, one by one completing the building process. One function can call a specific method on the same builder to complete a specific task, pass the same builder to another function to continue building it until it is fully built.