Implementing Google Analytics for iOS - ios5

As someone coming from experience with Flurry Analytics, can someone explain the correct location for implementing event tracking and custom variables in Google Analytics for iOS? The example that Google provides shoves everything into the AppDelegate. Not sure if they did that for the sake of brevity or not.I can see why the init call goes in the AppDelegate:
//AppDelegate.m
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[[GANTracker sharedTracker] startTrackerWithAccountID:#"UA-0000000-1"
dispatchPeriod:kGANDispatchPeriodSec
delegate:nil];
//...
}
But what about these calls that collect specific data related to a specific view? Can they go into their respective ViewControllers instead of the AppDelegate?
[[GANTracker sharedTracker] setCustomVariableAtIndex:1
name:#"iPhone1"
value:#"iv1"
withError:&error]
[[GANTracker sharedTracker] trackEvent:#"my_category"
action:#"my_action"
label:#"my_label"
value:-1
withError:&error]
[[GANTracker sharedTracker] trackPageview:#"/app_entry_point"
withError:&error]
Questions
1) What Google Analytics for iOS calls shown above need to be in the AppDelegate?
2) What Google Analytics for iOS calls shown above can be put into the ViewControllers?
Thanks

You put first part into AppDelegate, that's right.
In viewDidLoad method of each viewController put:
NSError *error;
if (![[GANTracker sharedTracker] trackPageview:#"/app_entry_point"
withError:&error]) {
// Handle error here
}
where #"/app_entry_point" should be the name of ViewController, for ex.: "/mainWindow".
Next piece of code used to track for your methods, used inside methods.
NSError *error;
if (![[GANTracker sharedTracker] trackEvent:#"my_category"
action:#"my_action"
label:#"my_label"
value:-1
withError:&error]) {
// Handle error here
}

Related

Why is this CMDeviceMotionHandler never called by CoreMotion?

I've included the CoreMotion framework in my project and imported the CoreMotion framework in the header of my view controller:
#import <CoreMotion/CoreMotion.h>
Then in -viewDidLoad I have this simple test code which I run on an iPhone 4 with iOS 4.3:
- (void)viewDidLoad {
[super viewDidLoad];
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
[motionManager setDeviceMotionUpdateInterval:0.1];
CMDeviceMotionHandler motionHandler = ^(CMDeviceMotion *motion, NSError *error) {
NSLog(#"foo");
};
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:motionHandler];
NSLog(#"merde!");
}
When I run the project on the device and move it around, I never get a "foo" log. Also, I tried setting a breakpoint in the motion handler block and it never halts there. But I do get the "merde!" log, so this code is definitely executed.
Why is core motion not calling my handler? My iPhone 4 is functional. Accelerometers and gyro work perfectly in other apps. There is no hardware bug.
I had very similar code running successfully, same applies to the available samples in the Event Handling Guide for iOS (there is only an appropriate one for gyro data). But there is one major difference:
All implementations hold their own reference to the opereation queue with operationQueue = [[NSOperationQueue currentQueue] retain]; or build their own. So hopefully this helps to get more than 'merde' in your logs ;-)

Push notification data retrieving

