Matt Vague
Feb 9, 2015 · 3 min read

Writing async code in Swift is (mostly) a joyful experience. Things can get hairy though when writing something like an API Client with functions that accept multiple closure arguments, which themselves accept multiple arguments.

Example:

class ApiClient {  // ....  func getUsers(success: ((result: AnyObject, operation: AFHTTPRequestOperation) -> Void)? = nil,
error: ((error: NSError, operation: AFHTTPRequestOperation) -> Bool)? = nil,
finished: (() -> Void)? = nil) {
// Do stuff
}
func getUser(user: User, success: ((result: AnyObject, operation: AFHTTPRequestOperation) -> Void)? = nil,
error: ((error: NSError, operation: AFHTTPRequestOperation) -> Bool)? = nil,
finished: (() -> Void)? = nil) {
// Do stuff
}
func getInvitations(success: ((result: AnyObject, operation: AFHTTPRequestOperation) -> Void)? = nil,
error: ((error: NSError, operation: AFHTTPRequestOperation) -> Bool)? = nil,
finished: (() -> Void)? = nil) {
// Do stuff
}
// ....}

Not only is this painful to read, there’s also a lot of needless repetition leading to a high probability of copy-paste-itis. There is a better way!

Introducing typealias

Luckily, Swift’s got our backs here with a handy feature called typealias.

From the Swift Docs:

A type alias declaration introduces a named alias of an existing type into your program. Type alias declarations are declared using the keyword typealias and have the following form:

typealias name = existing type

After a type alias is declared, the aliased name can be used instead of the existing type everywhere in your program. The existing type can be a named type or a compound type. Type aliases do not create new types; they simply allow a name to refer to an existing type.

Functions and closures in Swift have a type (consisting of the function’s parameter types and return type) and so can be aliased like anything else:

typealias MyFunctionDefinition = (Integer, String) -> Void

Applying that to our example API client, we end up with this:

class ApiClient {  // ....  typealias SuccessHandler = (result: AnyObject, operation: AFHTTPRequestOperation)
-> Void
typealias ErrorHandler = (error: NSError, operation: AFHTTPRequestOperation)
-> Void
typealias FinishedHandler = () -> Void
func getUsers(success: (SuccessHandler)? = nil,
error: (ErrorHandler)? = nil,
finished: (FinishedHandler)? = nil) {
// Do stuff
}
func getUsers(success: (SuccessHandler)? = nil,
error: (ErrorHandler)? = nil,
finished: (FinishedHandler)? = nil) {
// Do stuff
}
func getInvitations(user: User,
success: (SuccessHandler)? = nil,
error: (ErrorHandler)? = nil,
finished: (FinishedHandler)? = nil) {
// Do stuff
}
// ....}

To me those function definitions are now a lot easier to read, and undeniably easier to write. Best of all, this is a drop-in solution. Nothing else about your functions need change and everything can be called as before.

ApiClient.sharedInstance.getUsers(success: { result, operation in
// Do stuff
}, error { error, operation in
// Do stuff
}, finished {
// Do stuff
})

Disclaimer: This pattern works well where you have a few closure types used by a majority of a class’s functions, but I would be cautious when you have many closure types used by only a few functions. Doing so may introduce indirection into your code without much benefit.

All patterns have trade-offs, and in this case we’re trading increased indirection for decreased duplication. There are no magic bullets!

Thoughts? Feedback? Disagreements? Twitter me: @mattvague

Swift Programming

The Swift Programming Language

    Matt Vague

    Written by

    Rooby, Javascript and iOS developer

    Swift Programming

    The Swift Programming Language

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade