“This is the fourth day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

In iOS development, being able to binary stable components would greatly reduce compile time during development. In the article quickly implementing Swift component binaries based on Cocaopods engineering, we describe how to achieve binary packing in one step using the Pods project and Shell scripts but requiring us to manually change the PodSpec files so that if they are added as dependencies to other projects, Cocoapods-binary is a plugin for cocoapods-Binary that allows you to pre-compile components. The project has not been maintained for more than two years, and there have been some minor bugs with the pods update. Based on the source code, I made a few changes to the plug-in and can have fun again. Before we get to the plug-in, let’s take a quick look at what happens after you type Pod Install.

Pod install

If you want to debug the Cocoapods project, check out the previous Ruby and Cocoapods article collection.

A picture is worth a thousand words when Pod Install is entered

  • 1, first check whether there isPodfileFile, and parse toPodfileobject
  • 2, preparation stage, install plug-in, callpre_installPhase plug-in
  • 3. Resolve dependencies through the currentPodfileThe files and the last onePofile.Lock.Manifest.LockCompare files to determine which files need to be updated.
  • 4. Download dependencies, update files that need to be changed, and execute themPodfileInside definedpre_installHook function.
  • 5, the integration stage, generate newPods.xcodeprojProject documentation and executionPodfileThe inside of thepost_installHook function.
  • 6, write a new oneLockfileInformation.
  • 7, performpost_installPhase of the plug-in and output installation information.

Cocoapods-binary is precompiled as a plug-in during the pre_install phase of the Pod project,

Cocoapods – binary workflow

Pre_install plug-in

In Cocoapods-Binary, the HookManager is used to register the execution timing of the plug-in

Pod::HooksManager.register('cocoapods-binary'.:pre_install) do |installer_context|

end
Copy the code

Main process

The main process is shown in the figure below

  • 1. Must be usedframeworkOf the form of theta, which is thetause_frameworks.
  • 2, inPodsCreate a folder named_PrebuildThe folder asPrecompiled sandbox.
  • 3. In the current environment, readPodfileFile and createPodfileObject.
  • 4, readPodflie.lockFile, createLockfileObject.
  • 5, createPrecompiled installer, the sandbox address isPrecompiled sandbox.
  • 6,PodfileandPodfile.lockTo get the changedpod_name.
  • 7, usePrecompiled installerwillpod_nameDownload the source code toPrecompiled sandboxAnd generate a new onePods.xcodeprojFile. Start compiling what needs updatingframework.
  • 8. Return to main project and continuepod installIn this step, fix the ones that require binariespodspecFile.

Parse custom parameters

In the plugin, there are two custom arguments :binary and all_binary! If you are not familiar with this area, please refer to my article Cocoapods Podfile.

createPodfileObject when passedmethod swizzlingTo hook:parse_inhibit_warningsMethod, get us inPodfileConfiguration options written to the file. Will need toprecompiledthepodTo an array.

old_method = instance_method(:parse_inhibit_warnings)
define_method(:parse_inhibit_warnings) do |name, requirements|
    variables = requirements
    parse_prebuild_framework(name, requirements)
    old_method.bind(self).(name, variables)
end
Copy the code

In Ruby, Method Swizzling is divided into three main steps:

  • 1, getparse_inhibit_warningsInstance method.
  • 2. Define a method with the same name.
  • 3. Call the original method.

Compare the Lockfile

Here,Podfle.lockandPrecompiled sandboxIn theManifest.lockIt’s the same. You can do one by contrastHashobject

<Pod::Installer::Analyzer::SpecsState:0x00007f83370c61a8 @added=#<Set: {}>, @deleted=#<Set: {}>, @changed=#<Set: {}>, @unchanged=#<Set: {"Alamofire", "SnapKit"}>>
Copy the code

It is clear which POD libraries have changed. If there are changes, install in the precompiled sandbox.

binary_installer.install!

At this stage, it is mainly inPrecompiled sandboxPull inThe framework source codeAnd modifyPods.xcodeprojFile,Manifest.lockSuccessful writingPrecompiled sandboxBy hookrun_plugins_post_install_hooksFunction,Precompiled sandboxIn the usexcodebuildCommand to compile each one that needs to be updatedpod_targetAnd will compile wellframeworkTo put toGeneratedFrameworksDirectory.

Go back to the main project and execute pod Install

Once compiled, it is back to the Pod Install process in the main project. Make Method Swizzling to the :resolve_dependencies Method and modify the pod_target that you want to change. Reference the compiled framework by modifying the vendored_frameworks, source_files, resource_bundles, and resources properties of the Pod::Specification object in memory.

Workflow summary

At each stage, the author’s thinking goes like this: 1. Install the source code and Pods.project into the precompiled sandbox. 2. With the pods. project, use XcodeBuild to compile the scheme that needs to be pre-compiled. 3. Skillfully use Method Swizzling, modify Pod::Specification object, complete binary reference and other work in the dependency analysis stage.

The existing problems in

1, :binary => true

In Ruby 2.6.8 P205 (2021-07-07 Revision 67951) [universal.x86_64-darwin21], using :binary => true is not valid and cannot be compiled into framework. It works in bug mode, but expires after release.

def set_prebuild_for_pod(pod_name, should_prebuild)
    Pod::UI.puts("pod_name: #{pod_name} prebuild:#{should_prebuild}")
    if should_prebuild == true
        @prebuild_framework_pod_names ||= []
        @prebuild_framework_pod_names.push pod_name
    else 
        @should_not_prebuild_framework_pod_names ||= []
        @should_not_prebuild_framework_pod_names.push pod_name
    end
end
Copy the code

In Ruby 2.6.8, @should_not_prebuild_framework_pod_names is equal to @should_not_prebuild_framework_pod_names. The final array to be precompiled is []

pod_name: SnapKit, should_prebuild:true
pod_name: SnapKit, should_prebuild:
Copy the code

2, POD update does not update the latest framework in time

FrameworkA depends on frameworkB. In Podfile, only frameworkA is introduced

target xxx do 
    pod "frameworkA", :binary => true
end
Copy the code

The latest frameworkB was not updated when a new version of frameworkB was available. For our own components, we expect updates when a new version is released.

Solution: In the method of detecting updates, read the latest manifest. lock file, read the compiled framework plist file, compare the two version numbers are consistent, if not, recompile.

cocoapods-binary-bel

In order to make full use of the plug-in, I made some modifications to the source code according to the actual problems, and added plist version verification. Use the same method as cocoapods-binary. Github link to cocoapods-binary-bel

Because it is not published to the remote GEM library, you need to download it to the local PC for local compilation and installation

sudo gem build cocoapods-binary-bel.gemspec && sudo gem install cocoapods-binary-bel-0.4.8.gem
Copy the code

The local installation is successful if the following information is displayed:

Successfully built RubyGem Name: cocoapods-binary-bel Version: 0.4.8 File: Gem Successfully installed cocoapods-binary-bel-0.4.8 Parsing for Cocoapods-binary-bel-0.4.8 Done installing documentation for cocoapods-binary-bel after 0 seconds 1 gem installedCopy the code

If you feel that there is a harvest please follow the way to a love three even: 👍 : a praise to encourage. 🌟 : Collect articles, easy to look back! . 💬 : Comment exchange, mutual progress! .