I want to get data from push notification message. I successfully get the data when app is on foreground and in background. but I am unable to get data when app is quit and user press view button on push notification. I write the code in application did finish launching. This code cause the app crash when pressing on View button of push notification message. If I comment the code then app doesn't crash. Kindly help me to fetch data from push notification when app is quit and user press view button on push notification. I'll really appreciate that.
if(launchOptions != nil){
NSDictionary *tmpDic = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (tmpDic!=nil) {
pushedMessage=[NSString stringWithFormat:#"%#",[[tmpDic objectForKey:#"aps"] objectForKey:#"alert"]];
pushedCountry=[NSString stringWithFormat:#"%#",[tmpDic objectForKey:#"country"]];
[self saveToDatabase];
}
}
Please try this...
Add this code to appdelegate.m => didFinishLaunchingWithOptions
if ([launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"]) {
[self application:application didReceiveRemoteNotification:launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]];
}
From ios7 we have the below delegate method to handle the push notification when the app is in back ground or not running
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
When you click view button
- (void)application:(UIApplication*)application didReceiveRemoteNotification: (NSDictionary*)userInfo
{
}
this method is called and userinfo will contain all data
Things which you are doing in didfinishlaunch method do in didReceiveRemoteNotification:
I don't totally understand what your asking but you can do stuff with what is being being push with a function in the app delegate
- (void)application:(UIApplication*)application didReceiveRemoteNotification:
(NSDictionary*)userInfo
{
NSLog(#"Received notification: %#", userInfo);
[self addMessageFromRemoteNotification:userInfo updateUI:YES];
}
Now you can add that data to core data or sqlite. This may not be relevant to your question but it's the best I can give based on what you've asked in your question.
See my comment on the above answer.
Here is Apple's doc:
If the app is not running when a push notification arrives, the method
launches the app and provides the appropriate information in the
launch options dictionary. The app does not call this method to handle
that push notification. Instead, your implementation of the
application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions: method needs to get the
push notification payload data and respond appropriately.
So you need to implement:
- (void)application:(UIApplication*)application didReceiveRemoteNotification:
(NSDictionary*)userInfo
As well as handling the launchOptions in:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

FBConnect's handleOpenURL Method Not Called by my AppDelegate

I have implemented the FBConnect SDK into my app, and it works perfectly on the simulator. I then modified my app's .plist file appropriately, and added the necessary method to my AppDelegate for when Facebook is installed on the device:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
NSLog(#"handleOpenURL Method was called and sent the following:");
NSString *urlString = [NSString stringWithContentsOfURL:(NSURL *)url];
NSLog(#"URL String: %#", urlString);
return [[flipsideViewController facebook] handleOpenURL:url];
}
From the above NSLogs and the observation that my application is returned to the foreground after authorizing access via Facebook, I infer that the FB App is handing off control to my application appropriately. Unfortunately, Facebook.m's "handleOpenURL:url" method does not actually get called as I request in my AppDelegate (i.e. neither of the below NSLogs are displayed).
- (BOOL)handleOpenURL:(NSURL *)url {
// If the URL's structure doesn't match the structure used for Facebook authorization, abort.
NSLog(#"handleOpenURL was handled by SDK. Good!");
if (![[url absoluteString] hasPrefix:[self getOwnBaseUrl]]) {
NSLog(#"handleOpenURL structure doesn't match the structure used for Facebook authorization. Aborting.");
return NO;
}
//...
As a result, my app's view controller (where I clicked my facebook button to begin with) is simply returned to the screen, and the code I placed in the 'fbDidLogin' method (used in my view controller to publish to the user's wall) never gets called as it did in the simulator.
What am I overlooking? What, if any, other information is needed to solve this problem? Any help would be very much appreciated, as I have been struggling with this for a while.
Important Summary Notes:
1.) Application runs as desired on simulator.
2.) handleOpenURL Method not called when I request it from my AppDelegate.
3.) I do not receive any errors/warnings.
4.) I can run the DemoApp on my device, and I notice that the handleOpenURL Method is called appropiately when requested by the 'DemoAppAppDelegate.'
Thanks in Advance!
I ended up having to make my facebook object a singleton (stored in the appDelegate). Not completely sure why this was necessary though, as I was not using the fb object in multiple viewControllers. Please comment if you know why this was necessary.
See the following links for more information:
handleOpenUrl and TabBar Application
Facebook SDK: Call from multiple viewControllers
Cocoa with Love: Singletons

Could not locate an NSManagedObjectModel for entity name - Universal App

I have a strange error in my app, which says:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+entityForName: could not locate an NSManagedObjectModel for entity name 'Book'
I know, there are hundrets of "Could not locate an NSManagedObjectModel for entity name" topis here and on the web, but the strange thing is, it's a universal app and the iphone app always works fine, only the ipad app is crashing on startup.
In the main AppDelegate, there is some code in two methodes and in the iphone / ipad AppDelegate I'm calling this code in applicationdidFinishLaunchingWithOptions like this:
if ([self modelExists] == NO) {
[self buildModel];
}
So it's the same way I call the code, but the ipad version crashes and the iphone version does not.
The only different is that the iPhone version uses a TabBarContoller (set up in IB) and the iPad version uses a single viewController (also set up in IB).
It happens on both, simulator and device.
I have no idea what to do. Hope you can understand what I mean ...
Thx a lot
Sebastian
EDIT:
I found out, when I run the iPhone Version, the code in the main AppDelegate is called as it should be, but when I run the iPad Version NONE code of the main appDelegate is called at all, so there is no managedObject created and that's the reason for the error. But why is no code run in the main AppDelegate ? Thx
EDIT2:
This is the code in my main AppDelegate now:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([self modelExists] == NO) { // Checks if the model is allready filled up or not. (Uses CoreData stuff of course)
// iPhone Version is fine here. iPad Version crashes.
[self buildModel];
}
[self buildInterface]; // Called in the iPhone or iPad AppDelegate to make the window visible etc.
return YES;
}
So didFinishLaunchingWithOptions is called in the iphone and in the ipad version. The iPad version just doesn't run the coredata stuff anyway, whereas the iphone version does run the coredata stuff as it should. Any idea what could be wrong? THX!
Maybe the app delegate is not running any code if it's just not set as an delegate of the application.
Look in your main NIB for the iPad version and make sure the "AppName App Delegate" is set as the delegate of the File's owner of that NIB.
I found my problem. Really strange ...
It was the code of "modelExists"
- (BOOL)modelExists {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"Book" inManagedObjectContext:__managedObjectContext]; //<- Crashed. Had to change it to self.managedObjectContext
request.predicate = nil;
NSError *error = nil;
...
Sebastian
I had this same problem. My app had been working fine for weeks in development, then suddenly it was crashing with this error. Changing managedObjContect to [self managedObjectContext] solved the problem.
I would love to know why....any experts out there? Why would the original code be able to resolve the call to managedObjectContext to the member function's implementation....and suddenly not be able to? There is no other static implementation visible to this code as far as I know.
Thank for posting this, save me many hours of messing around.
In my project, I had a navigation controller and I was getting this error when I tried to segue into a child view control.
The problem was that I needed to pass set the managedObjectContext. This is taken from the CoreDate Master/Detail example.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[[segue destinationViewController] setDetailItem:object];
// set the managedObjectContext, too, if you need it
[[segue destinationViewController] setManagedObjectContext:self.managedObjectContext];
}
}
Also, double check the segue identifier in Interface Builder matches what you have in this function (showDetail in this example).

