// // MFFGHSAnalyticsProtocol.swift // MVM5G // // Created by Gujuluva Santharam, Ajai Prabhu on 20/05/20. // Copyright © 2020 Kyle. All rights reserved. // import Foundation import UIKit import MVMCore import MVMCoreUI protocol MFFGHSAnalyticsProtocol { func trackPage(data: [String: Any]?, additionalData: [String:Any]?) func trackAction(action: [String: Any]?, pageData:[String: Any]?, additionalData: [String: Any]?) func trackPageAppear(with pageJSON: [String: Any]?) func trackPageDisappear(with pageJSON: [String: Any]?) /** true means it will stop sending data to server when view appear to reduce data load. Temporary bool, will remove next release */ func stopInitialEvent() -> Bool } enum AnalyticsLogType: String { case UI = "UI" case BLE = "BLE" } struct AnalyticsPageInfo { var pageType: String? } extension MFFGHSAnalyticsProtocol { /** this boolean enable entire analytics feature. enable = false means nothing will send to server */ private var enable: Bool { let initialParams:[String: Any] = [:] return !initialParams.boolForKey("isFGAnalyticsDisabled") } /** enable time tracking feature. TODO - we are reducing call not sending data on start event. */ private var enableTimeTracker: Bool { return true } /** true means it will stop sending data to server when view appear to reduce data load. Temporary bool, will remove next release */ func stopInitialEvent() -> Bool { return false } func trackPage(data: [String: Any]?, additionalData:[String:Any]?) { let params: [String: Any] = [ "eventType" : additionalData?["eventType"] ?? "pageDisplay", "pageType" : data?["pageType"] ?? "NA", "template" : data?["template"] ?? "NA", "eventName" : additionalData?["eventName"] ?? "start", "pageClass" : additionalData?["pageClass"] ?? String(describing: Self.self), "epochSec" : Int(Date().timeIntervalSince1970), "tdn" : getVendorId(), "os" : "iOS" ] let mergedParams = params.merging(additionalData ?? [:]) { $1 } performRequest(params: ["data": mergedParams]) } func trackAction(action: [String: Any]?, pageData:[String: Any]?, additionalData: [String: Any]?) { /* let params: [String: Any] = [ "eventType" : "action", "actionPageType": action?["pageType"] ?? "NA", "actionTitle" : action?["title"] ?? "NA", "pageType" : pageData?.stringForkey("pageType") ?? "NA", "template" : pageData?.stringForkey("template") ?? "NA", "epochSec" : Int(Date().timeIntervalSince1970) ] performRequest(params: params) */ } func trackGemini(value: [String: Any], logType: AnalyticsLogType, pageInfo: AnalyticsPageInfo? = nil) { var result: [String: Any] = [ "os" : "iOS", "LogType" : logType.rawValue, "tdn" : getVendorId() ] if let pageInfo = pageInfo { result["pageType"] = pageInfo.pageType } let payload = result.merging(value) { $1 } performRequest(params: ["data": payload]) } func trackPKI(processStep: String, attributeList: GMFGAnalyticsAttributeList?) { var payload: [String: Any] = [ "processStep" : processStep, "os" : "iOS", "tdn" : getVendorId() ] if let attributeList = attributeList { payload["attributeList"] = attributeList.getList() //GlassboxManager.glassboxCustomEvent(forKey: GMFGConstant.Glassbox.fivegSetup, withParameters: ["gen3Data": attributeList.getList()]) } performRequest(params: payload, forPageType: "gen3Data") } func trackGen3Data(processStep: String, attributeList: GMFGAnalyticsAttributeList?) { var payload: [String: Any] = [ "processStep" : processStep, "os" : "iOS", "tdn" : getVendorId() ] if let attributeList = attributeList { payload["attributeList"] = attributeList.getList() } performRequest(params: payload, forPageType: "gen3Data", priority: .high) } func resetGMAnalytics() { LogRequestSerialiser.shared.resetTimer() } // MARK:- Get the Device UUID func getVendorId() -> String { let uuid = UIDevice.current.identifierForVendor?.uuidString return uuid ?? GMFGConstant.empty } private func performRequest(params: [String: Any], forPageType: String = "logUIData", priority: LogPriority = .normal) { if enable { LogRequestSerialiser.shared.queueLogRequest(params: params, forPageType: forPageType, priority: priority) } } // MARK:- Public API - Time Tracker func trackPageAppear(with pageJSON: [String: Any]?) { if !enableTimeTracker { return } //GMFGSelfInstallTimeTracker.shared.trackPageAppear(with: pageJSON) } func trackPageDisappear(with pageJSON: [String: Any]?) { if !enableTimeTracker { return } //GMFGSelfInstallTimeTracker.shared.trackPageDisappear(with: pageJSON) } } final class LogRequestSerialiser { static let shared: LogRequestSerialiser = LogRequestSerialiser() private lazy var requestQueue: [RequestItem] = [] private lazy var timer: Timer? = nil private init() { /* this avoid creating new instance */ } private var index = 0 fileprivate func queueLogRequest(params: [String: Any], forPageType: String, priority: LogPriority) { let requestItem = RequestItem(priority: priority, endpoint: forPageType, parameters: params) requestQueue.append(requestItem) startTimer() } @objc fileprivate func performRequestIfAvailable() { if requestQueue.count > index { let objectToBePerformed = requestQueue[index] if let reqParams = MVMCoreRequestParameters(pageType: objectToBePerformed.endpoint, extraParameters: nil) { index = index + 1 reqParams.add(objectToBePerformed.parameters) MVMCoreLoadHandler.sharedGlobal()?.loadBackgroundRequest(reqParams, dataForPage: nil, delegateObject: nil) } } if requestQueue.count <= index { resetTimer() } } fileprivate func resetTimer() { timer?.invalidate() timer = nil index = 0 requestQueue.removeAll() } fileprivate func startTimer() { if timer == nil { DispatchQueue.main.async { self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.performRequestIfAvailable), userInfo: nil, repeats: true) self.timer?.tolerance = 0.5 } } } } fileprivate struct RequestItem { var priority: LogPriority var endpoint: String var parameters: [String: Any] } fileprivate enum LogPriority { case high case normal } struct GMFGAnalyticsAttributeList { private var attrList: [[String: Any]] = [] @available(*, deprecated, message: "use add(name:, value:)") mutating func addAttribute(name: String, value: Any) { attrList.append([ "attributeName": name, "attributeValue": value ]) } mutating func add(_ name: String, _ value: Any) { attrList.append([ "attributeName": name, "attributeValue": value ]) } func getList() -> [[String: Any]] { return attrList } }