Before we start, let’s make it clear that we want to modify cocoapods-Binary to support server-side caching so that we can compile it in one place and use Pods Lib Dependencies everywhere. At the same time, we will make a brief comparison of the practices and advantages and disadvantages of the existing disclosed large factories, and why we do this.

The industry practice

For more business team, in order to better team collaboration componentization is inevitable, how about the components of the split step by step and promote compile Meituan has a good introduction Meituan take-out iOS multiterminal reuse of promote, support and thinking mentioned project in binary, but not involves how to realize, It’s more about how to do componentization iterations in steps. So how to start, and what giant shoulders are there to step on?

Zhihu iOS based on CocoaPods binary solution

The practice of Zhihu is based on the CI script that triggers binary package after the project submits PR. It describes how to switch and control the source code and binary, how to store the generated binary package on the server side, and attached the basic flow chart. To summarize the main points:

  • Use YML to configure binary whitelist and file service configuration information.
  • The ZIP package of dSYM and binary integration (including emulator and real device) after Xcodebuild was uploaded to the static server by libo, and the corresponding URL was obtained.
  • Use CocoaPods Analysis to modify podSpec to point the source of the library binary true to the obtained URL and update the Tag. Push the modified spec file to a private repository;

Analysis of the

  1. Controlling source code and binary switching via YML configuration is a good idea, but it would be nice to add binary properties to pod DSL via cocoapod-plugin.
  2. There is a bit of redundancy in modifying the PodSpec and updating the Private Pod Repo. If there is a ZIP package, replace it. If there is a ZIP package, go to prebuild. Here of course there is need to agree on the generated ZIP package name, zhihu on tag + zhihu – static, such as the/path/to/server/AFNetworking – 3.20 – zhihu – static. In essence, whichever way you reference Pod, the source configured in the spec file points to a Git node in the repository (PS: the hash for each commit, so it’s important to manage the version). CocoaPods recurses against semantic versions when resolving conflicting dependencies, so I don’t think it needs a separate static spec.

Fire console iOS based on CocoaPods component binarization practices

The same dual private source policy is adopted, with one static server holding the pre-packaged binary and one with the source server address. What is different from Zhihu’s solution is that when each private library is updated in advance, CI is triggered to package and upload to the server, and the source is replaced during pod install. Zhihu is a package and replacement of binary in the construction of a complete project. A package like Zhihu is the correct solution. However, this article mentioned a lot of pit in practice, there are more reference significance, they also produced aPod plug-in CocoapoDS-bin. To summarize the main points of the article:

  • Transform CocoapoDS-package, support for a single POD binary compilation, packaging upload static server;
  • The binary whitlist is controlled based on the global variable tdfire_use_source_Pods added to the Podfile, and the pod install environment variable is injected to control the source switch.

Analysis of the

  1. Cocoapods-package as an official plugin has been updated since the 1.7.0 release, many years after the release, to support The Swift Package and fix some issues. The biggest trouble of binary compilation with a single POD is that if the team carries out heavy componentization, there will generally be a large number of dependent libraries to maintain. If each library needs to configure a package script, the cost will be high, and the third-party library also needs mirror maintenance. Although it takes some effort to support CI automation, the business engineer also needs to have a complete understanding of the project, otherwise it will be difficult to understand the relationship.
  2. Controlling binary and source switches with the IS_SOURCE environment variable is also not very friendly. It is also possible to add extensions to the POD DSL to support binary Switches. Currently, adding variables to control each install feels a little strange to use;

Transform CocoaPods – Binary

Cocoapods-binary is a simple way to implement cocoapoDS-binary. Now that we know how the plug-in works, we can put our ideas into practice. First of all, we need to do something that many plugins have already done for us, but we need to simply support static server storage and delivery of the Binary Framework. Let’s start with a flowchart:

  • The Featch Remote Framework and Upload Zips to Server in the image above are what we want to do. Before Prebuild Framework, check whether the current pod_target has the corresponding server cache. If yes, download it to the local and unarchive it to the GenerateFramework file directory. Then skip the current compilation of pod_target.

