iCalvin.org

Forcing Names on Swift Extensions

Categories

Objective-C's categories syntax has traditionally been very helpful at managing the scope of your new methods. The reason is that in Objective-C you create categories on a class with an explicitly defined name, so if we wanted an category on NSURLSession to add some frequently used factory methods we'd define it like so

@interface NSURLSession (Factory)
+ (nonnull NSURLSession *)sessionWithTimeout:(nonnull TimeInterval timeout);
@end
@implementation NSURLSession (Factory)
+ (nonnull NSURLSession *)sessionWithTimeout:(nonnull TimeInterval timeout)
{
	// method impl
}
@end

The category name here does two things. First is the practical matter of matching the interface to the implementation, but it also clearly defines the scope of what's going on in the category such that anything not relating to factory methods clearly doesn't belong here. This typically is represented in the file names as well, so you might have a few different categories defined in separate files like so

NSURLSession+Factory.h
NSURLSession+Defaults.h
NSURLSession+JSONRequest.h

Of course you could also squeeze all of these categories into a generic file named NSURLSession+Categories.h but the file can still break apart the individual categories within itself.

This also has the benefit of allowing you to only import an individual category depending on what you need.

Extensions

Unlike Objective-C categories Swift extensions are unnamed. This means that the natural method of defining scope is gone and it is entirely up to the developer to make sure that extensions are well documented and organized. The language isn't going to hold your hand here. One approach I've seen many people taking to handle this is to continue writing Swift extensions in the same way as Objective-C categories, where each file has a single extension with an explicitely defined scope and is named as such.

URLSession+Factory.swift
URLSession+Defaults.swift
URLSession+JSONRequest.swift

Personally I think that this is leaning a bit too much on the legacy language of the platform for best standards, and Swift deserves it's own conventions around this, but I can't seem to find any documented standards around this. A lot of the advantages of this approach, such as being able to import just one extension and work within that scope, don't apply to Swift because of global availability within Modules.

I'd propose that the best way to manage this without leaning on the legacy of Objective-C would be to define separate, reasonably-scopped, and well documented extensions within a single file like so

// URLSessionExtensions.swift
/*
	MARK: Factory method extension
*/
extension URLSession {
	public class func createSession(timeout: TimeInterval!) -> URLSession! {
		// method impl
	}
}

/*
	MARK: Default Response Handler extension
*/
extension URLSession {
	// extension def
}

The advantages here are that we still are breaking apart our extensions by scope and capability, but we're not arbitrarily giving extensions implied names by dictating them in the file names.