# Encapsulate and Generalize in Swift

Whenever I use a 3rd-party dependency in my app, I try my best to `import` that dependency only **one single time** in the *entire* project. I also never use the actual dependency name in my variable names, class names, or any other names. In other words, I **encapsulate** and **generalize**.

Come along if you'd like to see what I mean. 😊

### Let's start with "this works" (and then we'll improve it)

(To be clear, the next few code snippets are not the final result. They'll be improved down below.) Here's an example "service" class whose job is to use APIs from AcmeSupport to implement customer support features directly in your app ("AcmeSupport" is a fake 3rd-party dependency name I thought of because I have Roadrunner cartoons on my mind).

```swift
import AcmeSupport

class AcmeSupportService {
    ...
    func fetchAcmeTickets() -> async [AcmeTicket] {
        // Call into the AcmeSupport API here
        ...
    }
}
```

As you can see, our method calls into Acme's API and returns an array of AcmeSupport's own type called `AcmeTicket`. It looks like we could use that type nicely, so we'll go ahead and use this service class in some *other* part of the app, like this:

```swift
import Foundation
import AcmeSupport

class DataLogicPlace {
    ...
    private let acmeService = AcmeSupportService()
    ...
    func loadAcmeTickets() async -> [AcmeTicket] {
        let acmeTickets = await acmeService.fetchAcmeTickets()
        // Here you can minpulate the `AcmeTicket` objects 
        // from the `acmeTickets` array
        ...
    }
    ...
}
```

It's possible you may want to do *other* things with the `AcmeTicket` type from the `acmeTickets` array result that *could* require you to `import AcmeSupport`. So the `DataLogicPlace` class imports it. And since we want to display these tickets in our UI, we can even use these `AcmeTicket` types right inside our `View`:

```swift
import SwiftUI
import AcmeSupport

struct AcmeSupportScreen: View {
    ...
    var body: some View {
        ...
        ForEach(acmeTickets) { acmeTicket in
            acmeTicketRowView(forAcmeTicket: acmeTicket)
        }
        ...
    }

    private func acmeTicketRowView(forAcmeTicket: AcmeTicket) -> some View {
        ...
    }
}
```

And for this `View` (i.e. "[Screen](https://scottsmithdev.com/screen-vs-view-in-swiftui)" 😉), we have to `import AcmeSupport` so we can access its `AcmeTicket` type to use as the argument type in our `acmeTicketRowView` method.

All of this code may already look great to you. And that's totally OK. This works. As planned though, we'll improve it all by encapsulating and generalizing. Readysetgo.

### Encapsulate

The goal here is to only `import AcmeSupport` once in the app so that there's only one file that depends on it, and the rest can compile happily without it. To do this, let's create our ***own*** version of AcmeSupport's type `AcmeTicket` and we'll name ours something *generalized* like "`SupportTicket`" (foreshadowing, anyone? 😄)

```swift
struct SupportTicket: Identifiable {
    ...
}
```

Cool! Let's use our new type right away so you can see the effect it will have!

```swift
import AcmeSupport

class AcmeSupportService {
    ...
    func fetchAcmeTickets() -> async [SupportTicket] { // 👀
        // Call into the AcmeSupport API here
        // ✅ THEN map THEIR type to our OWN type to return 😃
        ...
    }
}
```

Notice in the snippet that we're now returning an array of `SupportTicket` instead of AcmeSupport's type `AcmeTicket`. We do this by quickly mapping ***their*** type to our ***own*** type and walking away. This is really great, you know why? Because now all the other files in our app that were *previously* consuming an array of the `AcmeTicket` type will *now* be consuming an array of our very own `SupportTicket` type—which meeeeans all those files can immediately ***delete***`import AcmeSupport` because they won't need to access AcmeSupport's `AcmeTicket` type anymore! And just like that, we've successfully *encapsulated* the AcmeSupport dependency and its API calls into one single place in our app. Party time? ...Yeah. Party time. 🥳

### Generalize

Alright, in the original 3 code snippets, if you count the number of times the word "acme" appears in the code—excluding the `import` statements and the `AcmeTicket` type name—it comes out to around 15 times (and that's only in 3 tiny snippets representing 3 tiny files).

So the goal *now* is to ***generalize*** all of our naming (don't worry, I'm not talking about Swift generics 😅) so that it doesn't mention the name of the company, "Acme", anywhere. If we do this, the code instantly takes on a totally different feeling. Check it out:

```swift
import AcmeSupport // ⬅️ imported only once, right here

class SupportService {
    ...
    func fetchTickets() -> async [SupportTicket] {
        ...
    }
}
```

```swift
import Foundation

class DataLogicPlace {
    ...
    let service = SupportService()
    ...
    func loadTickets() async -> [SupportTicket] {
        let tickets = await service.fetchTickets()
        ...
    }
    ...
}
```

```swift
import SwiftUI

struct SupportScreen: View {
    ...
    var body: some View {
        ...
        ForEach(tickets) { ticket in
            rowView(forTicket: ticket)
        }
        ...
    }

    private func rowView(forTicket ticket: SupportTicket) -> some View {
        ...
    }
}
```

And would you look a that! We've successfully *generalized* our naming, bringing the number of times we see "acme" in our naming from ~15 all the way down to **zero**! All without compromising the readability of the code. In fact, I'd say the readability has improved! Mission: accomplished.

### Is this really necessary?

You decide. Over the years, I've seen lots of code where devs tightly couple 3rd-party dependency names and their types to the rest of the codebase, making it unnecessarily intertwined and dependent on some other thing/company. And then the Product team asks the devs to a/b test 2 different dependencies/companies, or even completely *replace* a dependency with a different one that offers the same functionality as the original but perhaps at a better rate. Well, in these situations, if your code looks like the original 3 snippets in this article, then you'll have a lot of naming-refactoring ahead of you, and likely a lot of logic-refactoring, and also type-replacing, like in that original `acmeTicketRowView` method above that depended on the 3rd-party `AcmeTicket` type.

So if we thoughtfully encapsulate and generalize from the get-go, then our code can be a lot more pleasant to work with; scaling will be easier, and we'll be more likely to code more confidently when we need to do something drastic like ***completely replace***`AcmeSupport` with `BeepBeepSupport`.

![Roadrunner cartoon character on a thin rock ledge jumping up while saying "meep meep!" then speeding away](https://cdn.hashnode.com/res/hashnode/image/upload/v1707810625709/00e4c44d-89ec-4aa9-ac13-82c57278ef79.gif align="center")

**"beep beep!"**

Come say "Hi!" on [Twitter](https://twitter.com/ScottSmithDev) or [Mastodon](https://mastodon.social/@ScottSmithDev)! Or don't. 😄

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707808245696/a0446bd5-7652-4391-a8ca-859273f842b8.gif align="center")
