Chat Cocoapods and Xcode project configuration

Source: Internet
Author: User
Tags version control system

Objective

The article is long, so at the beginning of the article I would like to briefly introduce the content of this article, the reader can choose the entire fineness, or directly find the part of their interest.

Since it is about Cocoapods, it is first to understand the background of its emergence. Experienced developers know that Cocoapods in practical use, often encountered a variety of problems, there is a certain cost of use, so the measurement of Cocoapods costs and benefits is critical.

The essence of Cocoapods is a set of automated tools. It is important to understand the rationale behind the automation process, if we can manually simulate the Cocoapods process, whether it is for Cocoapods or Xcode engineering configuration learning. For example, before and colleagues have studied the static library nesting problem, it is regrettable that did not solve, and now want to understand the relevant knowledge is not enough in place. This section is mainly about the project configuration of Xcode, and the concept of nouns such as target/project/workspace.

Finally, I'll take a practical example and talk about how to publish your own Pod for others to use. is a summary of the practice of Cocoapods.

Because of the practical operation more, I made a demo for this article, submitted on my github:cocoapodsdemo, interested readers can download it, study the history of submission, or operate it yourself. Friendly reminder: This article is involved in the static library is the simulator production, do not run the real machine.

Why to use Cocoapods

We know that the big project was initially evolved from a very simple engineering template provided by Xcode. In the course of the project's evolution, new classes have been created to implement new functions, and new code has been added. However, in addition to their own code, we often directly import the third party's open source code into the project, so as to avoid duplicating the wheel, saving development time.

It may seem easy to import code directly into a project, but in practice, you will encounter many problems. These problems can be confusing to the user of the code, which greatly increases the difficulty of integrating the code.

User's obsession

The most immediate problem is the subsequent maintenance of the code. If the publisher of the code updates the code on a Future day, fixes a major bug, or provides new functionality, it is difficult for users to integrate these changes.

Code has been added to delete, if the code compiled into a static library to provide users, you can save a lot of problems. If you do this, however, you will encounter another classic question: "Other linker flag".

For example, you can see in the Demo Bsstaticlibraryone this project that there are two classes in this static library, one of which is to expand Extension. A. A file is obtained when the project is compiled.

We all know that the format of a static library can be a. Framework, or. A. If you delve into it, the. A file can be understood as an archive, or a compressed file. It stores the target file in the compiled. O format. We can prove this by ar-x command:

Ar -x libbsstaticlibraryone. A

One thing to be reminded of is that the light has a. A file is not enough, we also need to provide a header file for the user to import. To do this, we need to add a Headers Phase to the project's Build phases, and then put the header files that need to be exposed to the public column:

The compiled header file is then placed in the directory where the. A file is located, in the Usr/local/include directory.

Next Open Otherlinkerflag This shell project, introduce. A file and header file, run the program, the result must be:

-[bsstaticlibraryone sayotherthing]: Unrecognized selector sent to Instance xxx

This is the classic linker flag issue. First of all, we know that. A is actually a collection of compiled target files, so the problem is the link, not the compilation. Objective-c when using a static library, you need to know which files need to be linked in, and it is based on the __ shown in the previous figure. Symdef SORTED file.

Unfortunately, this file does not contain all the. O Target files, but only the target files that define the classes. We can execute cat __. symdef\ SORTED to verify, you will see that there is no extension class information. As a result, BSSTATICLIBRARYONE+EXTENSION.O exists, but is not linked to the final executable, resulting in an error finding the method.

The workaround for the above problem is that the caller finds the other linker flag in Build Settings and writes the-OBJC option, which links all the target files. However, according to the document description, if the static library is only classified, without classes, even if the-OBJC option is added, the-force_load parameter should be used.

Because third-party code usage classifications are almost inevitable, almost every user is configured to do so, adding complexity and the chance of error.

In addition, the third-party code is likely to use the system's dynamic library. Therefore, the user must also manually introduce these dynamic libraries (remember this, the static library does not support recursive reference, this is a very troublesome thing, the following will be introduced), we take the Baidu map SDK integration As an example, the reader can compare the manual import and Cocoapods Integration Step difference: Configure the development environment iOS Sdk.

Therefore, I summarize the benefits of using Cocoapods as follows:

    1. Avoid the original way to import files directly, facilitate subsequent code upgrade

    2. Streamline and automate integration processes to avoid unnecessary configuration

    3. Automatically handle library dependencies

    4. Streamline the developer release code process

Cocoapods Working principle

In one of my previous articles: Vernacular Ruby and DSL, and the use of IOS in development, Cocoapods is a set of tools developed with Ruby. Each piece of code is a pod, and the version and dependencies of the library are analyzed first when the pod is installed, which is done at the Ruby level, and this is not the case.

