本文主要介紹 火球買手 項目上的埋點方案(基於神策),以及一些心得。事實上在項目早期,咱們的埋點徹底依賴於第三方的全埋點技術,客戶端開發人員只須要作一些簡單的工做就能知足 BI 部門對數據的需求。但隨着業務增加,對數據的準確性和精細化的要求愈來愈高,以後不得不轉向手動埋點,固然這個也是基於第三方的。swift
目前 BI 部門對埋點數據要求能夠總結爲一句話:『從哪裏來到哪裏去』,好比在 Timeline 中點擊一篇文章進入詳情頁,那麼 Timeline 就是『從哪裏來』,詳情頁就是『到哪裏去』,固然實際項目中『從哪裏來』不僅須要一個維度定位,有時候須要兩三個維度才能定位。app
下面具體來講下 火球買手 項目上是如何埋點的,首先『頻道主頁』是項目中比較常見的頁面,它對應的 Model 是 Channel,而後當任何點擊進入的頻道主頁的事件觸發後都須要上報如下數據函數
{ module_name page_name channel_name channel_id }
page_name
指的是當前的 ViewController 名稱,module_name
主要用於區分同一個頁面內的不一樣入口,這樣子就能肯定『從哪裏來』,channel_name
和 channel_id
數據來自於 Channel,至於『到哪裏去』這裏就用埋點的 key 來代表,好比是 ChannelClick。code
頻道主頁在 APP 中入口衆多,即便在不考慮埋點的狀況下,一個統一的入口也是必要的事件
extension UIViewController { func pushToChannelDetailController(_ id: String?) { // ... } }
顯然這樣的方法根本沒法知足埋點上的需求,改造一下:開發
extension UIViewController { func pushToChannelDetailController(_ model: Channel?) { // ... let value = [ "module_name" : model.module_name, "channel_id" : model._id, "channel_name": model.name, "page_name": self.pageName ] SensorsAnalyticsSDK.sharedInstance()?.track(key, withProperties: value) } }
須要注意的是大多數狀況下入口函數只接受一個具象參數是行不通的,由於隨着項目的開發業務的迭代總有一些其餘的模型被加入,它們一樣帶有可以跳轉至頻道主頁的 id 屬性。還有 pageName 映射:get
extension UIViewController { var pageName: String { switch self { case is ChannelDetailController: return "頻道主頁" } } }
最後在具體的跳轉處設置 module_nameit
@objc func buttonAction(_ sender: Any) { model.module_name = "Header" pushToChannelDetailController(model) }
總體看下來雖然能夠應付點擊進入頻道主頁的埋點,可是仍是存在如下問題io
在實際開發中接收不一樣的數據模型跳轉到同一個頁面的狀況應該很多見,而且入口函數也是由於埋點把參數從相對抽象的 String 替換成了具象的 Channel,因此抽象 model 是首先要作的。不管接受什麼類型參數,傳給 ChannelDetail 仍是 id,那麼讓一個只有帶有 id 屬性的 protocol 去約束模型再合適不過了。class
protocol CommonModelType { var id: String { get } }
而後讓 Channle 遵照這個協議,利用 extension 是爲了看起來更解耦
extension Channel: CommonModelType{}
這時候入口函數就是這樣
func pushToChannellDetail(_ model: CommonModelType?)
能夠接受任何有 id 屬性的模型。爲了更抽象,甚至可讓 String 也遵照這個協議
extension String: CommonModelType { var id: String { return self } }
固然不一樣其餘能夠用來跳轉到頻道主頁的模型均可以這樣約束。
埋點數據除了 pageName 不屬於 model 之外,其餘都屬於 model 自己的屬性(module_name 屬於額外添加),因此和參數同樣,一樣用 protocol 約束 Channel ,讓 Channel 擁有一個直接用於提供數據的屬性。
protocol AnalyticsModelType { var analytics: [String: Any] { get } }
讓 Channel 同時遵照這兩個協議,而且添加 analytics 屬性
extension Channel: CommonModelType, AnalyticsModelType { var analytics: [String : Any] { let value = [ "module_name" : module_name, "channel_id" : id, "channel_name": name ] return value } }
最後完整的入口函數是這樣的
extension UIViewController { func pushToChannellDetail(_ model: CommonModelType?) { guard let model = model else { return } let viewController = ChannelDetailViewController() viewController.id = model.id navigationController?.pushViewController(viewController, animated: true) guard let value = model as? AnalyticsModelType else { return } var properties = value.analytics properties["page_name"] = pageName SensorsAnalyticsSDK.sharedInstance.track(key: "ChannelClick", properties: properties) } }
總的來講入口函數夠抽象,不管後期增長多少種模型只要它遵循CommonModelType
便可,甚至對於不熟悉項目的人來講直接傳入 id 也是能夠正常跳轉的。埋點的細節也被隱藏到了入口函數內,而須要上報的數據又由相應的模型負責提供只要它遵循AnalyticsModelType
便可。