[iOS] Decouple Enum From Module

Ting Yi Shih (Peter Shih)
2 min readAug 21, 2020

--

Our codebase have so many classes in the same target. We decided to move modules from the target. However we have classes coupled with enum, which is a little challenging.

For example, we have a custom tab controller, in which we represent the tabs by an enum type. This is convenient for us to easily identify which tab we are working on and navigation by enum type is pretty straight-forward. The tab controller looks like this:

// In the app targetenum TabType {
case tab1
case tab2
}
protocol TabControllerDelegate {
func didSelectTab(forType: TabType)
}
class TabController {
let delegate: TabControllerDelegate
init(delegate: TabControllerDelegate) {
self.delegate = delegate
}
func selectTab(forType type: TabType) {
delegate.didSelectTab(forType: type)
}
}

And our app just use it:

// In the app targetclass ViewController {
lazy var tabController = TabController(delegate: self)
}
extension ViewController: TabControllerDelegate {
func didSelectTab(forType type: TabType) {
}
}

All things seem to be good. Nonetheless, the problem occurs when it comes to modularization — that is, we intend to move TabController to a framework, while the usage stays in the app target. Why we do this? Some merits:

  • Modularization prevents TabController from being coupled with irrelevant types.
  • Modularization improves organization of our codebase.
  • Modularization reduces the change of long rebuild time because the dependency of TabController is limited to the module itself. The change to the app target will not trigger TabController to be rebuilt.

We may simply move enum TabType, protocol TabControllerDelegate, and class TabController to a framework. Does it sound easy? Not really. Although TabControllerDelegate and TabController is generic enough to make a framework, enum TabType isn’t. The enum is application specific usage. That is, tab controller should be a component serving any enum type but not limited to TabType. If we want to leave enum TabType to the app target, we need some effort to decouple enum TabType from TabControllerDelegate and TabController.

The savior: generic classes and protocol associatedtype!

// In the framework target TabKitprotocol TabControllerDelegate {
associatedtype T
func didSelectTab(forType: T)
}
class TabController<T, Delegate: TabControllerDelegate>
where T == Delegate.T {
let delegate: Delegate
init(delegate: Delegate) {
self.delegate = delegate
}
func selectedTab(forType type: T) {
delegate.didSelectTab(forType: type)
}
}

And we use it in the app target:

// In the app targetimport TabKitenum TabType {
case tab1
case tab2
}
class ViewController {
lazy var tabController = TabController(delegate: self)
}
extension ViewController: TabControllerDelegate {
func didSelectTab(forType type: TabType) {
}
}

Yes, we decoupled enum from the class! Hurray!

Hope this tip helps you modularize your codebase. If the tips are helpful, please give me a clap to let me know! :)

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Ting Yi Shih (Peter Shih)
Ting Yi Shih (Peter Shih)

Written by Ting Yi Shih (Peter Shih)

Love exploring an elegant pattern that forms robust, maintainable, and understandable coding style.

No responses yet

Write a response