Let's start with the assumption that we've found the address of the code you want to download (for example, on Github), and from there, the next work is related to IOS development.

If you have a Cocoapods project on hand, you should notice the following features:

    1. No code or static library in the main project to import third-party libraries

    2. The main project does not explicitly rely on each third-party library, but it references the LIBPODS.A Cocoapods library

    3. Do not need to manually compile third-party libraries, directly run the main project can, implicitly specify the compilation sequence

This minimizes the impact of introducing third-party libraries on the main project, but does not completely drop to zero. For example, after the introduction of Cocoapods, the project has to use Xworkspace to open, the reasons are explained later.

Assuming the previous Bsstaticlibraryone project is to download the good source code, now we have to do is to integrate it into an existing project, such as called Shellproject.

The first problem we encountered was that in the previous demo, you had to manually drag the static library and the header file into the project. But this is inconsistent with the Cocoapods effect, after all, we hope that the main project is completely unaffected.

Static Library and header file import

If we don't do anything, it's certainly not possible to reference static libraries and header files under another project in the Shell project. But the question can also be asked in a different way: "How does Xcode know that they can be referenced, or not to be quoted?" The answer lies in the Search Paths section of Build Settings. By default, the header search path and the library search path are empty, meaning that Xcode does not go to any directory to find static libraries and header files unless they are imported into the project manually.

So, as long as the values of the above two options are slightly modified, Xcode will recognize it. Our current project structure is as follows:

- Cocoapodsdemo(root directory )

- bsstaticlibraryone (referenced static library )

- Build/products/Debug-iphonesimulator (catalog of compiled results )

- libbsstaticlibraryone. A (Static library )

- usr/local/include (header file directory )

- bsstaticlibraryone. H

- bsstaticlibraryone+Extension. H

- shellproject (Shell engineering )

So what we're going to do is let the Shell project's Library Search path point to the Cocoapodsdemo/bsstaticlibraryone/build/products/debug-iphonesimulator directory:

Library Search Path = $project_dir/. /bsstaticlibraryone/Build/products/Debug-iphonesimulator /

Remember to write relative paths here and Xcode will automatically turn into absolute paths. Then the Header Search Path also follows:

Header Search Path = $project_dir/. /bsstaticlibraryone/Build/products/Debug-iphonesimulator/ libone

The attentive reader may find that the Libone folder does not exist at all. So, because I think usr/local/include this path is too deep, too ugly, so you can in the static library project configuration, in the packaging section, find the public Headers Folder path, the value from usr/local/ Include is modified to Libone and then recompiled, you will see that the resulting header file location has changed.

Of course, it is still not possible to refer to the static library directly at this time. Because we're just telling Xcode to go to the corresponding path, but it's not explicitly declared, so we need to add an option to the other Linker Flags:-L "Bsstaticlibraryone", the contents of the quotation marks are the project names of the static library.

To be reminded, the static library compiled by the. A file will be manually added to the Lib prefix, when written to other Linker Flags should be careful to remove the prefix, or the library not found error will occur.

The following works are configured as shown:

Now that the project does not have any third-party libraries or code, it can still reference third-party classes and run successfully.

Referencing multiple third-party libraries

When our project needs to refer to multiple third-party libraries, there are two ways of thinking:

    • Each third-party code as a project, respectively, a static library and header files.

    • All third-party code is placed in the same project, creating multiple target, each corresponding to a static library.

Intuitively, the second form of organization looks more focused and manageable. Consider that we also need to solve the library dependency problem, and the dependency processing within the project is much easier than the dependency processing in workspace (described later), so the second way of organizing is more feasible.

If the reader has a project with Cocoapods in hand, you can see that it has the following file organization structure:

- shellproject(root directory, Shell engineering )

- shellproject (Project code )

- shellproject. Xcodeproj (project file )

- Pods (root directory of third-party libraries )

- Pods. Xcodeproj (total engineering of third-party libraries )

- afnetworking (a third-party library )

- Mantle (another third-party library )

- ......

In my demo, in order to lazy, do not put a third-party library in the Shell project directory, but choose and its lateral. This is not really a big difference, just the reference path is different, do not care too much. We now simulate adding a new third-party library, complete with the following code structure:

- Cocoapodsdemo(root directory )

- Bsstaticlibraryone (third-party library total folder, equivalent to Pods, because lazy, the name will not change )

- Bsstaticlibraryone (first third-party library )

- bsstaticlibrarytwo (add a third-party library )

- bsstaticlibraryone. Xcodeproj (project file for third-party libraries )

- Build/products/Debug-iphonesimulator (catalog of compiled results )

