How I refactor a Huge Singleton DataManager

Eunji Hwang
3 min readNov 18, 2023

--

While I was building my app, Coffee Pensieve , there were times when I thought, ‘I know this isn’t the best way, but….’ After putting my app on the App Store and coming back to my desk, I went through all my code again. It felt a bit overwhelming, especially when I looked at the big, complicated data manager — the part that needed fixing the most.

Situation

The tasks performed by the Data Manager included the following:

  • Fetching and saving data from the Network Manager.
  • Formatting data for the view.
  • Logic for calculating or combining with other data.
  • Returning the held data when requested from the view.

The reason I used the DataManager was that the same data was utilized in multiple views, and some views required presenting results calculated from multiple data. Therefore, I believe it would be beneficial to manage the data in one central location. When the DataManager already had the necessary data, I considered it more efficient to use the existing data instead of making an API request.

Problem

  • Logics were intricately connected in the Data Manager. After the release, when reviewing the project’s code and attempting to add new features or fix bugs, it was hard to figure out how data was processed and communicated with the network on each screen.
  • And unless it was data that rarely changed once created, and its value always needed to be up-to-date, there was no need for it to be stored in the DataManager.
  • In fact, storing it there caused confusion and led to situations where the latest state of the data became unclear, resulting in bugs.
  • The methods with distinct tasks, such as fetching, formatting, and returning data, were all placed in one location, making the overall code feel untidy.

Solution

  • I fetched data directly through each network manager without going through the DataManager.
  • I refactored the existing networking code that used completion handlers to handle it more cleanly using async/await. The code for processing data after receiving it became more readable and clear. Codes like Completion.success(()) this were almost eliminated.
  • I separated functions commonly used across multiple views from those specific to particular views.
  • If the method involved complex data modifications, I added parameters, returns, and examples

Result

  • It became clear how data is processed and communicated with the network on each screen.
  • Understanding the overall logic became more straightforward, reducing the burden of adding additional features.
  • While reviewing the code from scratch and refactoring it, I gained insights into features that could enhance usability. During this process, I also uncovered bugs that had gone unnoticed.
  • I learned that the development process doesn’t end with implementing features; it’s also crucial to make the code clean and maintainable for future reviews and work on it.
  • I came to better understand object-oriented programming, which had felt challenging for me. It prompted me to contemplate how to achieve better code architecture and write cleaner code.

--

--

Eunji Hwang

Be a maker, not just a talker🖌️ Share my journey and growth as a developer. iOS Developer. Writer. Contents Creator