Adding cases to in-built/framework Enums in Swift

Sumeet Kumar Gupta
The Startup
Published in
4 min readJul 24, 2019

To better understand how we can achieve this let us take a problem/use case where we might need something like this.

So let’s say, there is this screen in your application which needs to make an API call, the response from which is to be used to show the contents on the screen. Now the requirement is that you need to add support for caching API response of this endpoint used in your app.

The Problem

  1. When the screen loads
  2. An API call is requested by the ViewController, if the response for the API is available in local cache return the response immediately. In addition to that a fetch needs to happen anyway to update this cache and if the new response is different from the previous cache reload the view.
  3. As a side effect if the API fails we let the data from the local cache be available on the screen.

Now there are some obvious questions which could come to mind like:

  1. Why not just show a loading indicator?-
  2. Doesn’t the cachePolicy available in URLRequest already allow us to do that?

Well, to answer the first question:

a) In our screen, we do not want user to drop-off waiting for the screen to show up some content(the screen might not be time sensitive content).

b) That would defeat the purpose of our example 😝.

Let us pursue the second option and see how that works out.

Cool, we know that URLRequest has a cachePolicy which can be used. The cache policies available with URLRequest.CachePolicy are as follows(refer documentation):

public enum CachePolicy: UInt {
case useProtocolCachePolicy
case reloadIgnoringLocalCacheData
case reloadIgnoringLocalAndRemoteCacheData
public static var reloadIgnoringCacheData: NSURLRequest.CachePolicy { get }
case returnCacheDataElseLoad
case returnCacheDataDontLoad
case reloadRevalidatingCacheData
}

This is great, but limited and would not solve for our use case since what we want to do is get an implementation where our local cached data if available should be returned immediately, meanwhile also making the API call to get new data. Basically, we need a case like returnCacheDataAndFetch.

One of the way to achieve this is somehow adding the functionality for this new case(and may be more cases going forward) onto the URLRequest.CachePolicy.

I hope you are not thinking “Dude, just go to the enum definition and add a case.”

Well evidently we can’t do that since the enum is defined inside the Foundation framework.

The Solution

To get around this we will wrap the URLRequest.CachePolicy enum around a custom one 😅. So let’s do that:

Fig 1: Definition of CustomCachePolicy

The implementation of the NetworkManager before adding caching support was as follows:

Fig 2: Definition of NetworkManager without support for caching

Take a minute to understand what is happening in this class. We have a NetworkManager class with a method fetchData(fromURL:atCompletionSuccess:atCompletionFailure:), which we can be normally use to make an API call.

Let us now extend the functionality of the NetworkManager to support caching(using `NSURLRequest.CachePolicy` and our custom policy). For that we will add another function which would take the cachePolicy which we will then handle.

Fig 3: Implementation of the extension for supporting CustomCachePolicies

So, what we did is that we extended the NetworkManager to provide an interface for accepting the CustomCachePolicy which we can now use from inside the app. By doing this we have ensured that, if we intend to use the default caching mechanism, the support is still available but now we can add custom cache policies and handle them in our extension function.

If you noticed the definition of the function in the extension is fetchData(fromURL:cachePolicy:cacheMissResponse:atCompletionSuccess:atCompletionFailure:). There is a parameter cacheMissResponse. We added this as we want to provide a provision for the method to return a default response if the caller of the function wants in case a local cache is not available, you can see the usage of this parameter in the line 30 of Fig 3.

There are different ways we could have implemented this as well, e.g. using delegates. The method hasResponseChanged(response1: response2:) helps us decide whether the response changed from the one available in cache and can also be written in more optimised ways, but we will not focus on them since they are beyond the scope of this post.

Conclusion

We have managed to add custom enum cases to a framework enum while maintaining and utilising the cases and the functionality of the already available enums.

I would love to hear if you know of better and/or other ways of achieving this.

P.S.: There are a lot of improvements which can be made to the code above in general, but I have tried to not focus on those details and tried to focus on the main problem the post intends to address. Also, this is my first blog post so please feel free to give me feedback on how I can improve the quality of my posts, would appreciate that 🙏.

--

--