Hybrid Full-Click Tracking Design for iOS and Flutter Applications
This article presents a hybrid click‑tracking solution that combines the precision of code‑based instrumentation with the low‑cost advantages of full‑click (auto) tracking, detailing implementation patterns for iOS UIControl subclasses, UITableView delegation, and Flutter widget trees using inheritance and custom gesture wrappers.
Background : User data analysis and event tracking are essential for product decision‑making, requiring accurate capture of user interactions. The industry mainly uses three approaches: code‑based tracking, full (auto) tracking, and visual (selection‑based) tracking, each with distinct pros and cons.
Solution Overview : To leverage the strengths of both code‑based and full‑click tracking, a hybrid scheme is proposed, providing a supplemental click‑collection mechanism for temporary or product‑specific needs.
iOS Implementation :
For UIControls such as UIButton and UISwitch , a subclass (e.g., XXYButton ) overrides sendAction:to:forEvent: and swaps implementations via a category using method swizzling to inject identifier generation (class, selector, tag).
For UITableView , a subclass overrides setDelegate: and adds a swizzled method that creates a unique identifier for the didSelectRowAtIndexPath: callback, then forwards the call to the original delegate.
For third‑party public controls, a similar category approach is used to intercept the app‑specific click callback without affecting other business lines.
@interface XXYButton : UIButton
- (void)sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event {
[super sendAction:action to:target forEvent:event];
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method srcMethod = class_getInstanceMethod([self class], @selector(sendAction:to:forEvent:));
Method tarMethod = class_getInstanceMethod([self class], @selector(xxy_sendAction:to:forEvent:));
method_exchangeImplementations(srcMethod, tarMethod);
});
}
-(void)xxy_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
[self xxy_sendAction:action to:target forEvent:event];
NSString *identifier = [NSString stringWithFormat:@"%@/%@/%ld", [target class], NSStringFromSelector(action),self.tag];
}Similar swizzling is applied to UITableView and the host app’s custom click callbacks, generating identifiers that combine class name, method name, and element tag or index.
Flutter Implementation :
Page IDs are derived from GlobalKey when available; otherwise, a path is constructed by traversing the Element tree from the clicked widget up to a custom root state ( UCSamoCommonState ).
A unified gesture wrapper UCGesture() ensures all click entry points (e.g., GestureDetector , InkWell ) are captured.
Path generation concatenates widget runtime types and their indices, optionally pruning platform‑specific nodes (e.g., Column , Row ) to keep the identifier concise.
static String getPath(List list, String pagename) {
String finalResult = "";
if (pagename?.length > 0) { finalResult = "${pagename}[0]"; }
list.forEach((ele) {
finalResult += "/${ele.widget.runtimeType}[${_getIndex(ele)}]";
if (ele == list.last) { finalResult += "-[${ele.hashCode}]"; }
});
if (finalResult.startsWith('/')) { finalResult = finalResult.replaceFirst('/', ''); }
return finalResult;
}Optimizations remove redundant nodes and focus on key hierarchy levels, yielding shorter yet unique identifiers such as UCLookCardPageB[0]/Container[0]/Row[0]/UCGesture[0]-[1268] .
Additional utilities retrieve widget coordinates via RenderBox and aggregate click metadata (position, size, view ID, page ID) into JSON structures for downstream analytics.
Conclusion : By combining inheritance‑based swizzling on iOS and a custom state‑driven path extraction on Flutter, the proposed hybrid tracking scheme offers precise, low‑overhead click collection suitable for small‑to‑medium mobile projects without disrupting existing business logic.
HomeTech
HomeTech tech sharing
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.