Add more Routing lifecycle events to support RIB Tree tracing
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 193
- Forks
- 23
- PR merge metrics
- No merged PRs in 30d
Description
Original issue: https://github.com/uber/RIBs/issues/373
We are trying to improve the overall performance of our app. The first thing we think we should do beside of manual Profiling is tracking performance for specific code.
For UIView/UIViewController, swizzle help to automatically record a trace then send to a Telemetry tracing API. We also want to do that for Interactor/Router but they are pure Swift so no swizzle at all.
We tried to do that by adding more lifecycle events for Router and Interactor like below:
Router:
/// The lifecycle stages of a router scope.
public enum RouterLifecycle {/// Router did load. case didLoad /// Router will attach child case willAttachChild(Routing) /// Router did attach child case didAttachChild(Routing) /// Router will detach child case willDetachChild(Routing) /// Router did detach child case didDetachChild(Routing)}
open class Router: Routing {
/// Attaches the given router as a child.
///
/// - parameter child: The childRouterto attach.
public final func attachChild(_ child: Routing) {
assert(!(children.contains { $0 === child }), "Attempt to attach child: (child), which is already attached to (self).")
lifecycleSubject.onNext(.willAttachChild(child))children.append(child) // Activate child first before loading. Router usually attaches immutable children in didLoad. // We need to make sure the RIB is activated before letting it attach immutable children. child.interactable.activate() child.load() lifecycleSubject.onNext(.didAttachChild(child)) } /// Detaches the given `Router` from the tree. /// /// - parameter child: The child `Router` to detach. public final func detachChild(_ child: Routing) { lifecycleSubject.onNext(.willDetachChild(child)) child.interactable.deactivate() children.removeElementByReference(child) lifecycleSubject.onNext(.didDetachChild(child)) }}
Then we can do recursive observe for child RIBs lifecycle to add performance trace. This enables as we can add observer only one in Root:RootRouter:
func observeRouter(_ router: Routing) {
let didBecomeActive = router.interactable.isActiveStream.skip(1).filter { $0 == false }let willAttachChild = router .lifecycle .compactMap { lifecycle -> Routing? in if case .willAttachChild(let child) = lifecycle { return child } else { return nil } } .takeUntil(didBecomeActive) let didAttachChild = router .lifecycle .compactMap { lifecycle -> Routing? in if case .didAttachChild(let child) = lifecycle { return child } else { return nil } } .takeUntil(didBecomeActive) _ = willAttachChild .subscribe(onNext: { child in /// Recursive observe child router observeRouter(child) }) _ = willAttachChild .flatMap { child -> Observable<(Routing)> in return child.interactable.isActiveStream.filter { $0 }.map { _ in return child } } .subscribe(onNext: { child in /// Start Performance Tracing Interator didBecomeActive }) _ = didAttachChild .subscribe(onNext: { child in /// Stop Performance Tracing here })}
We can simply add observer at the Root RIB and for example, use os_signpost to tracing performance in Instrument:![]()
Adding more lifecycle event for Router also help to easier to implement a RIB Tree real-time viewer like https://github.com/srea/RIBsTreeViewerClient or https://github.com/imairi/RIBsTreeMaker
I really want to know your ideas and willing to open a PR if this will make sense to include in Uber RIBs
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the Router attachChild and detachChild lifecycle paths and the RootRouter observeRouter example described in the issue. Review how Router and Interactor lifecycle state is currently exposed, then clarify the accepted event API and tracing behavior with maintainers. Done means the lifecycle additions and their intended observation use case are agreed and implemented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- mobile-dev, observability
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100