Pass Info Between Operations With Fast

Comment

IOS Programming Swift 4 Programming

Introduction

In this article, we are going to see some approaches to pass the data between two Operations in Swift. To avoid losing the focus on this topic, I will not explain what is and how works an Operation. For this reason, you need a basic understanding of Operation to understand the next sections of the article.

I may write another article to explain the Operation if I see that you would be interested on it.

Happy Reading!

Contents

Getting Started

Before diving into these approaches, we need a scenario for our examples. I guess all of us made an application where we had to fetch the data from an API request and then parse the data received. For this reason, I think it’s quite familiar if we use a scenario where we have two Operations: one to fetch and one to parse the data.

We can start creating our two Operation classes:

FetchOperation

  1. The data fetched to send to ParseOperation.
  2. Saves the data received from an HTTP request.

For the sake of explanation, I skipped a real implementation since it would need an asynchronous operation. If you want to learn how to use an asynchronous operation, you can have a look at my gist.

ParseOperation

  1. The data received from FetchOperation.
  2. The dictionary created from the parsing of dataFetched.
  3. Checks if the data exists and then creates a dictionary from the data fetched.

For the sake of explanation, I kept both Operation implementations as plain as possible without caring of the lifecycle.

The last step is creating a handler class which will manage these operations with an OperationQueue:

  1. OperationQueue to run our Operations.
  2. Prepares our Operations setting the dependencies.
  3. Adds the Operations in the queue blocking the queue thread until it’s finished.

With these 3 classes, we are ready to start looking at the approaches to pass dataFetched from FetchOperation to ParseOperation.

Approaches

Internal dependency reference

The object Operation provides an array of its dependencies with the following property:

Thanks to this information, in ParseOperation we can have access to its dependency FetchOperation:

At this point, we can refactor the method main of ParseOperation to read dataFetched directly from its dependency:

This approach is the easiest since we don’t need any external helpers to inject dataFetched. To be honest, I don’t like this approach. I would prefer injecting the data from outside because ParseOperation wouldn’t have the responsibility to decide where to get the data.

Reference Wrapper

For this approach, we have to create a new class which will wrap fetchedData:

Then, we can inject this new wrapper in both operations. FetchOperation will use this wrapper to set the property dataFetched, whereas ParseOperation will read the value of dataFetched—previously set in FetchOperation.

We can change our FetchOperation to inject this wrapper and set its property once we receive the HTTP response:

  1. Injects DataWrapper and keeps an internal reference to use in main.
  2. Sets the wrapper property to be used in ParseOperation.

Then, we can change ParseOperation to read the wrapper property:

  1. Injects DataWrapper and keep an internal reference to use in main.
  2. Reads the wrapper property to parse it.

Finally, we can change the method start of Handler to use the new wrapper object:

To be honest, I don’t like also this approach. We cannot inject just the data but we must inject this wrapper—which may not have the data ready when we use it in ParseOperation.

Keep reading to learn better approaches.

Completion block

The object Operation provides a completion closure which is called once the Operation completes its task:

We can take advantage of this completion to pass the values between the two Operations:

Remember to use unowned for both operation objects otherwise you would create a retain cycle.

At this point, we can refactor the method start of Handler like this:

If you don’t set maxConcurrentOperationCount of OperationQueue to 1, parse would start without waiting the completion block of FetchOperation. It means that we would inject the data too late when the operation is already started. Instead, we must inject it before running ParseOperation.

I definitely prefer this approach rather than both Internal dependency reference and Reference Wrapper since we can inject dataFetched from outside.

Adapter operation

This approach is very similar to Completion block. Instead of using the completion block, we add a third operation which is called Adapter.

This new Operation has just a plain block where we can inject the data fetched inside ParseOperation like in Completion block:

At this point, we can refactor the method start of Handler like this:

  1. Sets new adapter operation with a trailing closure.
  2. The dependencies have been changed:
    • adapter needs fetch as dependency to start when we fetched the data.
    • parse needs adapter as dependency to inject the data fetched.
    • parse no longer needs fetch as dependency since adapter is in the middle.
  3. Adds adapter in the queue.

Thanks to this adapter operation, we don’t need to care about maxConcurrentOperationCount of OperationQueue like inCompletion block. We can leave its default value—OperationQueue.defaultMaxConcurrentOperationCount.

Leave a Reply

Your email address will not be published. Required fields are marked *