Mobile Development 20 min read

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.

Huolala Tech
Huolala Tech
Huolala Tech
Dynamic iOS Routing Made Easy with TheRouterSwift and Service Integration

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

Feedback QR Code
Feedback QR Code
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

iOSSwiftDynamic RegistrationService Injection
Huolala Tech
Written by

Huolala Tech

Technology reshapes logistics

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.