What is the "official" ruling on organizing code by splitting large files into Extensions?

The problem: Some class etc. definitions cannot help but grow very large, into thousands of lines. It can feel intimidating, annoying and wearisome to scroll through everything when you just need to focus on a specific subset of functionality.

Example: Take a class for scenes in a cross-platform game. It has tons of methods and properties for each category of functionality: setting up the graphics engine, handling physics, managing audio and music, processing touch input for iOS, keyboard input for macOS, and so on.

It really doesn’t make sense to lump them all in one big file. There just is no way to group everything nicely; you have to put physics properties at the top, next to the input properties, far away from the physics-handling methods. Or you have to put physics properties next to the physics methods, in the middle of the file. You can’t always break such a class into multiple classes either, without incurring performance penalties or rewriting everything (because all those things are the job of the scene, so everything else, including the operating system, sends them to the scene, like input events.)

Ideal world: I just want to be able to ⌘` to cycle through windows (which may be on different screens or spaces) and just see one set of related properties and methods at a time. When I want to update just the iOS-specific methods, I don’t want to come across macOS-specific stuff.

Xcode doesn’t really have a good solution for that; // MARK: - doesn’t help much in a menu with 100s of items, taller than a 5K screen. Filtering the list by typing some text or using ⌘F every time is cumbersome as well, and doesn’t always work, because of similar names, or even duplicate names in case of platform-specific code surrounded by #if os(etc).

Current workarounds: Extensions. See https://twitter.com/johnsundell/status/897186531592556545

But using the semantics of an “extension” for some simple code organization feels kludgy and seems bug-prone. See: https://github.com/realm/SwiftLint/issues/1767

Take the previous example of a game scene. A touch event is like override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { ... }

But wait, the Swift manual says “Extensions can add new functionality to a type, but they cannot override existing functionality. and yet, override-ing a method like that in an extension compiles (and as far as I can tell, runs) happily.

Is the manual wrong? Are we not supposed to be using extensions to implement API-event-handling methods like that? The cross-platform game template in Xcode also uses extensions like that, for iOS/macOS input handling, but in the same file (so it defeats the desired ideal behavior above.)

Actually, no. Overriding in extensions is only supported for @objc declarations. As all the Cocoa (Touch) classes and their methods are @objc, it’s safe to override them in extensions, but not for native Swift methods.

1 Like

I don’t know, but imho it definitely makes sense to forbid overriding methods that aren’t declared in the supertype.
Banning it completely would be ok for me as well — but I generally think that extensions are often misused, and that spltting protocol conformances into separate extensions is cargo cult programming without proven benefit in most cases :man_shrugging:.

I’d rather try to split functionality into separate types. If this isn’t possible due to performance issues, I’d consider that to be a flaw in either Swift or the general design.
Of course, that’s no cure for your problem — but having to deal with bad code is common among all coders, so at least you are not alone ;-)

1 Like

There’s not always a clean solution to program design, especially if performance is a priority. However, it seems that in the example you give, the delegate pattern would work well. With good optimizations, performance should be close enough, while still allowing code to be grouped logically. Ultimately, it comes down to a tradeoff between raw performance and good code architecture. You can’t always have both.

1 Like

I don’t think you’re going to get a good answer to this question because it’s so opinion-based. personally I never put things in extensions unless it’s to add a peripheral conformance that’s unimportant to the functionality of the program (like CustomDebugStringConvertible) but then again, I also split my types before they ever get that many methods in the first place

1 Like

Seconding @Avi’s suggestion, simple composition and delegates. Works great.

Terms of Service

Privacy Policy

Cookie Policy