TheRouter-iOS: A Lightweight Routing Middleware for Mobile Apps
TheRouter-iOS is a lightweight, annotation‑driven routing middleware that brings Java‑style dependency injection, hard‑code elimination, dynamic redirection, interceptors, and flexible navigation to iOS, offering a unified solution for complex mobile architectures and seamless integration with Android and web components.
TheRouter-iOS Lightweight Routing Middleware
TheRouter is a lightweight routing middleware created by Huolala that simultaneously supports Android and iOS . It introduces annotation capabilities to iOS, greatly improving the developer experience by moving away from traditional target‑action or protocol patterns and aligning with backend and Android routing concepts.
1. Why TheRouter
As application complexity grows, developers shift from simple MVC to MVP, MVVM, and other layered architectures. Decoupling layers and UI components while maintaining high cohesion and low coupling becomes challenging. Existing iOS middleware varies in implementation, making migration difficult and preventing a unified communication component across iOS, Android, H5, and Universal Links. TheRouter‑iOS addresses these gaps.
TheRouter provides four core features:
Dependency injection: similar to Java annotations, register routes by annotating VC classes or methods.
Hard‑code elimination: a built‑in script converts registered paths into static string constants.
Dynamic capabilities: supports adding redirects, interceptors, and backend‑driven dynamic routes.
Page navigation: supports standard VC or Storyboard push/present navigation.
2. Implementation Principles
2.1 Module Description
<ol>
<li><code>├── Classes</code></li>
<li><code>│ ├── TheRouter+Annotation.h</code></li>
<li><code>│ ├── TheRouter+Annotation.m</code></li>
<li><code>│ └── TheRouter.h</code></li>
<li><code>└── Resources</code></li>
<li><code> └── scan.py // annotation scanning and hard‑code processing script (referenced only, not compiled)</code></li>
</ol>2.2 Storage Structure
Each route is stored as a KeyPath and later looked up efficiently via valueForKeyPath.
Example storage for test://abc.com/login:
@{
@"test":@{
@"abc-com":@{
@"login":@{
@"_handler":block, // open callback
@"_placeholder":array, // placeholder info
@"_interceptor":block, // interceptor callback
@"_redirectUrl":string, // redirect link
}
}
}
}When opening a route, the flow is:
2.3 Annotation‑Based Dependency Injection
The concept originates from the BeeHive library, which uses __attribute__((used, section("__DATA,"#sectname))) to embed mappings into the Mach‑O file. TheRouter instead writes mappings to a pre‑generated plist, keeping the binary size small.
Annotation macros:
/// VC annotators, equivalent to call registController:clazzName storyBoard:nil path:routerPath
#define TheRouterController(routerPath,clazzName)
/// Storyboard VC annotators, equivalent to call registController:clazzName storyBoard:sbName path:routerPath
#define TheRouterStoryboardController(routerPath,sbName,clazzName)
/// Method annotator, equivalent to calling registSelector:selName targetClass:clazzName path:routerPath
#define TheRouterSelector(routerPath,selName,clazzName)Three annotation types:
TheRouterController – VC type (routerPath, clazzName)
TheRouterStoryboardController – Storyboard VC type (routerPath, sbName, clazzName)
TheRouterSelector – Event type (routerPath, selName, clazzName)
VC Type / Storyboard VC Type
Annotated VCs are instantiated automatically, and parameters are assigned via KVC. Navigation commands (push, present, etc.) are inferred from the current navigation controller.
Event Type
Methods annotated as events must accept a single parameter of type TheRouterInfo, which contains all routing information such as parameters and callback blocks. The method may return a value that becomes the result of openURLString or openPath, or it may invoke the asynchronous openCompleteHandler on the TheRouterInfo object.
The overall annotation discovery and registration flow:
2.4 Path Hard‑Code Handling
Registered route Path strings are transformed into static constants stored in a dedicated header file (similar to R.swift), reducing hard‑coded strings throughout the codebase.
Future versions will support adding documentation comments to routes and writing them into the header file for easy reference.
Overall process diagram:
3. Usage Introduction
3.1 CocoaPods Integration
pod 'TheRouter'3.2 Annotation Usage
Step 1 : Create TheRouterAnnotation.plist in the main bundle.
Step 2 : Add an Aggregate target to the project.
Step 3 : Add the scanning script to the new target.
Script parameters (example):
python3 $SRCROOT/../TheRouter/Resources/scan.py # script path
$SRCROOT/ # scan directory (usually project root)
$SRCROOT/TheRouter/ # directory for generated header files
$SRCROOT/TheRouter/TheRouterAnnotation.plist # annotation plist pathStep 4 : Register the host at app launch and add annotations to target VCs or service methods.
[TheRouter.shared registPathAnnotationsWithHost:@"hd://com.therouter.test"];Register a VC route:
TheRouterController(test/vc,TestViewController)
@interface TestViewController : UIViewController
@endRegister an event route:
#import "TestService.h"
#import "TheRouter_Mappings.h"
#import <TheRouter/TheRouter+Annotation.h>
@implementation TestService
TheRouterSelector(test/jump, jumpToTestVC, TestService)
+ (id)jumpToTestVC:(TheRouterInfo *)routerInfo {
UIViewController *vc = [TheRouter.shared openVCPath:kRouterPathTestVcVC cmd:TheRouterOpenCMDPush withParams:@{"title":@"123"} handler:^(NSString *tag, NSDictionary *_Nullable result) {
routerInfo.openCompleteHandler ? routerInfo.openCompleteHandler(tag, result) : nil;
}];
return vc;
}
@endStep 5 : After building, the target writes annotation data to TheRouterAnnotation.plist and generates TheRouter_Mappings.h, which should be added to the corresponding module.
3.3 Interceptors and Redirects
Interceptor: Can be set for full paths or wildcards. Return YES to allow the route, NO to block it. Asynchronous handling is possible by returning NO and later invoking continueHandle.
// Intercept any URL under hd://com.therouter.test/*
[TheRouter.shared registInterceptorForURLString:@"hd://com.therouter.test/*" handler:^BOOL(TheRouterInfo *router, id (^continueHandle)(void)) {
NSLog(@"will execute router %@", router.URLString);
return YES;
}];Redirect: Map an old path to a new one, useful for migrations or quick fixes.
[TheRouter.shared registRedirect:@"hd://test.com/test" to:@"hd://test.com/test/vc"];3.4 Executing Router Events
Routes support both synchronous and asynchronous returns. Example of opening a VC with parameters and a completion handler:
UIViewController *vc = [TheRouter.shared openVCPath:kRouterPathTestVcVC cmd:TheRouterOpenCMDPush withParams:@{"title":@"123"} handler:^(NSString *tag, NSDictionary *_Nullable result) {
routerInfo.openCompleteHandler ? routerInfo.openCompleteHandler(tag, result) : nil;
}];4. Conclusion
TheRouter is currently one of the most feature‑complete, lightweight routing solutions for iOS, combining the strengths of existing communication components while eliminating most of their drawbacks. Its annotation‑based registration removes manual route registration pain points, and it offers dynamic redirect and interceptor capabilities that can be driven by backend services for graceful degradation or dynamic navigation.
Contributions are welcome via GitHub issues and pull requests.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
