A lot of things have happened since the last time I wrote a post which seems to be around August 2018 here. The post was inspired from a Swift class I was teaching. At the time it was Swift 4.2 and now we have Swift 5.3. During this time, I worked for a few organizations further developing their apps. The organisations are quite large with user base that spans a large number in 6 digits. However since the code was written over a period of time by several developers, there are interesting artifacts that can be found in the code.
Problems with these codebase
There are several issues that these codebases have had, some of them are listed below
1. There is a poor CI/CD pipeline
2. There are far too many files that get compiled everytime there is a change
3. The codebase is written by Java developers in swift or devs that just got started with swift
4. There is no consistency in terms of using enums or structures or classes
5. There are extensions and overridden methods on extensions, like subclassing
How to fix these
While many of these issues can be fixed easily, like setting up a CI/CD pipeline. Others are a bit more complex to solve and require a lot more. So let’s see how to solve some of these.
#2. Too many files that get compiled
This is relatively easier to solve and fix. If the issue is with Pods (some projects have literally more pods than classes in the project) then one way could be to use pre-compiled binaries. This saves time by not compiling them when a project is being compiled/built. Proceed with caution on this one as if you commit the binaries to your repo, it will suddenly grow exponentially and can reach greater than 1 GB in size.
The other way is to separate the files into frameworks, so that only the frameworks that change are compiled
#4. There is no consistency
This one was also easy to fix, introducing a linter fixed a great deal of problems and also highlighted the casualness employed by earlier developers.
There are many interesting facets that are uncovered when using a linter. Some developers create a rule file and disable most of the rules so that the linter would not produce any warnings. The most common ones are the size of functions/code and the size of variable names. These are mostly disabled.
#5. There are extensions and overridden methods
This is perhaps less common amongst the points above. However I have seen a codebase where the developers have tried to use extensions as subclasses.
#3. The codebase is written by Java developers
This is something that cannot be changed. The code smells shall remain depending on the developers that work on the project, each has their own style. There are some that while writing swift native code end up adding portions of React, Redux, write loops and statements like Java code, mainly because there is a new move towards full stack developers and a lot of devs that come from developing backend systems generally write them in C# (DotNet) or Java (Spring Boot) and thereby the code smell.
Refactoring
Given all of these issues, the way to fix some of them is to rewrite the code and that’s where the actual problem comes in. Generally a majority of developers look for code that does what they want, and that quick fix is all that they want. Sites that offer these little bytes of code are popular, after all it provides something that they are looking for. Because majority of the code written is Copy Paste code it can have blocks that work but overall the code does not flow. It does not read well, it is more like a collection of different chairs at the same dinner table.
Show me the code
If you are also here for the code and not the commentary around it then lets get down to the first part of this.
let’s consider the code where we have a class/structure and then to add sample data to this class/structure
struct Dessert { let name: String let calories: Int }
and then somewhere in the code we can create some sample desserts like
let sampleDesserts = [ Dessert(name: "Lemon Tart", calories: 300), Dessert(name: "Sponge Cake", calories: 400), Dessert(name: "Jelly", calories: 50) ]
However this is the older way and the most common way, I personally prefer the Swifty way which is to add the same to the struct as static variables as
extension Dessert { static var lemonTart = Dessert(name: "Lemon Tart", calories: 300) static var spongeCake = Dessert(name: "Sponge Cake", calories: 400) static var jelly = Dessert(name: "Jelly", calories: 50) }
and then we can simply create the array of desserts as
let sampleDesserts: [Dessert] = [ .lemonTart, .spongeCake, .jelly]
This was it is more readable and cleaner and most importantly Swifty code.
In the next article we shall look at some other code and look at how we can re-write it or refactor it to make it better.