This is the 26th day of my participation in the August More Text Challenge
IOS componentized use
I. Implementation scheme
Currently, componentization schemes can be divided into two types: URL/ Protocol registration scheduling and Runtime scheduling
The registered scheduling scheme has the following two problems:
- Named domain penetration
- Registration is not necessary because we need to maintain the registration list, which is part of the cost
Therefore, this paper chooses the runtime scheduling scheme to achieve componentization; That is to use CTMediator Target_Action componentization scheme to achieve; CTMediator scheme extended support to Swift component on the basis of supporting Objective-C component call, which could achieve the purpose of mixed call. The development of components could use Objective-C or Swift language.
The appearance of CTMediator scheme is to schedule target-action through runtime, but the essence of CTMediator scheme is to complete the scheduling without the need to move the business code.
Therefore, when providing target-action, we usually choose to let the Action do the corresponding work
Note for Swift project statement target-action:
Target
Objects must be inherited fromNSObject
Action
Methods must have@objc
The prefixAction
Method cannot have the first argumentArgument Label
Second, the introduction of middleware
pod ‘CTMediator’, ‘~> 25’
Iii. Use (Take the Login module as an example)
The Mediator folder stores the middleware Target_Login file opened by Login module, which is the concrete implementation of Target_Action scheme. CTMediator+Login file is the open interface of Target_Action scheme, and is also the method that external modules can use when calling Login module.
The suffix Login of the two classes need not be the same, but the mapping relationship needs to be specified in the internal definition. Keep the suffix the same for easy understanding.
Fourth, code analysis
1, Target_Login
The concrete implementation of CTMediator+Login method in the interface class exposed by Login module
class Target_Login: NSObject {
/// Get the login view controller
/// -parameter sender: Parameter
/// - Returns the controller
@objc func Action_getLoginVC(_ sender: NSDictionary) -> UIViewController {
let login = LoginViewController(a)return login
}
}
Copy the code
The Swift project declares precautions for target-action
Target
Objects must be inherited fromNSObject
;Action
Methods must have@objc
The prefix.- The responder
action
Do not take the first parameter of the methodArgument Label
with_
; - The responder
Target-Action
You need toblock
Converted intoclosure
;
2, CTMediator + Login
This type refers to the callable interface developed externally and is also the category of middleware CTMediator. Its internal implementation is as follows:
import CTMediator
let kCTMediatorTarget_Login = "Login";// corresponds to Login in Target_Login
let kCTMediatorAction_getLoginVC = "getLoginVC";// corresponds to the second half of the method name Action_getLoginVC in Target_Login
extension CTMediator {
/// Get the login view controller
/// - Returns the controller
func mediator_getLoginVC(a) -> UIViewController {
let loginVC: UIViewController = performTarget(kCTMediatorTarget_Login, action: kCTMediatorAction_getLoginVC, params: [kCTMediatorParamsKeySwiftTargetModuleName:AppInfo.appDisplayName], shouldCacheTarget: false) as! UIViewController
return loginVC
}
}
Copy the code
Call kCTMediatorParamsKeySwiftTargetModuleName swift project module need to add parameters, corresponding to project name
The string Login corresponding to kCTMediatorTarget_Login is the last part of Target_Login class. The kCTMediatorAction_getLoginVC string getLoginVC is the latter part of the Action_getLoginVC method corresponding to Target_Login.
The method name mediator_getLoginVC here is not related to the string defined above, but is given the same name to make it easier to understand method mappings
– (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params ShouldCacheTarget :(BOOL) An implementation of shouldCacheTarget:
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName];
// generate target
NSString *targetClassString = nil;
if (swiftModuleName.length > 0) {
targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];
} else {
targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
}
NSObject *target = self.cachedTarget[targetClassString];
if (target == nil) {
Class targetClass = NSClassFromString(targetClassString);
target = [[targetClass alloc] init];
}
// generate action
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
SEL action = NSSelectorFromString(actionString);
if (target == nil) {
// This is one of the places to handle non-responsive requests. The demo is made simple, so if there is no target to respond to, just return. In practice, a fixed target could be assigned in advance to handle the request at this time
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
return nil;
}
if (shouldCacheTarget) {
self.cachedTarget[targetClassString] = target;
}
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// This is where the non-response request is handled. If there is no response, try calling the notFound method corresponding to target
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// This is also where unresponsive requests are handled. Without notFound, the demo returns directly. In practice, you can use the fixed target above mentioned.
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
[self.cachedTarget removeObjectForKey:targetClassString];
return nil; }}}Copy the code
In the implementation of CTMediator, these defined strings are concatenated into the corresponding Target_Login and corresponding Action_getLoginVC method by the runtime mechanism and invoked.
Five, use and decoupling ideas
1. Use method:
let login = CTMediator.sharedInstance()?.mediator_getLoginVC()
Copy the code
Obtain the login controller page and perform operations.
2. Decouple the idea
- Only need to
Target_Login
Is dependent on its own module, butTarget_Login
The self is not exposed to the outside - Externally by calling
CTMediator+Login
To implement theTarget_Login
But classCTMediator+Login
withTarget_Login
Dependencies are not generated directly, but are implemented internally through the runtime. - De-model the module before passing
NSDictionary
The value of
Sixth, other
1. Special types of value transfer
Take the three-level region selection list as an example to demonstrate the closure pass value:
/// Select a region
func selectAreaAction(a) {
let area = CTMediator.sharedInstance()?.mediator_getAreaVC(callBack: { (dict) in
})
area!.hidesBottomBarWhenPushed = true
navigationController?.pushViewController(area!, animated: true)}Copy the code
CallBack is the closure transmission value after the selected region is returned and the information of the selected region is returned. This closure ultimately needs to be tied to the implementation class of the tier 3 region selection module via middleware.
1> Put the closure in the NSDictionary and pass it through the middleware to the region selection class
let kCTMediatorTarget_Area = "Area";// corresponds to the Area in Target_Area
let kCTMediatorAction_getAreaVC = "getAreaVC";// corresponds to the second half of the method name Action_getAreaVC in Target_Area
extension CTMediator {
func mediator_getAreaVC(callBack: @escaping (NSDictionary) - >Void) -> UIViewController {
let params = [kCTMediatorParamsKeySwiftTargetModuleName:AppInfo.appDisplayName, "callBack": callBack] as [AnyHashable : Any]
if let loginVC = performTarget(kCTMediatorTarget_Area,
action: kCTMediatorAction_getAreaVC,
params: params,
shouldCacheTarget: false) as? UIViewController {
return loginVC
}
return CTMErrorViewController()}}Copy the code
KCTMediatorParamsKeySwiftTargetModuleName this parameter for swift call namespace, corresponding to the project name
2> Bind the closure to the locale selection class
class Target_Area: NSObject {
/// Get the area selection view controller
/// -parameter sender: Parameter
/// - Returns the controller
@objc func Action_getAreaVC(_ data: NSDictionary) -> UIViewController {
typealias CallBack = (_ dictionary: NSDictionary) - >Void
let callBack: CallBack = data.object(forKey: "callBack") as! CallBack
let area = SelectAreaController()
area.selectedBlock = {(dict) in
callBack(dict)
}
return area
}
}
Copy the code