Atelier Clockwork

Yet More Operations

Solving Problems From Past Projects

A previous project involved having to assemble credentials from multiple sources across several servers in a particular to successfully "log in". It wasn't ideal, and long term there's still hope of encapsulating much more of that into fewer API calls, but it meant that the services layer of the project involved code that started with some simple semaphore locking, and eventually evolved into something using NSOperation to control dependancies and order of execution.

This got me started on trying to find a better way to encapsulate network requests in an NSOperation. I was close to a solid solution before Forward Swift, and Shruti Malugu gave a talk about NSOperation that gave me a few more concepts that helped me improve the base asynchronous operation class backing my network operations.

For brevity, I'm showing off the public interfaces for the files, not the full implementations, and anyone interested in the all of the code, check it out and comment on github.

public enum Result<T> {

    case error(Error)

    case success(T)

    public var value: T? { get }
}

public protocol ResultParsing {

    associatedtype ParsedResult

    public func parseResult(data: Data?, response: URLResponse?, error: NSError?) -> ResultParsing.Result<ParsedResult>

    public var url: URL { get }

    public var urlSession: URLSession { get }
}

Parsing network requests to a result required creating a protocol for all of the shared concepts that a network loading operation would need, so a URL, a URLSession to tie in to, and a function that can parse the data from the completion handler for the URLSessionDataTask.

public class DataSessionOperation<Query : ResultParsing> : AsynchronousOperation {

    private(set) public var result: Result<Query.ParsedResult>?

    public init(query: Query)

}

That DataSessionOperation itself uses KVO behind the scenes to link itself to the state of the underlying data task, but none of the internals are exposed, and once the data task finishes, the query's parseResult function is called and there's some sort of value in the result of the operation. This means the a follow up operation can be dispatched to do useful things with the result, like displaying on the main thread, or storing the data.

My next steps are to add AlamoFire support, and to include a broader set of configuration options in the ResultParsing protocol, ideally I'd like to see most of the setup for my services layer take place in structs that implement ResultParsing. After that, I have several other types of transactions that I'd like to wrap up in operations, and some ideas for infix operators to add some syntactic sugar.