Swift Code Cleaning Up: Part 1

Recently I tried to implement a few new features for Leetcoder, then realized the code was getting messier and messier. So I decided to clean it up a little bit before adding new stuff.

When I first started building the app, I was just learning Swift and basically structured the app according to a few tutorials.

Horrible. I know.

Now the controllers in the app are super bloated since I put almost everything in them. Gradually, it’s getting harder and harder to make even a small change.

Here are the things I did so far.

Put String Literals in a Constant File

Using string literals everywhere in your code in never a good thing and unfortunately I found cell ids, storyboard ids, image names and even urls abundant in various files.

var cell = tableView.dequeueReusableCell(withIdentifier: "goodCell", for: indexPath) as? GoodCell

Shameful. 😬

So I cleaned them up and put them into a file named constants.swift. This file contains several structs. Each struct contains static string fields that are related. For example, I used a struct to store all Storyboard IDs and another to store all image names.

This was a small and easy change, but hunting down all the strings can be a bit time consuming.

After this change, I already felt a lot better about my code and, especially, myself.

Use Extensions to Simplify Controllers

Extension in Swift is awesome and using them to implement protocol methods is just great.

Quite a few controllers in my app use a table to display data, so I created extensions for them that implement UITableViewDelegate and UITableViewDataSource methods. Similarly I created extensions for controllers that uses WKWebView to implement WKUIDelegate and WKNavigationDelegate methods.

extension AwesomeController {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// other methods

Afterwards, the controllers became much smaller and they became easier to understand conceptually.

Use Protocol Extension to Provide Default Protocol Implementation

From Apple’s docs:

You can use protocol extensions to provide a default implementation to any method or computed property requirement of that protocol. If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension.

Several controllers in my app have a method that’s almost identical. So I created a protocol out of it

protocol MyAwesomeProtocol {
func awesomeFunc()

Then I provided a default implementation for it via a protocol extension:

extension MyAwesomeProtocol where Self: UIViewController {
func awesomeFunc() {
// default implementation

The reason I didn’t simply use an extension is that awesomeFunc aren’t exactly the same for all of my controllers. And for controllers that needed a different behaviour, I just overrode awesomeFunc in it.

My refactoring is still on-going and I’ll write more about it in the future.

Resources I found helpful

Show your support

Clapping shows how much you appreciated Jing’s story.