It\'s possible to add extensions to existing Swift object types using extensions, as described in the language specification.
As a result, it\'s possible to create exten
Most examples I have seen mimic the Objective-C approach. The example extension above would be:
String+UTF8Data.swift
The advantages are that the naming convention makes it easy to understand that it is an extension, and which Class is being extended.
The problem with using Extensions.swift
or even StringExtensions.swift
is that it's not possible to infer the purpose of the file by its name without looking at its contents.
Using xxxable.swift
approach as used by Java works okay for protocols or extensions that only define methods. But again, the example above defines an attribute so that UTF8Dataable.swift
doesn't make much grammatical sense.
I prefer StringExtensions.swift
until I added too much things to split the file into something like String+utf8Data.swift
and String+Encrypt.swift
.
One more thing, to combine similar files into one will make your building more faster. Refer to Optimizing-Swift-Build-Times
Rather than adding my comments all over the place, I'm surfacing them all here in one answer.
Personally, I take a hybrid approach that gives both good usability and clarity, while also not cluttering up the API surface area for the object that I'm extending.
For instance, anything that makes sense to be available to any string would go in StringExtensions.swift
such as trimRight()
and removeBlankLines()
.
However, if I had an extension function such as formatAsAccountNumber()
it would not go in that file because 'Account Number' is not something that would naturally apply to any/all strings and only makes sense in the context of accounts. In that case, I would create a file called Strings+AccountFormatting.swift
or maybe even Strings+CustomFormatting.swift
with a formatAsAccountNumber()
function if there are several types/ways to actually format it.
Actually, in that last example, I actively dissuade my team from using extensions like that in the first place, and would instead encourage something like AccountNumberFormatter.format(String)
instead as that doesn't touch the String
API surface area at all, as it shouldn't. The exception would be if you defined that extension in the same file where it's used, but then it wouldn't have it's own filename anyway.
If you have a team-agreed set of common and miscellaneous enhancements, lumping them together as an Extensions.swift works as Keep-It-Simple first level solution. However, as your complexity grows, or the extensions become more involved, a hierarchy is needed to encapsulate the complexity. In such circumstances I recommend the following practice with an example.
I had a class which talks to my back-end, called Server
. It started to grow bigger to cover two different target apps. Some people like a large file but just logically split up with extensions. My preference is to keep each file relatively short so I chose the following solution. Server
originally conformed to CloudAdapterProtocol
and implemented all its methods. What I did was to turn the protocol into a hierarchy, by making it refer to subordinate protocols:
protocol CloudAdapterProtocol: ReggyCloudProtocol, ProReggyCloudProtocol {
var server: CloudServer {
get set
}
func getServerApiVersion(handler: @escaping (String?, Error?) -> Swift.Void)
}
In Server.swift
I have
import Foundation
import UIKit
import Alamofire
import AlamofireImage
class Server: CloudAdapterProtocol {
.
.
func getServerApiVersion(handler: @escaping (String?, Error?) -> Swift.Void) {
.
.
}
Server.swift
then just implements the core server API for setting the server and getting the API version. The real work is split into two files:
Server_ReggyCloudProtocol.swift
Server_ProReggyCloudProtocol.swift
These implement the respective protocols.
It means you need to have import declarations in the other files (for Alamofire in this example) but its a clean solution in terms of segregating interfaces in my view.
I think this approach works equally well with externally specified classes as well as your own.
I prefer having a +
to underline the fact it contains extensions :
String+Extensions.swift
And if the file gets too big, you can then split it for each purpose :
String+UTF8Data.swift
String+Encrypt.swift
There is no Swift convention. Keep it simple:
StringExtensions.swift
I create one file for each class I'm extending. If you use a single file for all extensions, it will quickly become a jungle.