The author | wave son dish

Question why

This article uses the YBImageBrowser[1] component as an example.

YBImageBrowser relies on SDWebImage, and there may be some dependency conflicts when integrating into a project using CocoaPods. Recently, the community raised several Issues and saw that such Issues were of high concern in Insights -> Traffic -> Popular Content, so we had to tackle them.

Strict version limits

In the iterative process of an open source component, it is good to ensure downward compatibility of the upper interface. In order to optimize the performance and control the memory, YBImageBrowser does not directly use its topmost interface, but uses the download module and cache module separately. The iterative upgrade of SDWebImage easily leads to the incompatibility of the author’s components, so it has been dependent like this before:

Spyware doctor ependency 'SDWebImage', '~ > 5.0.0'Copy the code

This has the advantage of limiting the version range sufficiently small to reduce the risk of component code errors due to changes in the SDWebImage interface. However, if SDWebImage is upgraded to 5.1.0, CocoaPods will treat it as a dependency conflict, regardless of the related API changes.

Other components rely on different versions of SDWebImage

When two components depend on different versions of the same component and the dependent versions do not overlap, for example:

A.dependency 'SDWebImage', '~> 4.0.0'B.dependency 'SDWebImage', '~> 5.0.0'Copy the code

Then the integration of A and B into the project will lead to dependency conflicts.

The solution

Integrating projects with CocoaPods is very convenient. Component users want to be able to integrate easily in any scenario and enjoy future component updates and optimizations. Obviously, the issues mentioned above can affect the ease of integration.

Vaguer version limits

Most of the time a large version of a component does not change the API, and for community-popular components we can count on backward compatibility, so relaxing the dependency version limit can cover more future versions (rule reference: PodSpec Dependency [2]) :

Spyware doctor ependency 'SDWebImage', '> = 5.0.0'Copy the code

Why not just remove the version limit?

Since YBImageBrowser 3.x was developed based on SDWebImage 5.0.0, the author can confirm that it is not compatible with pre-5.0.0 versions, so until future iterations of SDWebImage show API incompatibility, This limitation is “perfect” for all versions.

Avoid violent schemes that rely on conflict

When there are other components that rely on different versions of SDWebImage, the crude solution is as follows:

• Directly modify versions of SDWebImage that other components depend on.

• Manually import YBImageBrowser into the project and modify the code to fit the current SDWebImage version.

• The solution mentioned in one Issue: go to ~/.cocoapods/repos to find the YBImageBrowser folder and change the dependency on SDWebImage in the corresponding podspec.json file.

Obviously, the above scenarios are not very elegant, manual import projects will not enjoy the component’s update optimization, and modifying the local REPO information will be reset as the REPO list is updated.

An elegant solution to avoid dependency conflicts

Dependency conflicts are a problem that must be resolved, and the versioning constraints of other component dependencies can be treated as invariants, and the solution can be considered in terms of component production.

The goal is not only to satisfy some users to quickly integrate components, but also to ensure that some users can enjoy the future update and optimization of components on the premise of resolving dependency conflicts.

The answer is subspec, and here is the code for yBImageBrowser.podSpec (complete code [3]) :

s.subspec "Core" do |core|core.source_files = "YBImageBrowser/**/*.{h,m}"core.dependency 'SDWebImage', '> = 5.0.0 ends. Subspec "NOSD" do | core | core. Source_files = "YBImageBrowser / / *. * * {h, m}" core, exclude_files = "YBImageBrowser/WebImageMediator/YBIBDefaultWebImageMediator.{h,m}"endCopy the code

As a result, the user is free to choose whether or not to rely on SDWebImage, which looks something like this in the Podfile:

// Do not rely on SDWebImagepod 'YBImageBrowser' // do not rely on SDWebImagepod 'YBImageBrowser/NOSD'Copy the code

So how do you tell in your YBImageBrowser code if you rely on SDWebImage and provide a default implementation?

The first step is to design an abstract interface that does not depend on SDWebImage:

@protocol YBIBWebImageMediator <NSObject>// Download methode, caching methode, and so on.@endCopy the code

The second step is to define an attribute in ybImageBrowser.h that follows this interface:

@property (nonatomic, strong) id<YBIBWebImageMediator> webImageMediator;Copy the code

The third step is to implement a default mediator (this class relies on SDWebImage) :

@interface YBIBDefaultWebImageMediator : NSObject < YBIBWebImageMediator > @ end @ implementation YBIBDefaultWebImageMediator / / by SDWebImage API <YBIBWebImageMediator> protocol method @endCopy the code

The fourth step is to import and initialize the default mediator via conditional compilation in internal code:

#if __has_include("YBIBDefaultWebImageMediator.h")#import "YBIBDefaultWebImageMediator.h"#endif... #if __has_include("YBIBDefaultWebImageMediator.h")_webImageMediator = [YBIBDefaultWebImageMediator new]; #endifCopy the code

Step 5 in YBImageBrowser. Podspec also can see, in does not rely on SDWebImage integration mode ruled out two files: YBIBDefaultWebImageMediator. {h.m}.

Thus the goal was achieved:

• Fast integration with sdWebImage-dependent integration.

• Use an integration approach that does not rely on SDWebImage to avoid dependency conflicts in all cases, but note that this requires a self-implemented mediator that follows the

protocol.

These are the tips to avoid dependency conflict, I hope readers can give better suggestions or opinions 😁.

reference

[1]https://github.com/indulgeIn/YBImageBrowser [2]https://guides.cocoapods.org/syntax/podspec.html#dependency  [3]https://github.com/indulgeIn/YBImageBrowser/blob/master/YBImageBrowser.podspec


How can the iOS list interface be elegantly modular and dynamic
Get any thread call stack through Signal handling
IOS LLDB implements reverse debugging based on the PATCH instruction of the RAM
How does SwiftUI implement a “more menu”?