I like the Swift way of trying to eliminate unintended consequences wherever possible. Whenever there is a change to the language that deprecates something I have been doing for years, it forces me to rethink what I consider to be best practice. The elimination of ++,– was the most recent time this happened.
One area where I am potentially seeing a huge potential for unintended consequences are extensions that are automatically included from external files. If I extended lets say UIView somewhere, and try to reuse code relying on said extension in a new project, I might be scratching my head for a while when my code is not compiling, and then I have to hunt for the file that introduced that extension.
My question is, what are best practices when it comes to extensions? Should each extension live in its own file? Should I group them by type?
One reason I am asking is thinking that in future iterations Swift might require implicit import statements for extensions to avoid the situation I described above. Is anyone aware of anything ruminating in the Swift community?
Future iterations of Swift could do many things but I don’t see anything on the 3.0 roadmap (https://github.com/apple/swift-evolution) which suggests you should worry about per-file imports. Swift imports are a package rather than file level concern and that seems unlikely to change.
It sounds like your current confusion stems from reusing extensions by copying use of them into the package of each app. The implicit dependency of that code on an extension in the same package is then not obvious. You might resolve this by moving reusable extensions into their own package. Use of those extensions would then require an import of the package containing the extension which might make it both a more obvious dependency and easier to locate when it cannot be fulfilled in your new app.
If you’re making use of a lot of reusable extensions you might want to consider that “Swift extensions do not have names”. As a result if you have two extensions introduce the same method on a type you may be unable to use both of those extensions’ modules. Instead you may want to:
- Define a protocol for the behavior your want to add.
- Use a protocol extension to provide your implementation.
- Use an extension to have the type you want to extend adopt this protocol.
You can then disambiguate a type which adopts multiple protocols which define methods with the same signature (see https://forums.developer.apple.com/thread/5812)
e.g. if I had protocols
BackgroundRadialGradientDisplayable which both define
func setBackgroundGradientColors(start: UIColor, end: UIColor) and
UIView has been extended to conform to both protocols then I could use
(view as BackgroundGradientDisplayable).setBackgroundGradientColors(start: color1 end: color2) to call a specific protocol’s implementation. If I were to just define two
UIView extensions I could not import both into the same package.