- shellproject (Shell engineering )

First, create a new folder Bsstaticlibrarytwo and drag it into the project, and then add a Target (as shown).

In the Xcode project, we have been exposed to project. Opening a. xcodeproj file is the opening of a project. Project code management is the responsibility. A Project can have multiple target, these target can use different files, and finally can draw different compilation artifacts.

By using multiple target, we can get different apps with a little different code, thus avoiding the need to open multiple projects. But some of our target does not contain the same code, but a third-party library that corresponds to a target.

Next we create a new class, remember to add to bsstaticlibrarytwo this target, remember to modify the public Headers Folder Path and add a Build Phase as before.

In the upper left corner, the Scheme is selected for Bsstaticlibrarytwo, and you can see that the new static library has been generated.

In-Project dependencies

For the main project, the compilation must be done after the sub-project (third party libraries) has been compiled, or in other words, when we press Command + r/b in the main project, all the sub-projects must be compiled first. For this cross-engineering library dependency, we cannot directly identify the dependencies, we must implicitly set the dependencies, and we will use the Cocoapods engineering example:

The static library of LIBPOD.A is used in the main project, and it is not generated in the main project, but is compiled and generated in the Pods project. Once this referential relationship is present, an implicit dependency is established. When you compile the main project, Xcode ensures that all the static libraries it references are compiled first.

Before we discussed two ways to manage multiple static libraries, if you chose the first method, each of the static libraries corresponds to a Xcode project, although it is not possible, the main project looks more complex, mainly due to cross-project dependencies.

The reliance on target management within a project is relatively straightforward. As long as we create a new target, we might as well call it a Pod. It does nothing but relies on two other static libraries to set the Target Dependencies:

At this point, select Pod to compile the target, and the other two static libraries will be compiled. So the next task is to make the main project directly dependent on the target of Pod, and naturally indirectly rely on the various third-party static libraries that are really useful.

Next we repeat the previous steps, set the header file and the static library search path, and add in other Linker Flags:-L "Bsstaticlibrarytwo", you can use the second static library.

Workspace

So far, we've modeled the organization of multiple static libraries and how to reference them in the main project. But there are some small flaws, I cut a picture in Xcode:

It is obvious that the code in the third-party library is considered the system code and the color is blue. And the normal custom method should be green, which will cause trouble to the developers.

In addition to this small flaw, in the cross-project dependencies that were mentioned earlier, one project needs to not only refer to the product of another project, but also a prerequisite: Put the two projects into the same Workspace. Workspace's role is to organize multiple project, so that each project can have referential dependencies directly, while also allowing Xcode to identify the code and header files in each project.

Hold Command + Control + N to create a new Workspace:

When you're done, you'll see a completely blank item, right-click on the left, and select Add Files to:

Then select both the static library project and the. xcodeproj file for the master project to add the two projects:

As a reminder, when you switch to Workspace, Xcode will use the Workspace directory as the project root, so the compilation results of the static library will be placed in/cocoapodsdemo/build/products/... instead of the previous/ cocoapodsdemo/bsstaticlibraryone/build/products/, so you need to adjust the search path in the main project manually.

After making the above changes, even if we delete the compiled results of the Bsstaticlibraryone project, and only compile the main project in Workspace, Xcode will automatically compile the dependent static libraries for us. That's why we just need to execute the pod install to download the code so that we can run it directly in the main project without doing anything else.

Of course, the small problem with the wrong color of the code also Workspace back to normal.

Static Library Nesting

Here, basically about Cocoapods's work principle is to be considered to be finished. These operations, in addition to the file increase, are basically modified. pbxproj files. All Xcode is reflected in the file, and as long as you modify the file, you can also achieve the effect of the above manual operation. Instead, Cocoapods developed a set of Ruby tools to encapsulate these changes, thus automating the implementation.

At the beginning of the article, we mentioned that as a code provider, if your code also refers to other third-party libraries, it can become cumbersome to provide code, mainly because static libraries do not cause recursive references. We already know that a static library is actually a packed form of a compiled target file (. o file) that needs to be used with a header file. The so-called recursive reference refers to the assumption that project a refers to static library B (or the same as a dynamic library), then a compiled static library does not contain the target file for static library B. If someone gets this static library A, they have to do with static library B, or they will encounter a "Undefined symbol" error.

If we provide the code to reference the system's dynamic library, the problem is relatively simple, as long as the document is noted, let the user to import. But if it's a third-party code, it's a disaster. Even if the user finds a static library that is used by the provider, the static library is likely to have been upgraded, while the version-inconsistent static library may have a completely different API. This means that the code provider also notes the version of the static library used in the document, which is then used by the user to find the version. I think this is the task that Cocoapods is really committed to solving.

