Dynamic iOS Routing Made Easy with TheRouterSwift and Service Integration
Discover TheRouterSwift, a Swift-based routing framework from Huolala that offers lazy registration, dynamic route handling, service injection, interceptors, caching, and redirection, enabling seamless navigation across iOS modules and facilitating cross‑platform compatibility with Android through flexible URL mapping and remote configuration.
Background
With growing community demand for Swift support, Huolala’s technical team open‑sourced TheRouterSwift, a Swift version of the existing Objective‑C routing component, to provide a unified solution for module decoupling and communication.
Features
TheRouterSwift delivers high‑availability routing for Swift developers, including page navigation, lazy route registration, router map export (JSON/Plist), automatic service registration, dynamic routing, chainable API, Objective‑C compatibility, service calls, asynchronous class fetching, and local cache of routes.
Detailed Feature List
• Page navigation (push, present, pop, window root, modal dismiss, etc.) • Lazy registration – routes are registered only on first openURL call • Router map export to JSON/Plist • Automatic service registration via runtime • Dynamic route addition, removal, and redirection • Chainable URL building • Interceptors for login checks and other pre‑navigation logic • Support for Objective‑C classes through protocol inheritance • Service invocation (local and remote) • Asynchronous fetching of eligible route classes • Local cache based on app version to speed up subsequent launches
Integration
Add the library via CocoaPods: pod 'TheRouter', '1.1.3' The Swift version requires Swift 5.0 or later.
Core Usage
Component Initialization
// Log callback for monitoring
TheRouter.logcat { url, logType, errorMsg in
debugPrint("TheRouter: logMsg- \(url) \(logType.rawValue) \(errorMsg)")
}
// Load router classes with optional cache
TheRouterManager.loadRouterClass([".The"], useCache: true)Route Registration
// Automatic registration via protocol conformance
extension TheRouterController: TheRouterable {
static var patternString: [String] { ["scheme://router/demo"] }
static func registerAction(info: [String: Any]) -> Any {
let vc = TheRouterController()
vc.qrResultCallBack = info["clouse"] as? QrScanResultCallBack
vc.resultLabel.text = info.description
return vc
}
}
// Manual registration examples
TheRouter.addRouterItem(RouteItem(path: "scheme://router/demo?desc=Simple", className: "TheRouter_Example.TheRouterController", desc: "Simple registration", params: ["key1": 1]))
TheRouter.addRouterItem(["scheme://router/demo": "TheRouter_Example.TheRouterController"])
TheRouter.addRouterItem("scheme://router/demo", classString: "TheRouter_Example.TheRouterController")Opening a Route
// Simple open
TheRouter.openURL("scheme://router/demo1?id=2&value=3")
// Chainable builder
TheRouterBuilder.build("scheme://router/demo")
.withInt(key: "intValue", value: 2)
.withString(key: "stringValue", value: "2222")
.navigation()
// Open with custom parameters and callback
TheRouter.openURL(("scheme://router/demo?id=2&value=3", ["model": model, "clouse": closure]))Route Removal
TheRouter.removeRouter(TheRouterViewCApi.patternString)Global Failure Handler
TheRouter.globalOpenFailedHandler = { info in
debugPrint(info)
}Interceptor Example (Login Check)
let login = TheRouterLoginApi.templateString
TheRouter.addRouterInterceptor([login], priority: 0) { info in
if LALoginManger.shared.isLogin {
return true
} else {
TheRouter.openURL(TheRouterLoginApi().build())
return false
}
}Objective‑C Integration
@interface TheRouterBController : UIViewController
@property (nonatomic, strong) UILabel *desLabel;
@end
@implementation TheRouterBController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor yellowColor];
[self.view addSubview:self.desLabel];
}
@end
public class TheRouterControllerB: TheRouterBController, TheRouterable {
public static var patternString: [String] { ["scheme://router/demo2", "scheme://router/demo2-Android"] }
public static func registerAction(info: [String: Any]) -> Any {
let vc = TheRouterBController()
vc.desLabel.text = info.description
return vc
}
}Dynamic Registration Mechanism
The framework scans classes whose names match a configurable prefix list, filters out system and CocoaPods classes, and registers any UIViewController.Type that conforms to TheRouterable. It uses objc_copyClassNamesForImage for efficient class enumeration.
Version‑Based Route Caching
// When the app is packaged, routes are cached by version to avoid re‑registration.
// During Xcode debugging, caching is bypassed to ensure new routes are recognized.Lazy Loading Details
@discardableResult
public class func openURL(_ urlString: String, userInfo: [String: Any] = [:], handler: complateHandler = nil) -> Any? {
guard !urlString.isEmpty else { return nil }
if !shareInstance.isLoaded {
return shareInstance.lazyRegisterHandleBlock?(urlString, userInfo)
} else {
return openCacheRouter((urlString, userInfo))
}
}
public class func openCacheRouter(_ uriTuple: (String, [String: Any]), handler: complateHandler = nil) -> Any? {
guard !uriTuple.0.isEmpty else { return nil }
if uriTuple.0.contains(shareInstance.serviceHost) {
return routerService(uriTuple)
} else {
return routerJump(uriTuple)
}
}Path‑Class Validation
public static func routerForceRecheck() {
let patternSet = Set(pagePathMap.keys)
let apiSet = Set(apiArray)
let diff = patternSet.symmetricDifference(apiSet)
assert(diff.count == 0, "URL spelling errors: \(diff)")
// Class‑name validation omitted for brevity
}KVO Class Name Handling
let NSKVONotifyingPrefix = "NSKVONotifying_"
if fullName.hasPrefix(NSKVONotifyingPrefix) {
let range = fullName.index(fullName.startIndex, offsetBy: NSKVONotifyingPrefix.count)..<fullName.endIndex
let subString = fullName[range]
pagePathMap[cls.patternString[0]] = "(subString)"
TheRouter.addRouterItem(cls.patternString[0], classString: "(subString)")
}Redirection and Remote Path Fix
let relocationMap: NSDictionary = [
"routerType": 1, // replace
"targetPath": "scheme://router/demo1",
"orginPath": "scheme://router/demo"
]
TheRouterManager.addRelocationHandle(routerMapList: [relocationMap])
TheRouter.openURL("scheme://router/demo?desc=Redirected to yellow view")
// Restore original route
let restoreMap: NSDictionary = [
"routerType": 4, // reset
"targetPath": "scheme://router/demo",
"orginPath": "scheme://router/demo"
]
TheRouterManager.addRelocationHandle(routerMapList: [restoreMap])Remote Service Invocation
let dict = ["ivar1": ["key": "value"]]
let url = "scheme://services?protocol=AppConfigLAServiceProtocol&method=openMiniProgramWithInfo:&resultType=0"
TheRouter.openURL((url, dict))Swift 5.9 Macros Consideration
The current lazy‑registration approach already meets performance goals (< 0.2 s) and provides compile‑time path constants via static variables, making additional macro support unnecessary.
About the Author
Huolala Mobile Technology Team.
License
TheRouterSwift is released under the Apache 2.0 license.
Feedback Group
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.
