Shirley
December 30th, 2015
Shirley is a minimal request framework for Swift programs. It uses ReactiveCocoa signal producers as a unified method for asynchronous data delivery, instead of callback functions. This allows disparate responses to be unified, with a single stream-based system.
Sessions
The central type of the framework is a session, represented by the SessionType
protocol. Session
is a closure-based implementation, which can also be used for type erasure. Sessions can be thought of as “signal producer producers” – they implement a single function that transforms a request value into a signal producer:
let session = ...
let request = ...
session.producerForRequest(request)
.startWithNext({ value in
...
})
SessionType
also requires type aliases for Request
, Value
, and Error
, so that the type signature for producerForRequest(:)
can be defined:
func producerForRequest(request: Request)
-> SignalProducer<Value, Error>
Transforms
Using producerForRequest(:)
alone requires response values to be interpreted as SessionType
declares. Shirley solves this issue by allowing sessions to be transformed into derived sessions. For example, let’s say we have User
and Post
types, and we want to create sessions that will process a JSON representation of each:
let baseSession = ...
// transform the session into one which produces JSON
// assume that we have a "JSON" type
let JSONSession = baseSession
.transform({ data in
if let json = JSON(data: data)
{
return SignalProducer(value: json)
}
else
{
return SignalProducer(error: Error.CouldNotParseJSON)
}
})
// transform the JSON session into one which produces Posts
let postSession = JSONSession
.transform({ json in
if let post = Post.parse(json)
{
return SignalProducer(value: post)
}
else
{
return SignalProducer(error: Error.CouldNotParseUser)
}
})
// transform the JSON session into one which produces Users
let userSession = JSONSession
.transform({ json in
if let user = User.parse(json)
{
return SignalProducer(value: user)
}
else
{
return SignalProducer(error: Error.CouldNotParseUser)
}
})
// process requests
postSession.producerForRequest(...)
.startWithNext({ post in
...
})
userSession.producerForRequest(...)
.startWithNext({ user in
...
})
This creates four sessions, all of which use baseSession
to execute their requests. The request type can also be transformed. Let’s assume that User
values have an identifier
, and we’d like to be able to request a user by its identifier:
let identifierSession = userSession
.transformRequest({ (identifier: String) in
...
})
identifierSession.producerForRequest("0")
.startWithNext({ user in
...
})
Integration with Foundation
Although Shirley is generic, it provides support for NSURLSession
, which is extended to conform to SessionType
. NSURLSession
accepts requests of type NSURLRequest
, produces values of type Message<NSURLResponse, NSData>
, and produces errors of type NSError
. Message
is a small container type provided by Shirley for the NSURLSession
extensions. Like Session
, messages are backed by a protocol and are generic, so they are not limited to use with NSURLSession
.
Additionally, some of the extensions to SessionType
are valuable when working with NSURLSession
:
HTTPSession()
will transformMessage<NSURLResponse, Body>
intoMessage<NSHTTPURLResponse, Body>
, failing with an error if the response is not of typeNSHTTPURLResponse
.JSONSession(:)
will transformMessage<Response, NSData>
intoMessage<Response, AnyObject>
, usingNSJSONSerialization
, failing with the error provided byNSJSONSerialization
if the data is not valid JSON.bodySession()
will transformMessage<Response, Body>
intoBody
, dropping the response value.
Of course, custom transforms can also be applied to NSURLSession
-derived sessions.
Installation
Shirley is available from Github, and it supports Carthage for dependency management. To install with Carthage, add this line to your Cartfile
:
github "natestedman/Shirley"
Shirley requires ReactiveCocoa 4.0.