Lauching App with URL (via UIApplicationDelegate's handleOpenURL) working under iOS 4, but not under iOS 3.2

I have implemented UIApplicationDelegate's
application:didFinishLaunchingWithOptions:
and
application:handleOpenURL:
according to specification, i.e.,
application:didFinishLaunchingWithOptions:
returns YES
and
application:handleOpenURL: opens the URL.
The code works under iOS 4 (in both cases, i.e., when the app is launched and when it becomes active from suspended state). However, the code does not work under iOS 3.2.
I give an answer to my own question. Finding out the solution took me a while and was quite frustrating. If you do an internet search you find some partial answers, but it still took me a while to work out the following solution and I do hope it adds some clarity.
So first, the recommended behavior of your app appears to be the following (see Opening Supported File Types in iOS Ref Lib):
Do not implement applicationDidFinishLaunching: (see the note at UIApplicationDelegate).
Implement application:didFinishLaunchingWithOptions: and check the URL, return YES if you can open it, otherwise NO, but do not open it.
Implement application:handleOpenURL: and open the URL, return YES if successful, otherwise NO.
In iOS 4, passing an URL to an app results in one of the following two behaviors:
If the app is launched then application:didFinishLaunchingWithOptions: is called and application:handleOpenURL: is called if and application:didFinishLaunchingWithOptions: returned YES.
If the app is becoming active from suspended state then application:didFinishLaunchingWithOptions: is not called but application:handleOpenURL: is called.
However, in iOS 3.2 it appears as if application:handleOpenURL: is never called! A hint that the behavior is different under iOS 3.2 can be found in Handling URL Requests. There you find that application:handleOpenURL: is called if application:didFinishLaunchingWithOptions: is not implemented, but applicationDidFinishLaunching: is implemented. But application:handleOpenURL: is not called if application:didFinishLaunchingWithOptions: is implemented.
Hence, one solution to make the code work under 3.2 and 4.0 is:
Open the URL in application:didFinishLaunchingWithOptions:, but then return NO to prevent that application:handleOpenURL: is called.
Open the URL in application:handleOpenURL:, in case you are under 4.0 and the app was in suspended state.
I found this solution in another post, but I was confused, because it contradicted the recommendation in iOS Ref Lib documentation (namely that we should return YES in application:didFinishLaunchingWithOptions:). (At that point I did not realize that the documentation contradicts it self).
I believe that the current iOS 4.0 behavior will be the future behavior I prefer the following solution:
Do not implement applicationDidFinishLaunching:.
Implement application:didFinishLaunchingWithOptions: and check the URL, return YES if you can open it, otherwise NO, but do not open it. If we are on 3.2, open the URL.
Implement application:handleOpenURL: and open the URL, return YES if successful, otherwise NO.
So in summary, I implement the iOS 4 behavior and added the following line to application:didFinishLaunchingWithOptions:
if([[[UIDevice currentDevice] systemVersion] hasPrefix:#"3.2"]) {
[self application:application handleOpenURL:url];
}
which make the code work under 3.2.
application:handleOpenURL: is now DEPRECATED.
As of iOS 4.2, you can use this for opening URLs:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
Documentation:
https://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplicationDelegate_Protocol/Reference/Reference.html
I started writing application which used Dropbox api. To understand concept, I ran a sample application using my Key/secret mentioned at dropbox/developer documentation.
Once sample app started working, I used same key/secret values for my application.
For sample app, implementation of handleOpenURL (or openURL on iOS 4.2) gets executed as expected. For some odd reason, it wasn't the case for my app. My app entered background in order to show login screen and authentication page of dropbox. After successful login and authentication, my app never entered foreground. It was true for both platform Simulator and device (iPad)
I tried almost everything listed on internet including this post. Thanks. There was NO success, though.
At last, it STARTED working for my application when I did following:
On simulator, select "iOS Simulator --> Reset Content and Settings", and reset.
On device, I deleted sample application related executable and which in turn delete cache associated to it.
Add the following to the end of application:DidFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
NSURL *url = (NSURL *)[launchOptions valueForKey:UIApplicationLaunchOptionsURLKey];
if (url != nil && [url isFileURL]) {
return YES;
} else return NO;
} // End of application:didFinishLaunchingWithOptions:
// New method starts
-(BOOL) application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
mvc = [nc.viewControllers objectAtIndex:0];
if (url != nil && [url isFileURL]) {
[mvc handleOpenURL:url];
}
return YES;
}
where mvc is my main ViewController, and nc my navigation controller.
Then in the MainViewController, do something like this:
- (void)handleOpenURL:(NSURL *)url {
[self.navigationController popToRootViewControllerAnimated:YES];
// Next bit not relevant just left in as part of the example
NSData *jsonData = [NSData dataWithContentsOfURL:url];
NSError *error;
NSDictionary *dictionary = [[NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error] objectAtIndex:0];
[self managedObjectFromStructure:dictionary withManagedObjectContext:self.context];
...
}
after declaring handleOpenURL in the .h of course.
Thanks goes to Christian for putting in the effort for this.