exist_remote_framewo = sandbox.fetch_remote_framework_for_target(target)

def fetch_remote_framework_for_target(target)
    existed_remote_framework = self.remote_framework_names.include? (zip_framework_name(target))return false unless existed_remote_framework

    begin
        zip_framework_path = self.ftp.get(remote_framework_dir + zip_framework_name(target))
    rescue
        Pod::UI.puts "Retry fetch remote fameworks"
        self.reset_ftp
        zip_framework_path = self.ftp.get(remote_framework_dir + zip_framework_name(target))
    end

    return false unlessFile.exist? (zip_framework_path) target_framework_path = generate_framework_path + target.namereturn true unlessDir.empty? (target_framework_path) extract_framework_path = generate_framework_path + target.name zf = Zipper.new(zip_framework_path, extract_framework_path) zf.extract()true
end
Copy the code

After the Prebuild is finished, file cleanup and binary substitution links are performed, and the bulk binary is synchronized at this point. Upload the pod_target matching binary files in the GenerateFramework directory to the static_frameworks directory where the resource server does not exist. The file name is pod_name + tag, such as POD ‘AFNetworking’, and the ZIP framework name for ‘3.0’ is AFNeworkings-3.0.0.zip. sync_prebuild_framework_to_server(target)

def sync_prebuild_framework_to_server(target)
    zip_framework = zip_framework_name(target)
    target_framework_path = framework_folder_path_for_target_name(target.name)
    zip_framework_path = framework_folder_path_for_target_name(zip_framework)

    # FTP server already has a package with the same Tag
    return if self.remote_framework_names.include? zip_framework
    # Local archive failed
    return if! File.exist? (target_framework_path)||Dir.empty? (target_framework_path)begin
        Zipper.new(target_framework_path, zip_framework_path).write unlessFile.exist? (zip_framework_path)self.ftp.put(zip_framework_path, remote_framework_dir)
        remote_zip_framework_path = self.ftp.local_file(remote_framework_dir + zip_framework)
        FileUtils.mv zip_framework_path, remote_zip_framework_path, :force= >true    
    rescue
        Pod::UI.puts "ReTry To Sync Once"
        self.reset_ftp
        sync_prebuild_framework_to_server(target)
    end
end
Copy the code

In practice, in order to facilitate the direct use of the company’s existing FTP file server, a separate open directory maintenance. Ftp_tools. rb and zip_tools.rb are added to cocoapods-binary. This is a simpler implementation than cocoapods-binary.

limit

  • The final binary size will be slightly larger than when using the source code and is not recommended for the final upload Store.
  • – There e is no verification mechanism, if the published binary package is not referenced properly by the project, then the compilation will fail for everyone;
  • Because the project takes the form of all static library dependencies, the project file changes during the binary and source switch.
  • CocoaPods has changed the framework logic to not copy resources to the framework, so we need to fix the CocoaPods version to 1.6.x;
  • For frameworks generated by dynamic configuration, such as RN-related dependencies, binary is not supported.
  • Binary versions compiled from different versions of Swift are incompatible. If the Swift library is referenced in the project, the Xcode version should be unified.

There are also some unexpected problems with using binary. For example, in order to reduce the number of Git changes that would occur during the source code and binary switch, the Pods directory was ignored. As a result, most of the branch switches in the transition phase were confined to the download of some third-party libraries in the POD install. It is impossible without translation. In addition, after the install, we found that the symbol link corresponding to POD was not generated correctly, the corresponding source was not copied successfully, and the business framework packaging took too long.

conclusion

In real project practice, there is no one-and-all solution. Different business dependencies and environment configurations, including the specification of the engineering code, and even simple header file management can cause a variety of problems in the development process. In short, it is a process of constant exploration and evolution.