An overview of the

After looking at casa and Bang’s ideas and plans for iOS componentization, I would like to record my thoughts and attempts here.

First of all, the componentization I understood before is the encapsulation of some basic and independent basic functions, which naturally has the characteristics of high aggregation and less dependence on external, so it can naturally become a basic component. For example, we often use some third-party wheels, such as AFNetworking and SDWebImage, which are good network and image components.

Casa’s solution here is to take an entire business module (for example, the entire MVC code of the business) asa business component and separate it into a POD subproject that can be compiled separately. The core point is that business components are not directly dependent on each other, and each business component is an independent sub-project. Here is a simple illustration. You can read the casA article for detailed principles and practices. If you have already learned about them, you can skip this part.

For example, the message list is one-way dependent on friend chats

Usually we import the vc of the friend list directly and then do the jump. This way the direct code depends on the vc of the friend chat.

To remove this direct code dependency at the compile level, call the Runtime. Although this way can relieve the dependency at the compile level, but the caller is more disgusting to write, repeated code is also much, no compile prompt is prone to error, but the loss is not worth the gain.

Therefore, the final optimized scheme is case’s scheme:

  • Component B will provide an external interface, written to the target object (which can have multiple targets), and the caller can look at the target to get an idea of the component’s external interface.

  • Component A calls the targetB interface through A unified Runtime method. This part of the interface is written to categoryB.

    Why category? The main reason is that the Runtime calls have some repetitive code, and the difference is mainly in the parameters. So category here is the difference between the component runtime calls. like this:

    123456789Copy the code
    - (UIViewController *)A_Category_ViewControllerWithCallback:(void (^)(NSString *))callback { NSMutableDictionary *params  = [[NSMutableDictionary alloc] init]; params[@"callback"] = callback; params[kCTMediatorParamsKeySwiftTargetModuleName] = @"A"; return [self performTarget:@"A" action:@"Category_ViewController" params:params shouldCacheTarget:NO]; }Copy the code

The question is, what if two business components are bidirectionally dependent?

All components provide an internal target, as well as an external category. In this way, bidirectionally dependent components can also be decoupled. Only categories with Runtime code are dependent.

With this decoupling in mind, it’s easy to understand the design idea of pulling components out of the whole business MVC dimension. Because in this dimension, the interaction between the business and the business, it is possible to consolidate, take a VC from another business, and push it to the interface to minimize the interaction as much as possible.

Expected goal of hand Q componentization

Take hand Q as an example. After componentization, the whole engineering structure may look like the following figure:

Explain the terms associated with business components:

  • The message list component A: contains all the basic code for the message list business and should be A complete MVC architecture.

  • TargetA: The capability of the message list to provide to other businesses, written in TargetA. For example, targetA provides a method that returns a VC instance of the message list. One important thing to note here is that business components are parallel and not directly dependent, so targetA is not called directly by other business component dependencies.

  • CategoryA: categoryA is a call to targetA from runtime code. The goal is to avoid direct code dependencies between businesses, leaving the base component A as A separate, compilable entity.

The key point here is:

  • At the compile level, dependencies are top-down, with no interdependencies between parallel components. (The removal of interdependencies between business components, as explained in the overview above)

  • Each business component is a POD subproject, which is a separate compilation entity

Why de-dependency at the compile level?

The answer is to make business components into building blocks, and de-dependency on compilation provides flexibility.

For example, A depends on B and B depends on C. In terms of product performance, A jumps to B and B jumps to C.

Abcs are required to participate in compiling without lifting dependencies. After the dependency is removed, AB can only be involved in compilation, because as A business development, I basically only need to pay attention to the logic of page A and the normal logic of jumping to page B.

The real situation may be more complicated, C may rely on more, and finally the recursive dependence will continue, which will lead to the development of A interface and the need to compile the whole app.

Componentized expected revenue?

The above scheme is very clear, but since I have not done the componentization of such business model, I have a big question about how much benefit it will bring. After all, if this scheme is implemented in existing projects, especially heavyweight projects like HANDq, the cost will be huge.

