Contents: 1. Sources of componentization requirements 2. Introduction to componentization 3. Module splitting 5. Component engineering compatible with SWIFT environment 6. Communication between components 7. Componentized resource loading 8.OC project base for swift code 9. Summary
1. Componentized source of demand
In the beginning of the project, the App is only one product line, code logic is relatively clear, along with the rapid development of the company, the late App hosted inside now probably WuLiuTiao product lines, each process is part of the product line is the same, also has the part is not the same, it is need to do all kinds of judgment and customized requirements. After more than a year of work, there were demands from different product lines, and the developers all needed to develop in the main project. However, the developers were developing different product lines and had to run the whole project, with obvious limitations on code management, parallel development efficiency, branch management and online time. At the end of last year, our leader put forward this problem, hoping to make it componentalization, refactoring the code into modules, assembling the modules in the main project, and forming a complete App.
2. Componentization
With the increase of business lines, the complexity of business increases, so does the complexity of App code logic, and the cost of later development and maintenance. Why? There is no classification of business logic, the efficiency of finding problems is reduced (for novices), and the operation is slow, really annoying…… We need to change that. Componentized development is to decompose a bloated, complex single project according to its functions or attributes into independent functional modules or components. It is then arbitrarily organized into a project with complete business logic, in some way, based on the needs of the project and the business.
Disadvantages of componentized development:
- Serious code coupling
- Depend on the serious
- It is difficult to integrate other apps into a product line
- Projects are complex, bloated, large, and take too long to compile
- Integration testing is difficult
- For developers, you can only use the same development mode……
Advantages of componentized development:
- Clear project structure
- Clear code logic
- Small resolution size
- Rapid integration
- Ability to do unit tests
- High code utilization
- High iteration efficiency……
The essence of componentization is to split the foundation, function and business logic of existing projects or new projects into component libraries one by one, so that the host project can find the required functions in the separated component libraries and assemble a complete App.
3. Componentize necessary tool use
Components exist in the form of each POD library. So the way to combine components is to add and install each component by using CocoaPods. We need to make CocoaPods remote private library, which can not be sent to the company’s GitLab or GitHub, so that the project can be Pod download.
- Git basic commands:
echo "# test" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/c/test.git
git push -u origin master
Copy the code
- CocoaPods remote private library
1. Create Component Project
pod lib create ProjectName
Copy the code
2, Use the Git
echo "# test" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/c/test.git
git push -u origin master
Copy the code
3. Edit podSpec file
vim CoreLib.podspec
Copy the code
Pod::Spec.new do |s|
s.name = 'Component Project Name'
s.version = '0.0.1'
s.summary = 'summary'
s.description = <<-DESC
description
DESC
s.homepage = 'Remote warehouse address'
s.license = { :type= >'MIT', :file => 'LICENSE' }
s.author = { 'the writer'= >'the writer' }
s.source = { :git => 'Remote warehouse address', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.source_files = 'Classes/**/*.{swift,h,m,c}'
s.resources = 'Assets/*'
s.dependency 'AFNetworking'.'~ > 2.3'
end
Copy the code
4, Create the tag
//create local tag
git tag '0.0.1'Git tag 0.0.1 //localTag push to remote git push -- Tags or git push origin 0.0.1 //deletelocal tag
git tag -d 0.0.1
//delete remote tag
git tag origin :0.0.1
Copy the code
Verify Component Project
pod lib lint --allow-warnings --no-clean
Copy the code
Push To CocoaPods
pod repo add CoreLib [email protected]/CoreLib.git
pod repo push CoreLib CoreLib.podspec --allow-warnings
Copy the code
4. Split modules
Basic component library: The basic component library contains some of the most basic tool classes, such as amount formatting, mobile phone number/ID card/email effective verification, in essence is not dependent on business, will not be involved with business files.
Function component library: sharing encapsulation, image rotation, running light function, push function secondary encapsulation, that is, once developed, can be quickly integrated in the future.
Business component library: login component, real name component, message component, loan component, repayment component, each product line component, etc.
Middleware (component communication) : after each business component is split out, communication, parameter transmission and callback between components should be considered. At this time, a component communication tool class is needed to deal with it.
CocoaPods Remote private library: Each component that is split out exists as aPod and can be run independently.
Host project: A host project is a shell that looks for the components needed by the project in the component library and assembles them into an App.
5. Components are compatible with swift environment
Before componentization, the project was written in Objective-C language, and there was no capability to use Swift in the project. Considering that Swift will definitely be used in the future, we took the opportunity of this reconstruction to create component projects that are Swift projects.
Podfile needs to add ==use_frameworks! = =
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
inhibit_all_warnings!
use_frameworks!
target 'CoreLib_Example' do
pod 'CoreLib', :path => '.. / '
end
Copy the code
After supporting Swift environment, some objective-C tripartite libraries use static == library ==, reference tripartite library header file in OC file, will always be unable to find the header file. When we encountered this problem, we searched Baidu, but could not find a solution. It took a whole week to try.
UMengAnalytics, Bugly, and AMapLocation- no-idfa are used for remote private library management by CocoaPods to expose the files we write and reference the header files we write.
Pod::Spec.new do |s|
s.name = ' '
s.version = '0.0.1'
s.summary = 'Package Amap, Share, UmENG Framework.'
s.description = <<-DESC
DESC
s.homepage = ' '
s.license = { :type= >'MIT', :file => 'LICENSE' }
s.author = { ' '= >' ' }
s.source = { :git => ' ', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.source_files = ['Classes/UMMob/**/*.{h,m}'.'Classes/Bugly/**/*.{h,m}'.'Classes/AMap/**/*.{h,m}']
s.public_header_files = ['Classes/*.h']
s.libraries = 'sqlite3'.'c++'.'z'.'z. 1.1.3'.'stdc++'.'stdc++. 6.0.9'
s.frameworks = 'SystemConfiguration'.'CoreTelephony'.'JavaScriptcore'.'CoreLocation'.'Security'.'Foundation'
s.vendored_frameworks = 'Frameworks/**/*.framework'
s.xcconfig = { "FRAMEWORK_SEARCH_PATHS"= >"Pods/WDContainerLib/Frameworks" }
s.requires_arc = true
end
Copy the code
6. Communication between components
==#import “controller header file “== After the business controller is split, if one component wants to call the controller in another component, it is normal to directly ==#import” controller header file “==, now it is not possible to import from different components, so what should I do? The answer is to use the == message sending mechanism ==.
Ideas:
1. Each service component library has a controller configuration file (routing configuration file), which marks the key of each controller. 2. At each startup of App, the tool class of component communication needs to parse the controller configuration file (routing configuration file) and load it into memory; 3. Query the route configuration in memory, find the specific controller and dynamically generate the class, and then use the == message sending mechanism == to call functions, pass parameters, and call back.
((id (*)(id, SEL, NSDictionary *)) objc_msgSend)((id) cls, @selector(load:), param);
((void(*)(id, SEL,NSDictionary*))objc_msgSend)((id) vc, @selector(callBack:), param);
Or
[vc performSelector:@selector(load:) withObject:param];
[vc performSelector:@selector(callBack:) withObject:param];
Copy the code
Benefits:
Removes dependencies between controllers; Use the message sending mechanism of iOS to send parameters, callback parameters, and transparent parameters. Routing table configuration file, can realize dynamic configuration interface, dynamic interface generation; When the routing table configuration file is placed on the server, the jump logic of online App can be realized. By providing the key of the controller to H5, H5 can jump to the Native interface.
7. Componentized resource loading
Frameworks== Frameworks== (” Frameworks== Frameworks== “, “Frameworks==”, “Frameworks==”, “Frameworks==”); End WDComponentLogin. The framework). ==xib, image == used in the project, using the normal way to load, is not loaded, Reason is the xib, image path = = (engineering. App/Frameworks/WDComponentLogin framework/LoginViewController nib, engineering. App/Frameworks/WDComponentLogin. Framework /login. PNG) == changed.
Here are all the cases of loading niB files/image files in the component library:
/** load image @param imageName imageName @ from the main project mainBundle or from all components (component name.framework)return*/ (UIImage *_Nullable)loadImageNamed:(NSString *_Nonnull)imageName; /** Load images from the specified component. This is mainly used to load images from other components from the current component @param imageName Picture name @param frameworkName Component name @return*/ + (UIImage *_Nullable)loadImageNamed:(NSString *_Nonnull)imageName frameworkName:(NSString *_Nonnull)frameworkName; /** Load images from the Bundle folder of the specified component. @param imageName imageName @param bundleName Bundle folder name @param frameworkName component name @return*/ + (UIImage *_Nullable)loadImageNamed (NSString *_Nonnull)imageName bundleName (NSString) *_Nonnull)bundleName frameworkName:(NSString *_Nonnull)frameworkName; @param imageName imageName @param bundleName Bundle name @param bundleName Bundle name @param bundleName Bundle namereturn*/ + (UIImage *_Nullable)loadImageNamed (NSString *_Nonnull)imageName bundleName (NSString) *_Nonnull)bundleName; @param imageName imageName @param targetClass Current class @param imageNamereturn*/ + (UIImage *_Nullable)loadImageNamed:(NSString *_Nonnull)imageName targetClass:(Class _Nonnull)targetClass; /** Load images from the Bundle folder in the specified component (component name.framework). To load an image in a component, specify the full name of the image and the bundle name of the image. @param imageName the imageName. @param bundleName the bundle folder namereturn*/ + (UIImage *_Nullable)loadImageNamed (NSString *_Nonnull)imageName bundleName (NSString) *_Nonnull)bundleName targetClass:(Class _Nonnull)targetClass; /** Load project niB file eg :[_tableView registerNib:[WDLoadResourcesUtil loadNibClass:[WDRepaymentheaderView class]]forHeaderFooterViewReuseIdentifier: kWDRepaymentheaderView] @ param class nib file name @returnReturn the required nib object */ + (UINib *_Nullable)loadNibClass:(NSObject *_Nonnull)targetClass;Copy the code
Controller loading mode:
@implementation WDBaseViewController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
NSString *classString = [[NSStringFromClass(self.class) componentsSeparatedByString:@"."] lastObject];
if ([[NSBundle bundleForClass:[self class]] pathForResource:classString ofType:@"nib"]! = nil) {// have xibreturn [super initWithNibName:classString bundle:[NSBundle bundleForClass:[self class]]];
}else if ([[NSBundle mainBundle] pathForResource:classString ofType:@"nib"] == nil) {// No xibreturn [super initWithNibName:nil bundle:nibBundleOrNil];
} else {
return [super initWithNibName:(nibNameOrNil == nil ? classString : nibNameOrNil) bundle:nibBundleOrNil];
}
}
@end
Copy the code
UIView view loading mode:
OC version
+ (id)loadFromNIB {
if ([[NSFileManager defaultManager] fileExistsAtPath:[NSBundle bundleForClass:[self class]].bundlePath]) {
return [[[NSBundle bundleForClass:[self class]] loadNibNamed:[self description]
owner:self
options:nil] lastObject];
}else{
return [[[NSBundle mainBundle] loadNibNamed:[self description] owner:self options:nil] lastObject];
}
}
+ (id)loadFromNIB:(NSInteger)index {
if ([[NSFileManager defaultManager] fileExistsAtPath:[NSBundle bundleForClass:[self class]].bundlePath]) {
return [[NSBundle bundleForClass:[self class]] loadNibNamed:[self description]
owner:self
options:nil][index];
}else{
return[[NSBundle mainBundle] loadNibNamed:[self description] owner:self options:nil][index]; }}Copy the code
Swift version
@objc public static func loadFromNIB() -> UIView! {return (Bundle(for: self.classForCoder()).loadNibNamed(self.description().components(separatedBy: ".")[1], owner: self, options: nil)? .first as? UIView)! }Copy the code
8. Change swift code at the bottom of OC project
At present, we are doing the unification of the bottom level of OC and changing it to the code written by SWIFT.
1, Controller Base, Web controller Base uses OC code, because OC controller cannot inherit Swift, while Swift controller can inherit controller written by OC. Navigation bars, toolbars, routing, base components, functional components, mixed development plug-ins are all in Swift. 3. Swift mobile components are mostly completed. OC project and Swift project all use the developed mobile component library.
9. To summarize
After half a year of hard reconstruction, the project was finally divided into component development, and I have learned a lot from it. I hope I can make persistent efforts and make progress together with my colleagues.
The original address