The (Dis)Advantages of Swift’s Flexibility

January 13, 2025
Swift Programming Language Logo
Over the past 3–4 years of learning and writing Swift code, I’ve had an interesting experience with the language. During this time, I have followed many online courses, started work on an iOS app that I plan to release on the App Store, and even created a prototype and internal tool for one of SEP’s clients. One thing I have grown to appreciate about Swift is its flexibility. Because Swift is so flexible, it adapts to whatever the task requires. While flexibility is a huge plus, it comes with certain disadvantages that can hinder code cleanliness and make it more challenging to build a well-designed codebase.

1️⃣ No Enforced Architecture Pattern

When Apple introduced SwiftUI—Swift’s UI framework—it gave developers alternatives to the traditional MVC pattern associated with UIKit. Gone were the days of creating your UI in a storyboard file and then creating a ViewController class to update the view behind the scenes. With SwiftUI and its declarative syntax, you can create your UI entirely in code, and the logic driving that UI can live right alongside it. This approach drastically reduces the time spent on building the app’s interface and shifts focus toward implementing the underlying logic.
However, with this new lack of enforced architecture, learning Swift has become something of a modern Wild West. In many tutorials and courses, you’ll find every architecture pattern imaginable, and there doesn’t seem to be a consensus in the Swift community on what the “default” pattern should be. Personally, I tend to lean toward MVVM, as it enforces clearer separation between the view and the logic that updates it. Others prefer sticking to the traditional MVC pattern, which remains prevalent when using UIKit.

2️⃣ Mocking for Unit Tests Gets Tricky

In 2024, Swift Testing was introduced as a more Swiftful counterpart to the XCTest framework for Swift. While Swift Testing brought several quality-of-life improvements—such as test decorators, parameterized tests, and simplified assertions—its main downfall, and XCTest’s for that matter, is the inability to properly mock services. Yes, you might point out Cuckoo or Mockingbird for mocking. While these packages are valuable tools, but they seem to just come short due to Swift’s limitations. Because true mocks for unit testing are unavailable, developers usually create class fakes, which end up mimicking much of what they intended to test. In my opinion, the gold standard for mocking is Moq in C# or Jest in JavaScript. Both can create mocks at compile time, and those mocks can be easily used within unit tests.

3️⃣ Codebases Can Be… Inconsistent

Earlier, I mentioned how Swift codebases can vary widely from one another, but inconsistency can also occur within the same codebase. Swift offers numerous ways to complete similar tasks, which can lead to drastically different implementations even with a team. This lack of consistency adds to the difficulty of learning the language and forming good coding habits. For example, there are many different approaches to dependency injection. The two leading approaches tend to be using the built-in Environment framework, or choosing to use a third-party package like Factory. Both options are completely valid with differing syntax and structure, but if someone encounters so many options early in their learning, it may cause some confusion.

💭 Final Thoughts

While Swift’s flexibility is undoubtedly a major advantage compared to other languages. It offers the adaptability to rapidly create applications without much friction. On the other hand, with this amount of flexibility, also comes the potential for the language to be a hindrance to building a polished and testable codebase in production.