preface
There are many and mature theories about componentization on the Internet. For theories, please refer to this collective article iOS componentization. This article only talks about my own understanding and practice.
First, the original intention of componentization. (Personal opinion)
- It is beneficial to the encapsulation and reuse of code modules.
- Different business modules can be physically isolated (through git private repository permission control) to further improve code stability and security.
- The overall structure of the project is well structured for later maintenance.
- It is convenient for project function subdivision, finer granularity, more reasonable allocation of work, easier control of project time nodes, and convenient for agile development.
- Easy to unit test.
Two, componentized development process.
1. Decoupling is necessary for componentization. When we talk about decoupling, we do not completely decouple code from code. This is not reasonable or possible through learning and practice. Our goal is to decouple code modules from each other, or to make them unidirectional, like a plug. (Reasonable or unreasonable graph)
2. Modularization and decoupling theory Modularization and decoupling theory because of my limited energy, this paper mainly records the practice of componentized architecture, so here only divide components by functional modules, modularization can encapsulate corresponding modules according to their own projects.
3. Componentized architecture design
Three, componentized architecture implementation.
1. The common inter-module communication schemes in the industry are as follows:
- UI page hop management (URL-block) based on routing URL.
- Reflection – based remote interface call encapsulation (target-action).
- Service registration Scheme (protocol-class) based on protocol- oriented idea.
- Notification based broadcast scheme (NSNotification).
You can use one or several. I choose target-action based on reflection and URL-block based on routing URL to encapsulate UI pages.
2. Implementation of routing: JLRoute is a very good routing framework for many mature schemes on the Internet, but I feel that JLRoute is still relatively large and its function is not single enough. Therefore, we have implemented a simple routing framework PTRouter.
PTRouter, which supports registration schemes. Register the Scheme feature to make it easier to call things like third-party sharing or statistical SDK integration. (See Demo for details) PTRouter only expands the function of controller jump in order to realize the single function. Data is passed in as a parameter (the underlying implementation is dependency injection). Using a block only returns whether the jump was successful, such as opening a Settings page:
[PTRouter openURL:@"InnerJump://setting/browse?userID=007" callback:^(BOOL result) { if (!result) { [SVProgressHUD ShowInfoWithStatus :@" open failed "];}}];Copy the code
PTRouter supports synchronous & asynchronous fetch return values, where asynchronous to synchronous is internally implemented through Semaphore
+ (void)openURL:(NSString *)url callback:(void (^)(BOOL result))callback; + (BOOL)openURL:(NSString *)url; Copy the code
In addition, openURL supports not only urls with parameters, but also dictionaries
+ (void)openURL:(NSString *)url param:(NSDictionary<NSString*,id> * __nullable)param callback:(void (^)(BOOL result))callback; + (BOOL)openURL:(NSString *)url param:(NSDictionary<NSString *,id> * __nullable)param; Copy the code
3. Public components, such as third-party sharing, data statistics, Bug analysis, which need to be registered when the APP enters, should be put into the main project for corresponding registration management:
Automatic registration of components is triggered when PTAppLaunchHelper APP starts
[PTAppLaunchHelper.shared autoInitModule]; // Automatically initialize components according to AutoInitialize. PlistCopy the code
Corresponding registration information is centrally written in PLIST for easy maintenance and management. Individuals use Runtime to dynamically register components.
PTAppLaunchHelper has two functions:
- AutoInitModule is used to initialize components. This function reads classes in AutoInitialize. Plist, which is initialized using the Runtime + automatic initialization protocol:
//init modules with AutoInitialize - (void)autoInitModule; Copy the code
- AutoRegistURL is used to automatically register routes. This function reads autoregisturl. plist to complete route registration. Controller represents the class name, and params represents the default parameter. If the parameter sent by openURL does not match the default parameter, the route will report an error
//init url with AutoRegistURL - (void)autoRegistURL; Copy the code
PTAppEventBus life cycle monitoring component PTAppEventBus obtains APP life cycle events by receiving system notifications, and changes the value of corresponding attributes after receiving life cycle events. Eight attributes, such as didEnterBackground, are provided by default and can be listened on using reactive functions.
- (void)observeWithBlock:(PTObservingBlock)block { if (self.owner && self.keyPath) { [self.owner addObserver:self.owner forKey:self.keyPath withBlock:block]; } else {NSLog(@"owner = %@, keypath = %@",self.owner, self.keypath); NSString *reason = [NSString stringWithFormat:@"Object does not set owner or keypath"]; @throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil]; return; }}Copy the code
PTAppEventBus needs to be called before use
- (void)start; Copy the code
If these are not enough and more events need to be monitored (such as the vertical and horizontal screen status of APP), you can add corresponding attributes to PTAppEventBus by classifying them as follows: See Demo for details
NSMutableDictionary *defaultMap = [NSMutableDictionary dictionaryWithDictionary:[PTAppEventBus defaultNotificationMap]]; [defaultMap setObject:KDidChangeStatusBarOrientation forKey:UIApplicationWillChangeStatusBarOrientationNotification]; [PTAppEventBus.shared startWithNotificationMap:defaultMap]; // Enable EventBus. After enabling EventBus, components can receive App life cycle eventsCopy the code
Conclusion:
The components in this article are integrated through the POD private library, as shown in Demo. The coupling part still needs to be improved, and we will continue to modify it later.