The modular way of Mushroom Street APP

Source: Internet
Author: User

Before the component, Mushroom Street APP code is developed in a project, in less people, business development is not very fast, this is more appropriate, to a certain extent to ensure the development efficiency.

Slowly the amount of code more up, developers are also more up, business development is also fast, then a single project development model will reveal some drawbacks

    • Coupling is more severe (because there is no explicit constraint, there are more references between components)
    • Prone to conflicts (especially with Xib and Xcode Project, although scripts can be improved)
    • The business party is not developing efficiently (only cares about its components, but compiles the entire project and blends with other irrelevant code)

To solve these problems, a "component" strategy is adopted. It can bring these benefits

    • Speed up compilation (without compiling the main code of the subject and the subject)
    • Free choice of development posture (MVC/MVVM/FRP)
    • Facilitate QA testing in a targeted manner
    • Improve business development efficiency

Let's take a look at the approximate architecture after the component

The "component" as the name implies is to split a large App into small components, not directly referenced to each other. How do we do that?

Implementation-mode inter-component communication

In the case of IOS, as in the previous URL jump mode, in theory the jump between pages just open a URL. So for a component, just define "which url" to support, such as the detail page, presumably. "

[Mgjrouter registerurlpattern:@ "Mgj://detail?id=:id" tohandler:^ (nsdictionary *routerparameters) {    NSNumber *id = routerparameters[@ "id"];    CREATE view controller with ID    //PUSH View Controller}];

Home simply call [Mgjrouter openurl:@ "mgj://detail?id=404"] to open the corresponding details page.

The question comes again, how do I know what URLs are available? To this end, we did a backstage dedicated to manage.

These short chains can then be generated on the IOS platform by generating the required files for different platforms. {h,m} file, the Android platform generates a. java file and is injected into the project. This allows the developer to know all the available URLs simply by opening the file in the project.

There is still a piece not to do, is the parameter of this piece, although the description of the short chain, but really want to generate a complete URL, but also need to know how to pass parameters, this is under development.

There is also a situation that is a little more troublesome, that is, "component a" to invoke "component b" of a method, such as on the Product Details page to show the shopping cart of the number of items, it involves to the shopping cart components to take data.

Similar to this synchronous call, IOS used a simpler scenario, or relied on mgjrouter, but added a new method – (ID) Objectforurl: To register with a new method

[Mgjrouter registerurlpattern:@ "Mgj://cart/ordercount" Toobjecthandler:^id (nsdictionary *routerParamters) {    // Do some calculation    return @42;}]

When used nsnumber *ordercount = [Mgjrouter objectforurl:@ "Mgj://cart/ordercount"] this will get the number of items in the shopping cart.

A slightly more complex but more versatile approach is to use the "Protocol"<->"Class" Binding method, or a shopping cart for example, the shopping cart component can provide such a Protocol

@protocol Mgjcart <nsobject>+ (Nsinteger) ordercount; @end

You can see that the data type returned is directly specified by the protocol. Then create a new class within the shopping cart component to implement this Protocol, assuming that the class name is Mgjcartimpl, then you can associate it with the protocol [Modulemanager Registerclass:mgjcartimpl forprotocol:@ Protocol (Mgjcart)], for the consumer, to get this mgjcartimpl, you need to call [Modulemanager Classforprotocol: @protocol (Mgjcart)]. Once you get it, call + (Nsinteger) ordercount.

So, where does this agreement fit in? If you put a component together, you will need to introduce a component first, and if you have more than one such component, you'll be more troublesome. So we put these public agreements into the PublicProtocolDomain.h, and then only rely on this file to be able to.

Android is also used in a similar way.

Component Life Cycle Management

The ideal component can be easily integrated into the subject, and there is a callback method consistent with Appdelegate. That's what Modulemanager did.

Let's take a look at the current entrance method.

-(BOOL) Application: (UIApplication *) application didfinishlaunchingwithoptions: (nsdictionary *) launchOptions{    [Mgjapp startApp];    [[Modulemanager sharedinstance] loadmodulefromplist:[[nsbundle mainbundle] pathforresource:@ "modules" ofType:@ " Plist "];    Nsarray *modules = [[Modulemanager sharedinstance] allModules];    For (id<moduleprotocol> module in modules) {        if ([module Respondstoselector:_cmd]) {            [module application: Application didfinishlaunchingwithoptions:launchoptions];        }    }    [Self tracklaunchtime];    return YES;}

where [Mgjapp STARTAPP] is primarily responsible for the initialization of some SDKs. [Self tracklaunchtime] is a point we hit to monitor how long it took to start from the main method to the end of the entry method call. The rest is done by Modulemanager, Loadmodulefromplist:pathforresource: The method reads a plist file in the bundle, which is probably the content of this file.

Each Module implements the Moduleprotocol, which has a-(BOOL) Applicaiton:didfinishlaunchingwithoptions: Method, which, if implemented, is called.

Another problem is that some of the system's events will be notified, such as Applicationdidbecomeactive will have a corresponding uiapplicationdidbecomeactivenotification, the component if you want to respond, Just listen to this system notification. But there are also some events are not notified, such as –application:didregisterusernotificationsettings:, when the component should also do something, how to do?

A simple solution is to manually tune the corresponding method of the component in each of the appdelegate methods, and execute it if there is one.

-(void) Application: (UIApplication *) application Didregisterforremotenotificationswithdevicetoken: (NSData *) devicetoken{    Nsarray *modules = [[Modulemanager sharedinstance] allModules];    For (id<moduleprotocol> module in modules) {        if ([module Respondstoselector:_cmd]) {            [module application: Application Didregisterforremotenotificationswithdevicetoken:devicetoken];}}    }
Shell Engineering

Since it has been removed, the disassembly of the component must have a carrier, the carrier is Shell engineering, Shell engineering mainly contains a number of basic components and business SDK, which is the main project contains some content, so if the shell works can be normal operation, to the main project is no problem. However, there is a version synchronization problem, which will be said later.

Problems encountered

Component splitting

Since the previous code is under one project, there are a number of problems to be taken out as a single component. The first is the division of components, at that time in the definition of component granularity also took some time to discuss, whether it is coarse granularity, or fine point good. If the coarse point is more advantageous to split, fine point words flexibility is higher. In the end, choose a coarse grain size, and then remove it first.

If you want to move the details page out, you will find that it relies on some other parts of the code, the quickest way is to directly copy the code over, change the name of the use. Relatively simple violence. It is easier said than done, because the normal business does not stop because of the "component", so the development students need to take into account the normal business and component split.

Version Management

Our components, including third-party libraries, are managed through Cocoapods, where the components use a private library. The choice of Cocoapods, one is because it is more convenient, there is the user base is relatively large, and the community is more active (active to the time will occasionally trigger the rate limit of Github, resulting in a long period of clone does not come down ... See this), of course, there are other management methods, such as Submodule/subtree, in the case of more developers, convenient, flexible programs easy to prevail, although it also has its own problems. There are major issues with version synchronization and slow update/compile.

If the basic components do an API interface upgrade, this upgrade will be changed to the original interface, will naturally raise a median version number, such as the original 1.6.19, then now becomes 1.7.0. And we are in the Podfile with ~ specified, so that the main project pod version rose up, but the shell project is not synchronized to, and then the group will be a variety of feedback compiled, but this compiled long tail can sometimes drag on for two or three days.

Then we thought of a way, if not to specify the base library version in the Shell project, only in the main project, it should theoretically be feasible, as long as there is not a basic library to maintain multiple versions of the case. But in practice, found that the shell project sometimes can not rise up, in the Podfile to specify the latest version and can rise up, so blocked.

Another problem is that pod update time is too long, often on the analyzing Dependency card for more than 10 minutes, greatly affecting efficiency. Later troubleshooting is related to the podspec of the component, configured with Subspec, and relies on more.

Then is the pod update after the compilation, because it is compiled by the source code, so this piece of time to spend a lot, next will consider the framework of the way.

Continuous integration

In the beginning, the continuous integration is not very perfect, the business party upgrade components, directly to the podspec throw into the private repo in the finished. This is the simplest, but it often leads to problems with compilation. And this random version of the upgrade is not very quality assurance. So we set up a continuous integration system, presumably.

Each component will need to be compiled before it is upgraded and then decided whether to upgrade it. The system does not look complicated, but it often encounters concurrency problems in the backend, causing the business party to either fail the integration or wait a few minutes. And there is no place to render the component version information for the current version. There is also the business side of this command line is not very high acceptance of the upgrade mode.

Based on this, after a few rounds of discussion, there is a new version of the continuous integration platform, upgrade operations through the Web side to complete.

The general idea is that if the business party is going to upgrade the components, assuming that the current version is 0.1.7, add some feature, Shell Engineering test pass, want to integrate into the main project to see the effect, or other components also want to reference the latest, you can manually in the background to the version of the 0.1.8- Rc.1, in this case, originally dependent on the ~> 0.1.7 components, will not rise to 0.1.8, and also want to test this component, as long as the version is manually transferred to the 0.1.8-RC.1. This process does not trigger a CI compilation check.

When the test passed, you can remove the tail-RC.N, and then click "Integration", will go to the CI compilation check, through the words, will be in the main project Podfile write a fixed version number 0.1.8. In other words, all the component version numbers in Podfile are fixed.

Documentation/DEMO/unit testing of infrastructure components and components for surrounding facilities

The function of the Wireless Foundation is to provide a solution for the group, but it is not enough to work in the Mushroom Street App, so you need to provide a portal, know what components are available, and how to use them, just like this (not yet implemented)

This requires the owner of the component to update the README/CHANGELOG/API in a timely manner, and to quickly notify the consumer when an API change occurs.

Public UI Components

After the component has a problem is the duplication of resources, before in a project, the resources can be very convenient to get, now independent out, do not know what is public, which is unique, simply put into their own components, which will lead to the package becomes larger. Another problem is that each component may be a different product manager, and they are likely to focus only on what the page they care about looks like, ignoring the overall style. Public UI components are used to solve these problems, and these components can even be used across apps. (not yet implemented)

Summary

"Component" is the expansion of the App to a certain size of the solution, to a certain extent, to solve the problem, in the process of improving the development efficiency, mining pits is inevitable, I hope this article can bring some help.

The modular way of Mushroom Street APP

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.