Encapsulating highly reusable code into a static library not only decouples the code, but also improves its maintainability. My company’s iOS project was developed using modularity, where a lot of reusable code was packaged into static libraries.

Circular references to static libraries

Consider this scenario

A Framework references B Framework, and B Framework needs to use A service in A Framework. Unfortunately, the service is coupled to the A Framework, so to avoid reference loops, we need to refactor the A Framework and migrate the service to another C Framework.

I believe that in reality, no programmer is willing to take the risk of refactoring A Framework code and unit tests (I might as well have played more with that time).

Use Protocol to resolve circular references

Here I offer an idea to help you expose services in Framework A to Framework B without refactoring existing code.

The sample code can be downloaded at the Github link at the end of this article

The preparatory work

In our main project, we defined a Color service

protocol ColorLayer {
    func backgroundColor(a) -> UIColor
}

class ColorService: ColorLayer {
    func backgroundColor(a) -> UIColor {
        return .red
    }
}
Copy the code

Let’s create a new static library and name it BridgeTestFramework

In this static library, we have a piece of code that looks like this.

public class BridgeViewController: UIViewController {
    
    let colorService = ColorService(a)public override func viewDidLoad(a) {
        super.viewDidLoad()
        view.backgroundColor = colorService.backgroundColor()
    }
}
Copy the code

The compiler gives an error because the BridgeTestFramework is referenced by the main project, and the BridgeTestFramework cannot reference the main project.

inBridgeTestFrameworkDefined in the Protocol

Copy the Protocol of ColorService into the BridgeTestFramework. Don’t forget to minor change the name of the Protocol and change its access to public.

public protocol BridgeTestFrameworkColorLayer {
    func backgroundColor(a) -> UIColor
}
Copy the code

Now let’s refactor the BridgeViewController

public class BridgeViewController: UIViewController {
    
    let colorService: BridgeTestFrameworkColorLayer
    
    public init(colorService: BridgeTestFrameworkColorLayer) {
        self.colorService = colorService
        
        super.init(nibName: nil, bundle: nil)}required init? (coder aDecoder:NSCoder) {
        fatalError("init(coder:) has not been implemented")}public override func viewDidLoad(a) {
        super.viewDidLoad()
        view.backgroundColor = colorService.backgroundColor()
    }
}
Copy the code

Here we use the type of attribute colorService BridgeTestFrameworkColorLayer instead. Because BridgeTestFrameworkColorLayer and ColorService exposed by the interface is the same, so we can use normal backgroundColor () this method.

Implement Protocol in the main project

Then we go back to the main project code. Introduce the BridgeTestFramework for ColorService and extend ColorService.

import BridgeTestFramework

protocol ColorLayer {
    func backgroundColor(a) -> UIColor
}

class ColorService: ColorLayer.BridgeTestFrameworkColorLayer {
    func backgroundColor(a) -> UIColor {
        return .red
    }
}
Copy the code

Because ColorLayer BridgeTestFrameworkColorLayer defined by the interface is the same, so we don’t need to change the concrete implementation ColorService.

Using the Protocol

Let’s then try to use our refactored BridgeViewController in the main project.

class ViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        let bridgeVC = BridgeViewController(colorService: ColorService())
        present(bridgeVC, animated: true, completion: nil)}}Copy the code

Because ColorService BridgeTestFrameworkColorLayer the agreement is achieved, BridgeViewController can normally use the ColorService implementation of the backgroundColor() interface.

summary

In this small Demo we use Protocol to create a temporary bridge between two static libraries. Protocol allows us to share certain services between static libraries that already have dependent references without circular references.

But keep in mind that good design patterns are the ultimate solution to avoid such circular references. The Demo source code can be downloaded here.