preface

  • A burial site is a specific process to collect some information in the application, used to track the status of the application to use, as now the Internet is more and more convenient, and accurate analysis of user data into a new trend, user time spent on this page, click on the button, browse the content, the mobile phone models, such as network environment, and so on can be statistics

To do data analysis is nothing more than two kinds, one is the server through the interface call statistics, the other is the front-end buried statistics, of course, the front-end buried statistics can be more accurate statistics of more data information ~

Buried point solutions

It can be divided into three kinds: code burial point, visual burial point and no burial point

The first: code burial point

It is very simple to do a good job of burying point processing in the controller and button events that need to statistic the burying point

The second: visual burial point

Each event is identified according to its identity, and reference points are taken for the specified event. The event identification and parameter information are written in the configuration table, and the buried point statistics can be realized by dynamically delivering the configuration table

Third: no buried point

No buried point means that when the developer integrates the collection SDK, the SDK directly starts to capture and monitor all of the user’s behavior in the application, and reports all of it, without requiring the developer to add additional code

To introduce these three kinds of information, there are a lot of online materials, interested friends can search to see,

Meituan point evaluation front-end non-trace buried practice

Actual processing

Statistics on user browsing pages

The following is a very simple statistics user browse buried point method, in fact, using Runtime exchange viewDidLoad method

+ (void)load{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ kMethodSwizzling(self.class, @selector(viewDidLoad), @selector(kj_viewDidLoad)); }); } - (void)kj_viewDidLoad{ [self kj_viewDidLoad]; NSString *clazz = NSStringFromClass([self class]); NSDictionary *dict = @{ @"userid":KJHookInfo.shared.userid, @"viewController":clazz }; NSString *parameter = dict.jsonString.kj_aesEncryptKey(@"key"); //TODO: upload data}Copy the code

Statistics specified pages

As an update, browse through the specified controller buried, and dynamically return the interface controller that you want to count, store it in hookViewControllers, and then count it

- (void)kj_viewDidLoad{ [self kj_viewDidLoad]; NSString *clazz = NSStringFromClass([self class]); BOOL isHook = ({ BOOL isHook = NO; for (NSString *name in KJHookInfo.shared.hookViewControllers) { if ([name isEqualToString:clazz]) { isHook = YES; break; } } isHook; }); if (isHook) { NSDictionary *dict = @{ @"userid":KJHookInfo.shared.userid, @"viewController":clazz }; NSString *parameter = dict.jsonString.kj_aesEncryptKey(@"key"); //TODO: upload data}}Copy the code

The user page browsing duration was calculated

So one more thing to do is to look at how long you stay on a page and to deal with a buried point is simply to swap viewWillAppear: and viewDidDisappear:

+ (void)load{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ kMethodSwizzling(self.class, @selector(viewWillAppear:), @selector(kj_viewWillAppear:)); kMethodSwizzling(self.class, @selector(viewDidDisappear:), @selector(kj_viewDidDisappear:)); }); } - (void)kj_viewWillAppear:(BOOL)animated{ [self kj_viewWillAppear: animated]; Kjhookinfo.shared. time = CFAbsoluteTimeGetCurrent(); } - (void)kj_viewDidDisappear:(BOOL)animated{ [self kj_viewDidDisappear: animated]; NSString *clazz = NSStringFromClass([self class]); BOOL isHook = ({ BOOL isHook = NO; for (NSString *name in KJHookInfo.shared.hookViewControllers) { if ([name isEqualToString:clazz]) { isHook = YES; break; } } isHook; }); if (isHook) { NSTimeInterval time = CFAbsoluteTimeGetCurrent() - KJHookInfo.shared.time; NSDictionary *dict = @{ @"userid":KJHookInfo.shared.userid, @"time":time, @"viewController":clazz }; NSString *parameter = dict.jsonString.kj_aesEncryptKey(@"key"); //TODO: upload data}}Copy the code

User click Event

Most clickable UI controls are based on UIControl, and the core is an interactive method called sendAction:to:forEvent:, which provides a button that counts to a specific page

@implementation UIControl (KJHook) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ kMethodSwizzling(self.class, @selector(sendAction:to:forEvent:), @selector(kj_sendAction:to:forEvent:)); }); } - (void)kj_sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event { [self kj_sendAction:action to:target forEvent:event]; _weakself; kGCD_async(^{ if ([NSStringFromClass(weakself.class) isEqualToString:@"UIButton"]) { void (^kDealButton)(NSString *) = ^(NSString *clazz){ NSDictionary *dict = @{ @"userid":@"userid", @"centerX":[NSString stringWithFormat:@"%.2f",weakself.centerX], @"centerY":[NSString stringWithFormat:@"%.2f",weakself.centerY], @"viewController":clazz }; NSString *parameter = dict.jsonString.kj_aesEncryptKey(@"key"); NSLog(@"%@",parameter); //TODO: upload data}; kGCD_main(^{ kDealButton(NSStringFromClass([[target viewController] class])); }); }}); } @endCopy the code

Here, if you want to be more accurate, you can also give each button a tag value, of course, this project is relatively large, this is just to provide ideas, how to do the actual situation shall be subject to

It is worth mentioning here that, when exchanging methods, make sure to query globally for name conflicts (whether there are duplicate method names), otherwise it may appear that the method you buried is not executed at all. You can refer to my other article about Category introduction on the use of iOS Category Category and tool encapsulation

Burial point follow-up processing

Uploading Interface Data

Usually, after we bury the point, the method is to call a specified interface of the server, but there is a defect is that the traffic will be very large in the peak time, there is the possibility of exceeding the scope of the server

Image access statistics

Let’s start by introducing the structure of a url link, https://upload-images.jianshu.io/upload_images/1933747-82138031f05852ab.gif? tR8XBkv3BaBjjEeck9VbeiZauP73MdXWlhvmUq+BAFY=

Scheme: HTTPS host: upload-images.jianshu. IO path: /upload_images/1933747-82138031f05852ab.gif Query: tR8XBkv3BaBjjEeck9VbeiZauP73MdXWlhvmUq+BAFY=Copy the code

To access an image, you only need scheme:// + host + path

But the following format consists of scheme:// + host + path +? + query is also accessible

Therefore, we can put the buried point information in the query, and then get the contents of the buried point quickly and simply by counting the access records of the image

  • Bandwidth considerations: Images can also be used1 pixelSo it doesn’t take up much bandwidth on the server
  • Security considerations: We can also encrypt the required parameters
NSDictionary *dict = @{@"app":@"appname"};
NSString *parameter = dict.jsonString.kj_aesEncryptKey(@"key");
Copy the code

It is also available at https://upload-images.jianshu.io/upload_images/1933747-82138031f05852ab.gif? tR8XBkv3BaBjjEeck9VbeiZauP73MdXWlhvmUq+BAFY=

Subsequent thinking

We all know that the accessed images are data data, so can we also hide the data we want to give back to the client in data and parse it?

So what’s the best way to bury it? Thank you for sharing

Note: Some of the functions and Demo used in this article are from the tripartite library **KJEmitterView**, if there is a need for friends can bepod 'KJEmitterView'Introduction to

No trace buried point plan and actual combat to share the introduction to this end, there are relevant to add, writing is not easy, please also point a **Little stars* * portal