Generalizing Swift Code for Reusability

KD Knowledge Diet
2 min readJun 25, 2024

--

To effectively generalize Swift code, consider these key steps and strategies:

1) Start Specific: Begin by creating a specific and well-defined implementation to solve a particular problem. Clear APIs and separation of concerns are essential.

2) Identify Generalizable Components: Identify parts of your specific code that can be generalized. Look for common patterns or functionalities that can be reused.

3) Build a Generic Foundation: Transform your code into a generic foundation that can handle different data types. For instance, create a generic loader that works with any Decodable model.

class ModelLoader<Model: Decodable> {
typealias Handler = (Result<Model, Error>) -> Void

private let networking: Networking
private let endpoint: (UUID) -> Endpoint
private var cache = [UUID: Model]()

init(networking: Networking, endpoint: @escaping (UUID) -> Endpoint) {
self.networking = networking
self.endpoint = endpoint
}
}

4) Generalize Core Logic: Ensure that the core logic of your code is generic and applicable across various scenarios. Tasks like loading, caching, and decoding data are general and reusable.

5) Create Domain-Specific Conveniences: To maintain clarity, establish type aliases and convenience methods that provide domain-specific APIs. This bridges the gap between generic code and specific use cases.

typealias ProductLoader = ModelLoader<Product>
typealias UserLoader = ModelLoader<User>

extension ProductLoader {
convenience init(networking: Networking) {
self.init(networking: networking, endpoint: Endpoint.product)
}
}

6) Share Abstractions: Leverage your generic codebase to build shared abstractions that address common problems. Extend the generic code with specific functionalities when necessary. For instance, add methods to load multiple models in parallel or domain-specific loading methods.

extension ModelLoader {
func loadModels<S: Sequence>(
withIDs ids: S,
then handler: @escaping MultiHandler
) where S.Element == UUID {
// Load multiple models in parallel
...
}
}

extension ModelLoader where Model == Product {
func loadProducts(in bundle: Product.Bundle, then handler: @escaping MultiHandler) {
// Load products with specific domain logic
...
}
}

7) Use Domain-Specific Types When Appropriate: Recognize when domain-specific code is necessary and avoid overgeneralization. Create dedicated types or functions for tasks that are inherently domain-specific.

In conclusion, generalizing Swift code can promote code reuse and reduce duplication. It also enables the creation of powerful shared abstractions. Strive for the right balance between generality and specificity to maintain code clarity and simplicity.

--

--

KD Knowledge Diet

Software Engineer, Mobile Developer living in Seoul. I hate people using difficult words. Why not using simple words? Keep It Simple Stupid!