CocoaPods's approach is simple, because he has a unified version of the rules, can also automatically analyze dependencies, and each version of the code is recorded. The following will introduce Cocoapods's related practices, here we first think about how to manually solve the problem of static library nesting.

Since the static library is just the packaging of the target file, I just need to find the nested static library, get the target file, and then repackage it with the outer static library. This process is relatively simple, I did not do the demo, in the code should be able to explain very clearly. Suppose we have static libraries a.a and B.A, where a requires reference B, and now I want to release A, and integrate B:

Lipo A. A -thin x86_64 output a_64. A # if it is a multi-CPU architecture, first extract the. A file from one of the schemas.

Lipo B. A -thin x86_64 output b_64. A

Ar -x a_64. A # unzip the target file in a

Ar -x b_64. A # unzip the target file in B

Libtool -static -o Together. A *. o # Pack all. o files together into the Together.a

This time the Together.a file can be used as the full version of static library A for others.

Cocoapods use

Originally, the use of Cocoapods is relatively simple. Especially after understanding the principle, use should be more handy, for some common mistakes also have analysis ability. But there's a little bit of detail you need to pay attention to:

Podfile.lock

There is no clear answer about whether the Cocoapods file should be added to version control. My previous habit was not to join version control. Because this makes the commit history significantly more complex, if different versions of the pod are used on different branches, there is a lot of conflict when merging the branches.

However, the official recommendation is to add it to version control. This eliminates the need for the pod install to be performed and ensures that everyone's code must be consistent.

However, although it is not mandatory to add the entire Pod to version control, Podfile.lock must be added to the version control system anyway. To explain the problem, let's take a look at the possible problems of Cocoapods.

Suppose we write in podfile: Pod ' afnetworking ', then the default is to install the latest afnetworking code. This causes user A to be able to install version 3.0, and user B will become the 4.0 version. Even if we specify a specific version of the library in Podfile, there is no guarantee that there will be no problem. Because a third-party library may also rely on other third-party libraries, it does not guarantee that its dependencies are specific to the version number.

So the meaning of Podfile.lock's existence is to record the versions of each library that was used at one time for the pod install, and the version of other third-party libraries that the library relies on for others to use. In this way, the pod install process is actually:

    1. Determine if podfile.lock exists and if not, install as specified in Podfile

    2. If Podfile.lock exists, check that each Pod in the podfile is present in the Podfile.lock

    3. If present, ignores the configuration in the Podfile, using the configuration in Podfile.lock (actually doing nothing)

    4. If it does not exist, the configuration in Podfile is used and written to the Podfile.lock

Another common command, pod update, is not a daily update command. Its principle is to ignore the Podfile.lock file, fully use the configuration in Podfile, and update the Podfile.lock. Once you decide to use Pod Update, you must update all team members together. So be sure to understand what's going on behind it and the impact on your team before using update, and make sure it's necessary.

Publish your own Pod

Many tutorials have introduced the process of open source Pod, and I have mainly referred to the following two articles in practice. Relatively detailed, well-organized, also recommended to you:

    1. Cocoapods Series Tutorial (ii)--Open source successor

    2. Cocoapods Series Tutorial (iii)--private library management and modular management

If you want to create a private library inside your company, first build your own warehouse, which is also stored locally:

As shown in, Master is the official warehouse, and Baidu is the private repository I used to test. All Pod information is stored in the repository, and each folder is differentiated according to the version number, and each version corresponds to a podspec file. As you can see, cocoapods caches all podspec locally, but does not cache the specific code for each Pod. Whenever we execute the pod install, we will first find the Podspec cache locally, and if it does not exist, we'll go to the central repository to download it.

The pod install we often encounter is slow because the entire master is updated by default. At this point, master not only stores the Podspec file that uses the pod locally, but it stores all of the existing pods. So this update process looks unusually slow. Some solutions are to use:

Pod Install- -verbose --no-repo-update

This is actually a palliative treatment method, because the local warehouse will be updated sooner or later, or you will not get the latest podspec. To solve this problem completely, in addition to regular updates, you can also select other faster mirrored warehouses.

The Podspec file is the file that we need to fill out when we open source pod, mainly describes the basic information of pod. In addition to some irrelevant configuration and introductory information, the most important to fill out source_files and dependency. The former is used to specify which documents will be published externally, while the latter specifies which other pods the pod depends on. For example, my privatepod relies on corepod, and using PodS in a project within a company relies on an integration process that can simplify the code a lot. A typical podspec may look like this:

Fill in the above information, we only need to lint first podspec, to ensure that the format is correct, you can submit.

Chat Cocoapods and Xcode project configuration

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.