What are the different possible approaches of doing it?
There are two ways of doing it:
Inheriting GAITrackedViewController for all the viewcontrollers and then setting screenName property.
Extension of UIViewController with the following function:
func trackScreenView() {
let tracker = GAI.sharedInstance().defaultTracker
tracker?.set(kGAIScreenName, value: NSStringFromClass(type(of: self)))
tracker?.send(GAIDictionaryBuilder.createScreenView().build() as [NSObject : AnyObject]?)
}
Related
I'm trying to replicate Firebase Analytics behaviour, which automatically fire screen events whenever ViewController screen get's changed with another.
Though I'm able to find currently visible ViewController using :
UIApplication.shared.windows.first?.rootViewController?.presentedViewController
But I need some way to get notified for any change in rootViewController. I tried to observe this rootViewController using KVO, but I don't get any callback. I found that KVO only works on NSObject with dynamic properties.
Is there any way I could receive callback for change in ViewController? Since this will be a library project, I couldn't make changes in main code to support the feature.
Following solution worked for me:-
import Foundation
import UIKit
public extension UIViewController {
#objc dynamic func _tracked_viewWillAppear(_ animated: Bool) {
UserActivityTracker.startTracking(viewController: self)
}
static func swizzle() {
//Make sure This isn't a subclass of UIViewController,
//So that It applies to all UIViewController childs
if self != UIViewController.self {
return
}
let _: () = {
let originalSelector =
#selector(UIViewController.viewWillAppear(_:))
let swizzledSelector =
#selector(UIViewController._tracked_viewWillAppear(_:))
let originalMethod =
class_getInstanceMethod(self, originalSelector)
let swizzledMethod =
class_getInstanceMethod(self, swizzledSelector)
method_exchangeImplementations(originalMethod!, swizzledMethod!);
}()
}
}
In above code _tracked_viewWillAppear() is my custom function which I want to call my implementation before actual implementation called.
Then in AppDeligate class, call UIViewController.swizzle() method, as follows:-
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UIViewController.swizzle()
return true
}
It's my first time integrating google analytics in my app.I'm just following this official document here
I already have a tracker ID. I don't want to create configuration file.
How can I use that tracker ID and how can I integrate Google analytics?
Create extension of UIView Conroller.
extension UIViewController {
func setScreeName(name: String) {
self.title = name
self.sendScreenView()
}
func sendScreenView() {
let tracker = GAI.sharedInstance().defaultTracker
tracker.set(kGAIScreenName, value: self.title)
let builder = GAIDictionaryBuilder.createScreenView()
tracker.send(builder.build() as [NSObject : AnyObject])
}
func trackEvent(category: String, action: String, label: String, value: NSNumber?) {
let tracker = GAI.sharedInstance().defaultTracker
let trackDictionary = GAIDictionaryBuilder.createEventWithCategory(category, action: action, label: label, value: value)
tracker.send(trackDictionary.build() as [NSObject : AnyObject])
}
}
For Each view Controller viewdidload() you add following code
self.title = self.navigationItem.title!
self.sendScreenView()
I've got a problem with screen tracking on iOS. I'm doing it like it's presented in sample app from Google:
let tracker = GAI.sharedInstance().defaultTracker
tracker.set("My screen", value: "Value")
tracker.send(GAIDictionaryBuilder.createScreenView().build() as [NSObject : AnyObject])
And it doesn't work. It's correctly tracking events though, and when I'm subclassing View Controller as GAITrackedViewController it's tracking screens. But, I don't want to subclass my view controllers just to track screens. Does anyone got an idea?
You do not have to inherit your ViewControllers, just do this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let googleTracker = GAI.sharedInstance().trackerWithTrackingId("UA-YOUR_TRACKER_ID")
googleTracker.set(kGAIScreenName, value: "MyPageName")
let builder = GAIDictionaryBuilder.createScreenView()
googleTracker.send(builder.build() as [NSObject : AnyObject])
}
Of course you do not need to create the "googleTracker" object every time, do it on some central place and get it from there.
I am developing an iOS app using Google Maps API for IOS. And I installed the CocoaPod for my project and configure them according to tutorial on Google Developer. However, when I run my project, it says
*** Terminating app due to uncaught exception 'GMSServicesException', reason: 'Google Maps SDK for iOS must > be initialized via [GMSServices provideAPIKey:...] prior to use'
But I already call "GMSServices.provideAPIKey on the AppDelegate.swift. Following is the code:
....
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
GMSServices.provideAPIKey("***********************")
return true
}
....
(**************) is my API Key.
And because Google Maps API use Objective C, so I created a Bridging Header to import the library.
I tried to set breakpoint on [application:didFinishLaunchingWithOption]. But it will raise exception before run that function, which I think is very weird.
So confused about it. Thanks in advance.
Problem finally solved, the reason is that I initialize a fields using Google Maps library in the one model class and it will be created before the app run. So this error happens. When I moved this variable into the method, problem solved. Following is the code that causes error:
class PlaceManager {
let placeClient = GMSPlacesClient()
...
func getSuggestions(queryString:String) -> [String]{
...
}
}
After
class PlaceManager {
func getSuggestions(queryString:String) -> [String]{
let placeClient = GMSPlacesClient()
...
}
}
Instead of didFinishLaunchingWithOptions, move the call to willFinishLaunchingWithOptions. This method is called after state restoration has occurred but before your app’s window and other UI have been presented. (Which in your case might be a UI that consumes Google Map API)
func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
GMSServices.provideAPIKey("***********************")
return true
}
I just had the same problem. I created a GMSMapView object and it was initialized before maybe the api key could be read. So I moved it inside the viewDidLoad method and problem solved.
Before :
class ViewController: ..... {
let mapView = GMSMapView()
After :
class ViewController: ..... {
var mapView : GMSMapView?
override viewDidLoad(){
mapView = GMSMapView()
I have a problem with Swift 2 (Swift 3) and Google Analytics.
This is the line with the problem:
tracker.send(GAIDictionaryBuilder.createScreenView().build())
Xcode tell's me:
Cannot invoke 'send' with an argument list of type '(NSMutableDictionary!)'
Update for Swift 3 (2016.10.19)
let tracker = GAI.sharedInstance().defaultTracker
let build = (GAIDictionaryBuilder.createScreenView().build() as NSDictionary) as! [AnyHashable: Any]
tracker?.send(build)
Still an ugly approach, let me know if there's an cleaner conversion.
Original
Same here, struggling to resolve tons of errors.
What I did (deprecated):
var build = GAIDictionaryBuilder.createAppView().build() as [NSObject : AnyObject]
tracker.send(build)
Edit (2015)
Thanks to #George Poulos. . Recently they updated the options, now createAppView is deprecated, should use createScreenView instead.
var build = GAIDictionaryBuilder.createScreenView().build() as [NSObject : AnyObject]
tracker.send(build)
In addition to the accepted answer:
Changed this:
tracker.send(GAIDictionaryBuilder.createEventWithCategory("UX", action: "User sign in", label: nil, value: nil).build())
To this:
tracker.send(GAIDictionaryBuilder.createEventWithCategory("UX", action: "User sign in", label: nil, value: nil).build() as [NSObject : AnyObject])
This might be a bit of an overkill, but I prefer creating a short extension and not need to type the castings every time:
In any swift file, paste the following code:
extension GAIDictionaryBuilder
{
func buildSwiftCompatible() -> [NSObject:AnyObject]
{
return self.build() as [NSObject:AnyObject]
}
}
Then you can call buildSwiftCompatible() instead of the usual build():
tracker.send(GAIDictionaryBuilder.createScreenView().buildSwiftCompatible())
Have fun.
This is a solution I came up with.. Maybe it could help some of you. It's a struct you need to instantiate in every UIViewController, but it helps with the boilerplate.
import UIKit
struct Analytics {
fileprivate let viewController: UIViewController
fileprivate let tracker = GAI.sharedInstance().defaultTracker
init (forScreen viewController: UIViewController) {
self.viewController = viewController
}
func startTracking () {
let screenView = GAIDictionaryBuilder.createScreenView().build() as NSDictionary
guard
let tracker = tracker,
let build = screenView as? [AnyHashable: Any]
else { return }
tracker.set(kGAIScreenName, value: String(describing: viewController))
tracker.send(build)
}
}
class HomeViewController: UIViewController {
lazy var analytics: Analytics = {
return Analytics(forScreen: self)
}()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear() {
super.viewWillAppear()
analytics.startTracking()
}
}
For swift 3:
let build:NSObject = GAIDictionaryBuilder.createScreenView().build()
tracker?.send(build as! [AnyHashable: Any])
let build = GAIDictionaryBuilder.createScreenView().build() as [NSObject : AnyObject]
tracker?.send(build)