Swift typealias to the rescue

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

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.