?? To build a user's behavioral statistics system in line with their company's needs, I believe it is the dream of many operators, but also the persistent pursuit of technology by developers.
Below, I share the user behavior statistics system that we have built for the company.
?? User behavior statistics (users Behavior Statistics, UBS) has been an indispensable part of mobile internet products, also known as buried point. For the product manager, the operator. Of course, the more you bury, the better the coverage. Nonsense nonsense is not much, here I mainly use AOP oriented to the idea of slicing programming to solve the problem. Blog: Reference blog address? First of all, I have not completely copied people's blogs here. Here is mainly along with others blog ideas to go, into a dead end, and then back to basics, with their own ideas to achieve.
The reason to write down other people's ideas to discuss. It is to show that the process of thinking is sometimes very important.
User behavior Statistics what?
?? We often say that the user behavior statistics, then the user behavior statistics is the main system, in my opinion, mainly divided into two categories:1, page statistics: PV ;2, Event statistics: Events.
Page Statistics: PV
?? The page count is just when the user enters a page. Keep the records in the row. Records are saved when the user leaves a page.
Send the saved data to the backend server when appropriate. Implementation code such as the following:
[UiviewcontrollerAspect_hookselector: @selector (viewdidappear:) Withoptions:Jkubsaspectpositionafterusingblock:^ (ID Data) {[ self jkhandlepv:data status:jkubspv_enter]; } Error:nil];[UiviewcontrollerAspect_hookselector: @selector (viewdiddisappear:) Withoptions:Jkubsaspectpositionafterusingblock:^ (ID Data) {[ self jkhandlepv:data status:jkubspv_leave]; } Error:nil];
A lot of blog post this kind of code think to conquer the problem. In fact, ignoring a very large problem, so simple coarse single-storm to deal with, will find the item in the Uiviewcnotroller of the two methods viewDidAppear:
, viewDidDisappear:
are will hook, created an additional performance overhead, very bad.
So I'm going to handle this. The hook operation is only for the meter page of the system. Examples of the present bodies are as follows:
+ (void) configpv{for (NSString*vcnameinch[[jkubsshareinstance].configuredata[Jkubspvkey] AllKeys]) {Classtarget =nsclassfromstring(Vcname); [Target aspect_hookselector: @selector (viewdidappear:) Withoptions:Jkubsaspectpositionafterusingblock:^ (ID Data) {[ self jkhandlepv:data status:jkubspv_enter]; } Error:nil];[Target aspect_hookselector: @selector (viewdiddisappear:) Withoptions:Jkubsaspectpositionafterusingblock:^ (ID Data) {[ self jkhandlepv:data status:jkubspv_leave]; } Error:nil];}}
Incident Statistics: Event
?? Event statistics are mainly recorded when the user triggers the event, and then send the recorded data to the backend server for processing at the appropriate time.
According to the article at the beginning of the blog, it is simple to divide the matter into Uibutotn,uicontrol,uigesturerecognizer and click on the UITableView cell cell trigger event. Click the Uicollectionview cell cell to trigger the event.
?? According to this idea, I first deal with events triggered by Uibutton,uicontrol:
+ (void)configUIControlEvent{ [UIControl aspect_hookSelector:@selector(sendAction:to:forEvent:) withOptions:JKUBSAspectPositionAfter usingBlock:^(id<JKUBSAspectInfo> data){ [self JKHandleEvent:data]; } error:nil];}
This is relatively easy to implement, I believe we have achieved.
?? UIGestureRecognizer
dealing with triggered events UIGestureRecognizer
is a first class cluster, and the tap,longpress,swipe,pan of gestures such as when we trigger events are the real classes that do not send events. I found the real class for sending events in the form of break points: UIGestureRecognizerTarget
The private method of sending the event is: _sendActionWithGestureRecognizer:
then I handle the event triggered by the hook operation:
+ (void)configGestureRecognizerEvent{ Class UIGestureRecognizerTarget =NSClassFromString(@"UIGestureRecognizerTarget"); [UIGestureRecognizerTarget aspect_hookSelector:@selector(_sendActionWithGestureRecognizer:) withOptions:JKUBSAspectPositionAfter usingBlock:^(id<JKUBSAspectInfo> data){ [self JKHandleEvent:data]; } error:nil];}
It is difficult to count the events triggered by gestures, but it has been achieved.
?? For an event triggered by clicking UITableView Cell Cell, click Uicollectionview cell to trigger the event. On my side, for example, click the UITableView cell cell-triggered event. Assuming JKBViewController
the implementation UITableView
of the proxy method tableView:didSelectRowAtIndexPath:
then my implementation such as the following:
+ (void)configureDelegateEvent{ [JKBViewController aspect_hookSelector:@selector(tableView:didSelectRowAtIndexPath:) withOptions:JKUBSAspectPositionAfter usingBlock:^(id<JKUBSAspectInfo> data){ [self JKHandleEvent:data]; } error:nil];}
Through this implementation we can do to click on UITableView cell cell triggered by the event statistics, but along with the author of the survey of the idea of this step by step. I have a bad feeling inside.
Into a dead end
The following are some of the issues that the blog author encountered during the development process
1。并非全部的事件都是有继承自UIControl的空间来发出的。比方:手势。点击Cell。2。并非全部的button点击了之后就立刻须要埋点上传?可能在button的响应方法中经过了层层的if(){ }else{ }最后才须要埋点。
4,对于代理方法该如何处理?5,假设非常多个button相应着一个事件该如何处理?
The needle is on the 1th, and I have combed through very many types of events. But there are still very many events that have not been statistically, for example, shaking the trigger. Events triggered by the pedometer. Tabbar Click triggered events, etc., there are many events that I might not have thought of. I now assume that according to the author's intention, according to the type of event triggered to one of the hook operation, the work of two pretty big. And there will still be omissions. Especially if it involves a lot of laws. Apple is not open to developers, and we deal with it more troublesome.
It's exhausting to be counted by the sender.
According to the 2nd, according to the intention of the person will be after the click on the inside of a layer of inference. How to bypass the layers of inference? I'll elaborate on that in the next step.
For the 4th. I've already done it on top.
For the 5th, in reality the situation does exist in different pages. Even a different button on the same page corresponds to the same event.
It is very troublesome to assume that the idea of a blog author is actually handled.
Basics
?? In response to the difficulties that arose above. I was wondering if there was a better way to solve it.
The first thing to think about is that we count user-operated events, not to count the user clicking on a button, or to do a gesture. A proxy method was tuned. And for the purpose of counting the user to do this operation. It's for shopping. Or for the sake of sharing. So I'm going to break the idea of the blog author, no more buttons, gestures. Unit Gueyang and other events to hook. Instead, a hook is made to the method that the user's purpose event triggers, and the event is an event, with no source points. This is the event that the hook prompts. I don't have to think about the logical inference in the middle layer. I only consider the purpose of the hook event.
For example, - (void)goShare;
I don't care if the user clicks on the button, or the tap gesture triggers the method, or the cell is selected. I just care about whether the method of sharing has - (void)goShare;
been called. Can I get into a record operation when I'm called?
In addition the only one determines a method. In addition to selector, there must be a relevant target (the implementation of the method, or the recipient of the message). Needle above the 5th, different button corresponding to the same event, under normal circumstances, the same target is different, we can be differentiated.
When it is clear that there is also the same event triggered by a different button on the same page, it is not too common in this case to wrap a layer outside the function. It would be nice to distinguish the individual names. Just Envetid still want the same.
?? In order to better facilitate the people. I wrote a pod library on my own line of thinking. Here's your plist file first:
watermark/2/text/ahr0cdovl2jsb2cuy3nkbi5uzxqvagfuagfpbg9uzze4/font/5a6l5l2t/fontsize/400/fill/i0jbqkfcma==/ Dissolve/70/gravity/southeast "alt=" here to write a picture descriptive narrative "title=" ">
You can see the PV field, each page can be set to the name of the page, but also some other information.
There is EventID under the event field, and at the same time it agrees that there are different triggering events under the same EventID.
Event 1 This Level field is written with detailed event content, mainly for the convenience of developer Reading.
JKVC1 Click, JKVC2 Tap, tap click. Select cell TableView These are all for the purpose of marking the source, so that developers can read. Also, assume that the event requires additional parameters to be configured. It is possible to add new content under the EventID sibling field.
Let's take a look at the code:
JKUBS.h
#Import<foundation/foundation.h>#Import "JKUBSAspects.h"extern NSStringConst*jkubspvkey;extern NSStringConst*jkubseventkey;extern NSStringConst*jkubseventidkey;extern NSStringConst*jkubseventconfigkey;extern NSStringConst*jkubsselectorstrkey;extern NSStringConst*jkubstargetkey;typedef Ns_enum (Nsinteger, jkubspvstatus) {jkubspv_enter =0,//Enter the pageJkubspv_leave//Leave page};@interfaceJkubs:nsobject@property(nonatomic,strong,readonly) Nsdictionary *configuredata;/** Single-instance method @return Singleton Object * /+ (instancetype) shareinstance;/** Importing configuration information through a JSON configuration file JSON configuration file or plist configuration file just import one just fine @param jsonfilepath json file sandbox path * /+ (void) Configuredatawithjsonfile: (NSString *) Jsonfilepath;/** Importing configuration information via Plist profile JSON configuration file or Plist profile just import one just fine @param plistfilename plist file name word (without suffix) */+ (void) Configuredatawithplistfile: (NSString *) plistfilename;/** Processing PV This method requires developer overloading for detailed operations @param data page information @param status Enter or leave the state of the page */+ (void) Jkhandlepv: (id<jkubsaspectinfo>) Data status: (Jkubspvstatus) status;/** Handling Events This method requires developer overloading for detailed operations @param Data event information @param eventId Event ID * /+ (void) Jkhandleevent: (id<jkubsaspectinfo>) Data EventID: (Nsinteger) EventID;@end
Jkubs.m
#import "JKUBS.h" NSString Const*jkubspvkey = @"PV";NSString Const*jkubseventkey = @"Event";NSString Const*jkubseventidkey = @"EventID";NSString Const*jkubseventconfigkey = @"EventConfig";NSString Const*jkubsselectorstrkey = @"Selectorstr";NSString Const*jkubstargetkey = @"Target"; @interface jkubs()@property(nonatomic,Strong, ReadWrite)nsdictionary*configuredata;@end @implementation jkubs StaticJkubs *_ubs =Nil; + (Instancetype) shareinstance{Static dispatch_once_tOncetoken;dispatch_once(&oncetoken, ^{_ubs = [jkubs new]; });return_ubs;} + (void) Configuredatawithjsonfile: (NSString*) jsonfilepath{NSData *data = [NSData Datawithcontentsoffile:jsonfilepath];nsdictionary*dic = [nsjsonserialization jsonobjectwithdata:data options:nsjsonreadingmutableleaves Error:Nil]; [Jkubs Shareinstance]. Configuredata= dic;if([Jkubs Shareinstance]. Configuredata) { [ SelfSETUP]; }}+ (void) Configuredatawithplistfile: (NSString*) plistfilename{nsdictionary*dic = [nsdictionarydictionarywithcontentsoffile:[[NSBundleMainbundle] Pathforresource:plistfilename oftype:@"Plist"]]; [Jkubs Shareinstance]. Configuredata= dic;if([Jkubs Shareinstance]. Configuredata) { [ SelfSETUP]; }}+ (void) setup{[ SelfCONFIGPV]; [ SelfConfigevents];}#pragma mark Pvconfig ----+ (void) configpv{ for(NSString*vcname in [[Jkubs shareinstance]. Configuredata[Jkubspvkey] AllKeys]) {Class target = nsclassfromstring (vcname); [Target Aspect_hookselector:@selector(viewdidappear:) Withoptions:jkubsaspectpositionafter usingblock:^ (IDData) {[ SelfJkhandlepv:data Status:jkubspv_enter]; } Error:Nil]; [Target Aspect_hookselector:@selector(viewdiddisappear:) Withoptions:jkubsaspectpositionafter usingblock:^ (IDData) {[ SelfJkhandlepv:data Status:jkubspv_leave]; } Error:Nil]; }}+ (void) Jkhandlepv: (ID<JKUBSAspectInfo>) Data status: (Jkubspvstatus) status{}#pragma mark EventConfig ----+ (void) configevents{nsdictionary*eventsdic = [Jkubs shareinstance]. Configuredata[Jkubseventkey];Nsarray*events =[eventsdic allvalues]; for(nsdictionary*dic in events) {NsintegerEventID = [Dic[jkubseventidkey] integervalue];Nsarray*eventconfigs = [Dic[jkubseventconfigkey] allvalues]; for(nsdictionary*eventconfig in Eventconfigs) {NSString*selectorstr = Eventconfig[jkubsselectorstrkey];NSString*targetclass = Eventconfig[jkubstargetkey]; Class Target =nsclassfromstring (targetclass); SEL selector = nsselectorfromstring (SELECTORSTR); [Target Aspect_hookselector:selector Withoptions:jkubsaspectpositionbefore usingblock:^ (ID<JKUBSAspectInfo> data) {[ SelfJkhandleevent:data Eventid:eventid]; } Error:Nil]; } }}+ (void) Jkhandleevent: (ID<JKUBSAspectInfo>) Data EventID: (Nsinteger) eventid{}
There are two ways to focus on that.
+ (void)JKhandlePV:(id<JKUBSAspectInfo>)data status:(JKUBSPVSTATUS)status。+ (void)JKHandleEvent:(id<JKUBSAspectInfo>)data EventID:(NSInteger)eventId;
Both of these methods need to be overloaded in the Jkubs category to do a detailed implementation.
such as the record of the page activity, the record of the event. Create a user behavior statistics system. My side is over. The event collection under the AOP thought. Detailed how to record, save, sent to send the backstage, here is not detailed explained.
Code
Use pods such as the following:
"JKUBS"
Note: I have made changes to the aspects library in the demo. To prevent name collisions, I've added a jkubs prefix to the unification of my side.
Welcome everyone to pick a fault, a exchange of learning.
iOS builds its own user behavior statistics system