A. An overview
The ultimate goal of this scheme is to abstract out an intermediate layer to make unified jumps through the clutter of reference relationships. Modules are only coupled with the middle layer and decoupled between modules. The middle tier invokes the business components of a module in the form of runtime, independent of the specific module code.
2. Service scenarios
Any design without business requirements is a castle in the air. The following will analyze and design solutions based on specific business scenarios.
Here are the business scenarios that most apps will encounter:
- Open screen advertising is an operation position, need to jump to any
- The rotation chart is an operation location, need to jump to any
- There is also the possibility of arbitrary jump to native via bridge in webView
- Various push between modules
Simple and crude method, is the switch to determine the type, introduce various header files, directly jump. As the business expands, the following disadvantages become more obvious:
- There is no unified jump management. Each service maintains its own logic, scattering the jump logic everywhere.
- If you want to add a jump type, as described in 1, you must find the jump logic everywhere and add it all possible, which is very difficult to maintain.
- Direct reference of files between modules, serious coupling. If you delete a file between modules, it will directly affect all files that reference it. Ideally, individual modules can be compiled and run, and each module can be plugged and removed at will;
- The coupling between modules leads to module reuse. Ideally, a module can be compiled and run directly when copied to a new project;
Iii. Sorting and analysis
Through the above business scenarios, the most intuitive pain point jump management problems, but are essentially coupling problems, is the reference coupling between files. It is perfectly normal to call a class method, import the associated header file, and then make the call based on the API exposed by the header file. When services swell, references can be coupled directly within modules, but they must be decoupled between modules. There are two mainstream solutions in the industry:
- URLRoute solution represented by JLRoutes: The URL is the key and the block to be executed is the value, which is saved in a global map and permanently stored in memory.
- Mediator method: Collects all calls together and manages them using a Mediator. All callers call another module through an intermediary;
Individually, both schemes have their own advantages and disadvantages:
- URLRoute scheme uses the idea of Web for reference and fits the scenario of remote call very well. However, when making native calls at development time, consumers can feel twisted. We’re more used to calling methods, assigning values to methods. When using URLRoute, we need one more step of transformation logic to convert the method call to the URL form. This is both inconvenient and leaky. Because urls can only carry regular data, data in formats such as UIImage cannot be encapsulated in urls.
- The Mediator scheme is relatively friendly to native calls, but it doesn’t quite fit when it comes to jump requirements such as app://user/:userName. Detailed analysis can be found at https://casatwy.com/iOS-Modulization.html.
Iv. Jump scheme combining URLRoute and Mediator
Analysis here, in fact, the final plan is already looming. We want both the remote invocation experience of URLRoute, which perfectly fits the Schema model, and the local method invocation experience of Mediator.
Plan, as shown in the figure specific demo at https://github.com/xuzhenhao/ZHMediator. The following will be combined with the idea of componentization to explain how to land the whole program.
- Modular split.
- The first step is module partitioning. This is only considered for inter-module calls. Calls within modules are allowed to be coupled to each other, so proper module partitioning is important.
- Expose the Target object. The Target object exposes all of the services provided by the entire module. Moreover, because Mediator and Target interact through the Runtime, the method exposed by Target receives a dictionary as an argument, but the method implementation is responsible for restoring the passed dictionary to the parameters and calling the module’s specific classes and methods.
- Mediator classification for writing modules. As mentioned above, due to the limitations that the Runtime can only pass a series of parameters in the form of a dictionary, the responsibility of Mediator classification is to provide a series of parameters-friendly methods externally, but wrapped in the form of a dictionary in method implementation. The key definition must be the same as the key definition in the Target restore, and therefore divided into the same development maintenance.
- The remote invocation
- Configure URL resolution rules. Similar app: / / user / : userId/detail such URL, need through the parsing rules, resolve TargetName, ActionName, Params, delivered to the Mediator for processing. At present, it is only the secondary encapsulation based on JLRoutes. Of course, it can be replaced easily according to the demand. You are advised to configure these configurations in the category of AppDelegete+Routes.
At this point, the core part is introduced. Can complete the demo at https://github.com/xuzhenhao/ZHMediator.