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".

As an example, you can see in this project of the Demo BSStaticLibraryOne that there are two classes in this static library, one of which is to expand Extension. A file is obtained after the project is compiled .a .

We all know that the format of a static library can be .framework , or can be .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 ar -x prove this by order:

ar -x libBSStaticLibraryOne.a

One thing to be reminded of is that it's .a not enough to have a file, and 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 usr/local/include .

Next open OtherLinkerFlag the Shell project, introduce the .a file and header file, run the program, the result must be:

selectorto instance xxx

This is the classic linker flag question. First of all, we know that it .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, based on the file shown in the previous illustration __.SYMDEF SORTED .

Unfortunately, this file does not contain all of the .o target files, but contains only the target files that define the classes. We can do it cat __.SYMDEF\ SORTED to verify that you will see that there is no extended class information. BSStaticLibraryOne+Extension.othis way, although it exists, it is not linked to the final executable file, resulting in errors that cannot be found.

The solution to the above problem is that the caller finds it in Build Settings other linker flag and writes an -ObjC option that will link all the target files. However, according to the document description, if the static library is only classified, and there is no class, even if the option is added -ObjC to the error, the parameter should be used -force_load .

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 libPods.a it references the 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 xworkspace be used to open, followed by the reasons.

Suppose 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, theheader 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 (Directory of compiled results) - Libbsstaticlibraryone.                A (static library) - Usr/local/include  (header file directory)  - bsstaticlibraryone . h - bsstaticlibraryone  +extension . h - shellproject  (Shell project)   

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

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=$PROJECT_DIR/../BSStaticLibraryOne/Build/Products/Debug-iphonesimulator/LibOne

The attentive reader may find that the Libone folder does not exist at all. Yes, because I think usr/local/include This path is too deep and ugly, so you can find the public Headers Folder in the project configuration of the static library, in the Packaging section. Path, modify its value from usr/local/include to Liboneand recompile, and 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 : The -l"BSStaticLibraryOne" content in the quotation marks is the project name of the static library.

Note that the static library compiled .a files will be manually lib prefixed, in writing to the other Linker Flags should be careful to remove this prefix, or the library not FOUND 0> 's error.

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:

    1. Each third-party code as a project, respectively, a static library and header files.
    2. 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(根目录,壳工程)    - ShellProject (项目代码)    - ShellProject.xcodeproj (项目文件)    - Pods (第三方库的根目录)        - Pods.xcodeproj (第三方库的总工程)        - AFNetworking (某个第三方库)        - Mantle (另一个第三方库)        - ……

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(根目录)    - BSStaticLibraryOne (第三方库总的文件夹,相当于 Pods,因为偷懒,名字就不改了)        - BSStaticLibraryOne (第一个第三方库)        - BSStaticLibraryTwo (新增一个第三方库)        - BSStaticLibraryOne.xcodeproj (第三方库的项目文件)        - Build/Products/Debug-iphonesimulator (编译结果的目录)    - ShellProject (壳工程)

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 join Bsstaticlibrarytwo This target, remember to modify the public Headers Folder Path as before and add a Build P Hase.

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 search path for the header file and the static library, and add it in the 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 Filesto:

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

To be reminded, after switching to Workspace, Xcode will use the Workspace directory as the project root directory, so the static library compilation results will be placed in /cocoapodsdemo/build/products/..., Instead of the previous /cocoapodsdemo/bsstaticlibraryone/build/products/..., you need to manually adjust the search path in the main project.

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 downloaded code so that we pod install 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:

A.aA_64.a # 如果是多 CPU 架构,先提取出某一种架构下的 .a 文件lipo B.a -thin x86_64 output B_64.aA_64.aA 中的目标文件ar -x B_64.a # 解压 B 中的目标文件libtool -static -o Together.a *.o # 把所有 .o 文件一起打包到 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 others to execute and pod install 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: So the pod ‘AFNetWorking‘ default is to install the latest code for afnetworking. 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 pod install record the versions of each library used at one time, and the version of other third-party libraries that the library relies on for others to use. pod installin this way, the 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 pod update to use it, you must update all team members together. So be sure to update understand what's going on behind it and the impact on your team before you use it, 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 do pod install , we will first find out whether the Podspec cache exists locally, and if it does not exist, we'll go to the central repository to download it.

We often encounter pod install 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:

podinstall--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.