Google Analytics for iOS - manual screen tracking won't work - ios

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.

Related

How to track all screens in an iOS App using Google Analytics?

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]?)
}

How can I continue to receive messages/data from apple watch after dismissing a secondary view contoller

I am writing an application that allows me to record my golf strokes and save them, so that I can view the scores on an iPhone. The problem is that while the app does receive messages after launching and opening secondary view controllers, it stops listening for messages after dismissing any secondary view controller. If I then switch to a secondary view controller, it will listen for messages until I dismiss it again. I do not know why the root View controller will only listen until a secondary view controller is opened.
I am using sendMessage and its corresponding receiver function to transfer the number of stokes between devices. Each view controller contains the receive message function.
The code in question is as follows:
import UIKit // or WatchKit on Apple watch
import WatchConnectivity
class ViewController: UIViewController, WCSessionDelegate {
var session: WCSession!
let userData = NSUserDefaults.standardUserDefaults()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
ScrollHoles.contentSize = CGSize(width: 360,height: 1090)
if (WCSession.isSupported()){
self.session = WCSession.defaultSession()
self.session.delegate = self
self.session.activateSession()
userData.synchronize()
}
}
The send message function is...
// Save Data From Strokes To standatdUserDefaults.
#IBAction func SaveButton(sender: AnyObject) {
let hole1Score = Strokes1.text;
userData.setObject(hole1Score, forKey: "hole1Strokes")
userData.synchronize()
session.sendMessage(["score1A": hole1Score!], replyHandler: nil, errorHandler: nil) // score1A becomes score1B on Apple watch
}
and the receive message function is...
// Recieve Data from Apple Watch And Save It To standardUserDefaults (e.g. # of strokes).
func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
let hole1Msg = message["score1B"] as? String // score1B becomes score1A on apple watch
let hole2Msg = message["score2B"] as? String
if (hole1Msg != nil){
userData.setObject(hole1Msg, forKey: "hole1Strokes")
}
if (hole2Msg != nil){
userData.setObject(hole2Msg, forKey: "hole2Strokes")
}
The constants and if statement continue for all 18 holes.
I am using the following to dismiss secondary view controllers.
#IBAction func backButton(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: {});
}
As explained in this answer, WCSession only supports a single delegate at a time.
Since you're setting up your session within view controllers, the session delegate is changing when you load another view controller. Although you may dismiss the secondary controller, it is still the delegate for the session.
This is why your root view controller no longer receives messages, as it is no longer the session delegate.
Recommended approach by Apple engineers:
As the previous answer and developer forum posts suggest, you can create a session wrapper which you activate in the phone's AppDelegate, have it handle app-wide messages from your watch, and then either update a data store, or directly pass that data on to observers.
In general, it's advantageous to setup Watch Connectivity as soon as possible (at launch, instead of in a specific view controller).
Off-site tutorial:
Natasha The Robot has an excellent tutorial which walks through using a Watch Connectivity session manager and data source in great detail.

How to override initial ViewController on First Run thru AppDelegate

I am using the following code to detect whether the app is running for the first time:
// Detect First Launch
let firstLaunch = NSUserDefaults.standardUserDefaults().boolForKey("FirstLaunch")
if firstLaunch {
print("Not first launch.")
Show()
}
else {
print("First launch, setting NSUserDefault.")
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "FirstLaunch")
}
I am using the following function to show the ViewController:
func Show(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier(“myViewController”) as! MyViewController
self.window?.rootViewController?.presentViewController(vc, animated: true, completion: nil)
}
I put both code under: func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
However, when I build and run the app, the initial view controller is shown as if nothing happened. I want “myViewController” to be shown FIRST (during the app’s FIRST EVER LAUNCH on the device.) After that first launch, however, the “myViewController” won’t be shown anymore, unless the user uninstalls and freshly reinstalls the app.
Does anyone know how to do it?
Since you want to show the custom screen on first launch and then dismiss it, I would run this code in your normal root VC's viewWillAppear(animated:Bool) method
EDIT:
Yes, you will have to modify the code a bit. For example, like so:
func Show(){
guard let sb = storyboard else { return }
let vc = sb.instantiateViewControllerWithIdentifier(“myViewController”) as! MyViewController
navigationController?.presentViewController(vc, animated: true, completion: nil)
}
After setting the value to user defaults, you have to save it using the synchronize method. This method is invoked periodically buy the system, but if you didn't synchronize before it was called, your value isn't saved.
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "FirstLaunch")
NSUserDefaults.standardUserDefaults().synchronize()
Documentation of synchronizefrom to Apple docs :
Writes any modifications to the persistent domains to disk and updates all unmodified persistent domains to what is on disk.
Discussion
Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic synchronization (for example, if your application is about to exit) or if you want to update the user defaults to what is on disk even though you have not made any changes.

Swift - Go to view when App enters background

How do I make my app go to a view when it detects that it enters the background? I have an authentication screen and I want that the user has to authenticate again when he comes back to the app. (Something like in 1password)
I tried to do something in appDelegate, but I just got a lot of errors.
let mapViewControllerObejct = self.storyboard?.instantiateViewControllerWithIdentifier("MainVC") as? MainVC
self.navigationController?.pushViewController(mapViewControllerObejct!, animated: false)
This is what I have tried to implement in applicationWillResignActive, but I got the error that appDelegate has no member called storyboard.
Thanks for the help
You can move to rootview controller and write this code in this function:
func applicationWillEnterForeground(application: UIApplication) {
if let navigationController = window?.rootViewController as?UINavigationController {
navigationController.popToRootViewControllerAnimated(false)
}
}

What is the proper way to handle imported files in iOS 9?

My app includes the ability to open .plist files in it. My info.plist file has all the necessary entitlements, and opening the document in my app works fine. The problem is I don't know the proper way to handle imported files, especially after the application:openURL:sourceApplication:annotation: method is deprecated in iOS 9. Also, it is possible my app will be open to a list of local files when the file is imported, so I need to be able refresh that list when the file is received.
Thanks in advance for any help.
I figured out how to do it (although it may not be the proper way).
Declare a global variable fileURL that will be an NSURL? using var fileURL: NSURL? outside of any class declarations.
Implement the following method in AppDelegate.swift:
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool
fileURL = url
// The next two lines make sure the app open up to the first screen (the one marked as "Is Initial View Controller" in the Main.storyboard file.
// If you would like to handle the file in a different ViewController then you will need to modify the code below.
let rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
self.window?.rootViewController = rootViewController
return true
}
In the class declaration for the UIViewController (or subclass such as UITableViewController) implement the following method:
override func viewDidAppear(animated: Bool) {
if fileURL != nil {
// handle file actions here (don't forget to use fileURL! not fileURL)
}
}

Resources