Standalone component development scenarios

If I am a developer of the friends list module, mainly developing and maintaining the requirements of the friends list, how might my working mode become:

Add A new program entry main A, take the VC of the friend list component as Root VC, and rely on the project of the friend list component and the basic function component. In this way, we have a single page APP of friends list component, which can facilitate the development of friends list page.

The code logic of a business component that is not compiled cannot actually be executed. For example, if you click a friend’s profile picture, you can jump to the chat service, but the chat component is not compiled, so you can’t really display the chat interface.

When you click on your avatar, you crash.

Uniform exception handling is possible when a category is invoked at run time. For example, an abnormal interface is displayed.

What if I want to debug whether jumping to the chat page works?

Consider adding a dependency to your Podfile for that component to compile as well. Business components that are completely unrelated do not need to be compiled.

The expected benefits of independent business development look like this:

  • Greatly reduced compilation time: it used to require full compilation of the whole project (with the volume of hand Q, the time may be about 40min), but now it is independent component compilation, the time may be reduced to about 1-2min.

  • Unit testing of individual pages is becoming possible: it looks like mobile development, not much white-box testing, I don’t know if that’s the case with other projects. Here the white box test seems to be easy to do because the sub-project is more focused, this point is just a fantasy, not implemented

The whole APP tuning scene:

In addition to the development scenario of single business, if we want to carry out the joint adjustment and series of the whole APP. What’s the payoff?

Because each business module is separated to POD sub-project, and support independent compilation into static library. When we want to compile the main project, we can consider the integration of business components in a static library, so that the compilation time of the whole main project is only the compilation time of the link of the sub-projects.

summary

From the above analysis, it seems that the benefits of componentization are huge. In most development scenarios, the business components are developed and run independently as a single-page app, without having to endure long compilations of the original master project and xcode being prone to getting stuck.

Is it feasible to move forward step by step

With the volume of hand Q, it is expected to be a long and lengthy process to achieve the componentization of most business completion. So can we first take a single business componentization, the implementation of small fast run?

Here, take intimate relationship business for componentization analysis:

The main reason for choosing the intimate relationship page is that it is the newly written code in recent versions, so there should be less coupling. There is also continuous requirement planning in subsequent products, so the actual effect can be experienced when developing new requirements.

Comparing the complete diagram at the beginning of the article, you can see that the individual componentization needs to be done as follows:

  • Uncomponentized code remains in the main project

  • The intimacy component that has been componentized is abstracted from the code to the POD subproject

  • Other business components on which the intimacy component depends need to provide target and category. Target is in the main project, category is in the standalone project.

How does it affect us that other businesses are not componentized?

The only impact is that the Intimacy single-page app no longer has the ability to jump to other business modules. Because other business modules must be compiled into the main project to have their code implemented.

Try to analyze dependence using intimate relationships as an example

Recognizing the componentization of individual business modules can also bring good benefits. Then we can start to break it down. First, we need to analyze the dependency of the intimate relationship module. Currently, tools found on the Internet don’t run well on Handq, or take a long time to run.

Here I wrote a small tool RelationDependenceAna, is in accordance with the depth-first traversal way, to index file import/include, currently for those useless import temporarily not filter. The first time you do it, you’re going to build the cache and it’s going to be slow. The subsequent execution will be very quick.

Take a look at the results:

That’s sort of what it means, but it’s not posted here as far as the code is concerned. A ViewController recursively imports all the files in the main project, close to 1W files.

Starting from the root node, manual analysis shows that there are more than 10 basic modules that are relatively large and rely on intimate relationship businesses with relatively new codes and less dependence. Therefore, to complete the first step of business module componentization, or to carry out the basic component separation work, first solve the vertical dependence.

Vertical base component dependencies are fundamental to the building of our business components and must be removed from the main project in order for our business components to operate independently.

A long way to go, and line and cherish.