IOS modular development solution (pure dry goods), ios dry goods
There are also some introductions on the iOS modular development solution online, but we seldom see the actual implementation in specific instances. I plan to write a system article to introduce my understanding of the modular solution, it will contain some information about decoupling, routing, encapsulation, private Pod management, etc. An Instance project compiled will be put in git for open-source [jiaModuleDemo], there are some encapsulated functional modules in it. They will be constantly updated. If you are interested, you can Star them and the project will be constantly updated and optimized; if you have better solutions or good suggestions, we recommend that you update and modify the problem in a short time;
I. Problems in the project
1: when there are multiple projects in the company at the same time and there may be different projects for multiple people, in fact, each APP has many common modules. Of course, you may copy the code of the same functional module in a new project, but this is not the best method, in the process of continuous iteration in the future, different people will add a lot of personal code to the project. In this way, the same module project will cause disastrous unified management for multiple projects in the future, it may be out of control. Even if the project is transferred to another person, it will waste a lot of time and increase the maintenance cost. Therefore, the instance pays more attention to extracting some identical modules for the same purpose; modular management is combined with private Pods. For encapsulation of common functions, You can implement a function by opening some simple switch configuration methods, such as logging, network request module, and network status change prompt;
2: For the coupling between pages, and the passing parameters between pages are also different, the type of passing parameters varies due to different developers or simple methods, contains entities, simple basic types, and other such items. The previous project does not support the routing method. As a result, hard encoding is required for different page jumps to receive the message push, there are quite a lot of problems with function expansion, while there is a modular interaction between pages on the right. There is no coupling between pages, and they are only dependent on the intermediary of JiaMediator; the data transfer process is in the form of a dictionary. Although it may sacrifice some convenience and freedom, it can be decoupled and modularized, and it can be added to the process of routing. It is agreed that the relevant protocols should be used for interaction; this routing method is used to replace the third-party routing plug-ins because of its flexibility. The most important reason is that the third-party routing plug-ins need to register routes when starting;
Ii. modularization of solution implementation
1: JiaCore (Basic Function encapsulation)
JiaCore is the most basic module of the entire APP and relies on all modules. It mainly includes some global functional modules, such as JiaBaseViewController and JiaAppDelegate; at present, some default functions have been integrated into the system, including network status change judgment and prompts, and log recording functions. In addition, the management class JiaCoreConfigManager is used for unified configuration, for example, whether to enable the logging function; the JiaCoreConfigManager class is to enable global configuration settings for specific apps. The following describes one of the logging functions:
// JiaCoreConfigManager * jiaCoreConfig = [JiaCoreConfigManager sharedInstance]; jiaCoreConfig. recordlogger = YES;
Then, the PrefixHeader. pch of the specific APP introduces the namespace and sets the log record level:
# Import "jiaco coalumberjack. h" // DDLog level static const int ddLogLevel = DDLogLevelVerbose;
This completes the introduction of the logging module by an APP. JiaCore has completed the log record-related configuration for you, the error content is recorded in the file in a readable format, and the file generation rules are also defined. Of course, if you want to display different levels of colors on the Xcode console, you only need to install the XcodeColors plug-in and simply set it. JiaCore has been configured for different levels of colors;
2: JSPatch hot update function
The hot update feature is also integrated in JiaCore by default. Hot update is started as long as a simple object array is passed in. JiaPathchModel is already a defined model, in the APP, convert an Interface request to a model array, among them, patchId is the unique value name, md5 is the MD5 value of the JS file, the url is the JS download path, and the Verver is for which version; generally, apps outside the company coexist in multiple versions, and versions must be differentiated for hot updates. Only the hot update JS files corresponding to the current version will be downloaded; the MD5 value is used to increase security and prevent the JS file from being modified by others, which affects the APP running. In JiaCore, MD5 calculation and comparison are performed on the downloaded JS file; JS files not before jSPatchMutableArray will be deleted;
// Hot update content: JiaPathchModel * sample = [[JiaPathchModel alloc] init]; sample. patchId = @ "patchId_sample1"; sample. md5 = @ "2cf1c6f6c5632dc21224bf42c698706b"; sample. url = @ "http://test.qshmall.net: 9090/demo1.js"; sample. ver = @ "1"; JiaPathchModel * sample1 = [[JiaPathchModel alloc] init]; sample1.patchId = @ "patchId_sample2"; sample1.md5 = @ "signature"; sample1.url = @ "http://test.qshmall.net: 9090/demo2.js "; sample1.ver = @" 1 "; // JiaCore Basic module configuration JiaCoreConfigManager * jiaCoreConfig = [JiaCoreConfigManager sharedInstance]; jiaCoreConfig. jSPatchMutableArray = [@ [sample, sample1] mutableCopy];
3: JiaGT module (push encapsulation)
Message pushing is very important for an APP. Generally, it uses third-party sdks for integration. In fact, most of the SDK processing code is similar. In this example, we extract differentiated content, the instance will be modularized with a single push, because most of the Code for message pushing is concentrated in AppDelegate, resulting in a lot of messy code. Of course, some people also remove code for extended classification of AppDelegate, the instance will adopt another solution for extraction, which can achieve full decoupling. In a specific APP, there will be no more SDK-related content, you only need to simply configure and process the message. The following is a simple list of some codes. For other encapsulated codes, see the source code;
// Configure jiaGTConfigManager * gtConfig = [jiaGTConfigManager sharedInstance]; gtConfig. jiaGTAppId = @ "0uuwznWonIANoK07JeRWgAs"; gtConfig. jiaGTAppKey = @ "26LeO4stbrA7TeyMUJdXlx3"; gtConfig. jiaGTAppSecret = @ "2282vl0IwZd9KL3ZpDyoUL7 ";
# Pragma mark message push processing/*** @ author wujunyang, 16-07-07 16:07:25 ** @ brief processes push messages ** @ param icationicationmessage */-(void) gtNotification :( NSDictionary *) icationicationmessage {NSLog (@ "% @", NotificationMessage [@ "payload"]); NSLog (@ "----- receives push notification ------");} /*** @ author wujunyang, 16-07-07 16:07:40 ** @ brief handles Remote Apple notifications ** @ param RemoteNotificationMessage */-(void) receiveremotenotionary ication :( NSDictionary *) remoteNotificationMessage {NSLog (@ "% @", RemoteNotificationMessage [@ "message"]); NSLog (@ "----- received Apple notification ------");} /*** @ author wujunyang, 16-09-21 14:09:33 ** @ brief you can perform some binding operations on the deviceToken when the device is successfully registered ** @ param deviceToken <# deviceToken description #> */-(void) receiveDeviceToken :( NSString *) deviceToken {NSLog (@ "----- current deviceToken: % @ ------", deviceToken );}
4: JiaAnalytics module (umeng statistics package)
The JiaAnalytics module is based on the combination of the umeng statistics SDK and Aspect. It adopts the Aop Aspect Method for page access statistics and removes the Statistical Code that should have been used in the lifecycle of each page, if the App simply configures the information corresponding to umeng, it can also set filter conditions for statistics pages, currently, three types of prefix string arrays, the page name string array to be counted, and the page name string array without statistics are available, to achieve the purpose of accurate statistics page, and put the statistical code in the asynchronous thread, will not affect the response of the main thread;
__weak typeof(self) ws = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, BOOL animated){ UIViewController *controller = [info instance]; BOOL filterResult=[ws fileterWithControllerName:NSStringFromClass([controller class])]; if (filterResult) { [ws beginLogPageView:[controller class]]; } } error:NULL]; [UIViewController aspect_hookSelector:@selector(viewWillDisappear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, BOOL animated){ UIViewController *controller = [info instance]; BOOL filterResult=[ws fileterWithControllerName:NSStringFromClass([controller class])]; if (filterResult) { [ws endLogPageView:[controller class]]; } } error:NULL]; });
3. solution implementation-page decoupling
JiaMediator acts as an intermediary. All response and interaction between modules are performed through it. Each module is extended and classified (for example, JiaMediator + module ), classification is mainly used for local calls and does not require routing. If you want to use routing, you must note the accurate Writing of routing constraints, it will directly affect the ability to respond to the target correctly. The instance also has the issue of callback parameter return using the notification method;
The instance code is as follows:
NSDictionary *curParams=@{kDesignerModuleActionsDictionaryKeyName:@"wujunyang",kDesignerModuleActionsDictionaryKeyID:@"1001",kDesignerModuleActionsDictionaryKeyImage:@"designerImage"}; switch (indexPath.row) { case 0: { UIViewController *viewController=[[JiaMediator sharedInstance]JiaMediator_Designer_viewControllerForDetail:curParams]; [self presentViewController:viewController animated:YES completion:nil]; break; } case 1: { UIViewController *viewController=[[JiaMediator sharedInstance]JiaMediator_Designer_viewControllerForDetail:curParams]; [self.navigationController pushViewController:viewController animated:YES]; break; } case 2: { NSString *curRoue=@"jiaScheme://Designer/nativeFetchDetailViewController?name=wujunyang&ID=1001&image=designerImage"; UIViewController *viewController=[[JiaMediator sharedInstance]performActionWithUrl:[NSURL URLWithString:curRoue] completion:^(NSDictionary *info) { }]; [self.navigationController pushViewController:viewController animated:YES]; break; } case 3: { NSDictionary *userParaDictionary=@{kUserModuleActionsDictionaryKeyID:@"1"}; UIViewController *viewController=[[JiaMediator sharedInstance] JiaMediator_User_viewControllerForDetail:userParaDictionary]; [self.navigationController pushViewController:viewController animated:YES]; break; } default: break; }
Iv. Modular Combination of private Pods Solutions
The examples only reflect the extraction of related Modularization in a project. Finally, we need to implement management in conjunction with Pods and manage each module separately, different apps can simply use the Pods command to introduce modules. For some identical modules, they can be repeatedly referenced in different apps to reduce repeated development costs;