We are in the process of turning our native iPad app into a hybrid app. Some functionality and UI will remain in native code and other functionality will be implemented in HTML that will be served from our servers and will also be available offline.
The main issue I encounter now is with using Google Analytics:
The existing native code uses the GA SDK for IOS and I planned on using the web API for the web part, however I can't find how the data from both channels can be used together in GA as the data stores seem to be distinct.
Furthermore, I plan to use Google Analytics' Content Experiments for A/B testing the web part but conversion goals might be ones achieved in the native part.
Anyone have any experience with analytics on hybrid apps or alternative solutions.
Thanks
You really want to use the SDK. It has some features that will come handy for mobile apps like crashes, play store integration. It also sends data in batches to improve battery usage and also can queue hits while the app is offline to be sent when online. You won't be able to emulate that with the Javascript implementations.
So what you need to write is Javascript methods that send data from the WebView back to the Native Part of the App. This other Stack Overflow thread has more detail on how to do that.
So the javascript to track Google Analytics interactions could look something like this.
var _gaq = {};
_gaq.push = function(arr){
var i, hit;
hit = arr.slice(1).join('&');
location.href = 'analytics://'+arr[0]+'?'+arr;
};
Now this will work as a replacement for your ga.js file, you can still use the same API as you use on _gaq today on your Web App, and the adapter above will sends its data to te native part of the APP. And then you just need to write the native part that will intercept that HTTP request and use the native SDK to issue the Google Analytics functions.
A normal _gaq.push(['_trackPageview', '/homepage']); will become a uri like analytics://_trackPageview?/homepage, now you just need to intercept and parse that on the Native part.
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = [request URL];
NSLog(#"Hit detected %#", url.absoluteString);
if ([[url scheme] isEqualToString:#"analytics"]) {
id<GAITracker> tracker = [[GAI sharedInstance] defaultTracker];
if ([url.host isEqualToString:#"_trackPageview"]) {
// Get the page from parameters and then track the native View.
// (...)
[tracker trackView:page];
}
else if ([url.host isEqualToString:#"_trackEvent"]) {
// Get the event parameters from url parameters and then track the native GA Event.
// (...)
[tracker trackEventWithCategory:cat
withAction:act
withLabel:lab
withValue:val];
}
// Check for all other analytics functions types
// (...)
// Cancel the request
return NO;
}
// Not an analytics: request.
return YES;
}
I hope it have given you a good starting point. Good luck.
Indeed a challenging configuration.
Have you looked into using analytics.js (Universal Analytics) for the web part ? Then you may be able to feed data into a single App profile
Else, you could send all the tracking calls from your backend, by using a server side implementation of the Measurement Protocol but you'll probably loose usage of Content Experiment !
I use http://www.flurry.com/ for my apps and Google Analytics for my other stuff. I never mixed both of them in the same app but I'm guessing it's doable. I'd sugest to check out flurry first. There's a good chance it will suffice also for an hybrid app.
Related
I'm working on a project in which, I have UIWebView where I need to call some JavaScripts on a webpage in this UIWebView and that particular JavaScript will call my Objective-C native method.
To do that, I'm fetching context from UIWebView and setting my Objective-C object to the context and this object I'm fetching in JavaScriptand In JavaScript, object I'm calling a Objective-c with this function/method.
Here is the code I'm using to do above,
JSContext *context = nil;
context = [_webView valueForKeyPath:#"documentView.webView.mainFrame.javaScriptContext"];
// enable error logging
[context setExceptionHandler:^(JSContext *context, JSValue *value) {
NSLog(#"WEB JS: %#", value);
}];
context[#"MyObj"] = self;
So my simple question is, whether this way of doing this is ok, in terms of Apple Store. I mean, is there something that can cause my app to get rejected my Apple for App Store?
Thanks.
There is nothing currently in the App Store guidelines that would prohibit what you intend to do.
The ability to initiate functionality from a web server, even if it's via JavaScript in a web view, is common behavior.
Your app is run in a sandbox, so any security concerns are limited to your app. You're not adding code to your app, which is strictly prohibited. You're simply including functionality that may be called at a later time.
The short answer is that it depends on who reviews your app.
I have apps that do something very similar, and they were approved without issue. I have other apps rejected for doing the same thing because the JavaScript was considered "downloaded executable code".
To answer your specific question, I do not believe what you are doing in your Objective-C code will cause a problem with Apple's review, but depending on the source of the JavaScript, that may.
Bottom line is that Apple's review guidelines are still interpreted by humans at Apple, and that interpretation is not perfectly consistent.
I am working with Parse SDK in my iOS Application. I have integrated Parse Facebook SDK with my Application. I just want to do an analytics based on Users not overall count like we can give read count or something. (e.g) I want to do know the current user's access count of modules which I am having in my application and overall How many users have visited the particular module in certian period of time.Please help me out on this.Thanks in advance
What you are looking for is the [PFAnalytics trackEvent: dimensions:] call. This allows you to track an event with a particular name as an NSString and provide dimensions as an NSDictionary.
Here's an example of what you could put in the loading code for each module:
NSDictionary *dimensions = #{
// Define the user via whatever unique ID you use
#"user": userId,
// Define the name of the module that is being loaded
#"module": #"moduleName",
};
// Send the dimensions to Parse along with the 'search' event
[PFAnalytics trackEvent:#"moduleLoad" dimensions:dimensions];
To get a total number of events, you will need to use the Parse.com dashboard. You may also need to download the events log and sum it via Excel.
Also, if you are looking for analytics tracking that is more robust at the user-level, I recommend http://www.mixpanel.com
This SO post addresses how to customize the UIActivityViewController by excluding services like AirDrop or printing.
It also mentions this Apple doc which highlights the stock services supported, but how do we identify other supported end points like Line and other messaging apps?
Specifically:
(1) Do Skype, Kakao, Line, Viber, WeChat, Kik, WhatsApp, and Facebook Messenger (not Facebook proper) have end points?
(2) What are those end points?
You can't do that currently on iOS 7, because no application can talk directly to other applications yet for security reasons. One of the highlights of the last WWDC was the introduction of extensions for iOS 8, which will make this possible; you can read how in the Creating Action Extensions example.
There are however attempts at fixing this. A notable example is IntentKit, which works by having a repository of known apps.
What is IntentKit?
IntentKit is an open-source iOS library that makes it easier to link to other apps. It's sort of like Android's Intents or Windows Phone's Contracts.
Another example of one of such attempts is OvershareKit
Why OvershareKit?
Sharing is far too cumbersome to implement on iOS. UIActivityViewController is too limiting, and rolling your own library is too time-consuming. Most devs end up settling for underwhelming sharing options for lack of the time or inclination to make something better.
OvershareKit makes it trivial to add rich sharing options to your iOS apps.
How to know if an application is installed?
Even though you can't discover them. If you know the application you're looking for and what kind of URL Scheme it responds to, then you can check if your app is able to open that kind of URL.
That's what IntentKit is for, it's a repository of knowledge about applications, the URL Schemes they respond to and the kind of actions they can perform. With the introduction of extensions.
For example, you can check if Facebook is installed by checking if you can open a fb:// URL.
BOOL isFacebookInstalled = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:#"fb://"]];
About IntentKit's inner workings
Internally, IntentKit will check for that same thing, as you can see in INKActivity's implementation:
- (BOOL)canPerformCommand:(NSString *)command {
if (!self.actions[command]) { return NO; }
if (self.presenter) {
return [self.presenter canPerformAction:command];
} else {
NSURL *url = [NSURL URLWithString:[self.actions[command] urlScheme]];
return [self.application canOpenURL:url];
}
}
Info about requested UIActivity services:
Skype uses the "skype:" URI, more info in the official documentation
Kakao & Line, with DCActivity (there seems to be an official API for Kakao, but the documentation is in korean)
Line, with LINEActivity
WeChat, with WeixinActivity (there's also an official API with which you can make your own UIActivity)
WhatsApp uses the "whatsapp:" URI, more info on the official FAQ, there are also various UIActivity implementations for WhatsApp, take a look at them in cocoapods.com
Facebook Messenger uses the "fb-messenger:" URI, more info in this other answer by tia, also see workarounds.
Kik has a public API, but no SDK nor UIActivity implementation that I know of. Also, see workarounds.
Viber has no SDK nor public API, see workarounds.
Workarounds
Most of these services are based on known protocols, or slight variations of them. For example, you can use XMPP (aka Jabber) to directly send messages to a Facebook IM or Kik account; some people say that Viber seems to use a modification of SIP for signaling with VoIP phones. So you could work around some SDK/API limitations by using the underlying mechanisms.
SDK or API?
If all you need is to send a message to those services, I'd argue that you don't really need to communicate with the installed application via an SDK or URL Schemes, I haven't been able to test the Big Emoji app you mentioned, as it just crashes on iOS 8, but if it's using the services API's, you could easily work it out by using Charles or Wireshark.
Presumably they are adding a bunch of their own custom actions, as described in this answer.
There is no central repository for third-party sharing support before iOS 8. You can check for the other apps' presence by using URL Schemes. To do this, you'll have to look at each app's documentation and figure out what schemes they accept, then do something like this:
NSArray* items = /* stuff you want to share */
NSMutableArray* activities = NSMutableArray.array;
if ([UIApplication.sharedApplication canOpenUrl:#"whatsapp://url"])
{
UIActivity* activity = /* create activity for whatsapp */
[activities addObject:activity];
}
if ([UIApplication.sharedApplication canOpenUrl:#"facebook://url"])
{
UIActivity* activity = /* create activity for facebook */
[activities addObject:activity];
}
// ... repeat for other services ...
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:activities];
// show the VC however appropriate.
In addition to #NinoScript, you can find here the URL schemes for the iOS apps (inside the .plist files) which is provided by IntentKit as he mentioned.
Here is a summarized list from the project:
1Password ophttps://{{{url}}}
Chrome: googlechromes://{{{url}}}
Gmail: googlegmail:///co?to={{recipient}}
Google Maps: comgooglemaps://?q={{query}}
Google+: gplus://plus.google.com/{{userId}}
Safari: http://{{{url}}}
For a full URL-schemes search the git project.
We are using Google Analytics to track events, but events don't appear to track 100% of the time. Sometimes they track, and sometimes they don't. We're not exceeding quota limits per session (at most we have 20 events per session). That shouldn't be the issue.
The tracking fails to work consistently on our normal website as well as our HTML5 mobile app version, though it's far less reliable with the HTML5 mobile app version.
Code:
var share_url = 'http://twitter.com/intent/tweet?text=';
// Log in GA
_gaq.push( ['_trackEvent', 'Share Twitter', ''] );
// Open URL in browser
open_external( share_url + encodeURIComponent( msg ) );
function open_external( url ) {
window.open( url + '#phonegap=external' );
}
_gaq.push( ['_trackEvent', 'Share Twitter', ''] );
This won't do anything.
For _trackEvent, the third argument (where you pass an empty string) is required. It's the 'Action' parameter. But an empty string is falsey, so it just fails silently.
Pass any value there, and it'll work.
Is this a reduced case? You shouldn't be seeing any events with that code.
Are you positive that you waited long enough for the data to be processed by Google? Especially since some tracking seems to be working.
I had the same behaviour (in a mobile app btw) but after waiting for more than a day it still came through. This still occurs on a daily basis... Hope this is the case for you too.
I'm not exactly sure what your problem can be, so I will throw some idea.
Most of them are obvious but it might help.
On your website:
Are you sure your embed the Google Analytics code snippet on every page who required tracking?
Load Google analytics from the asynchronous way.
On Google analytics check on Real time > Overview. As full report are delayed from few hours.
If you url is something like httq://localhost/ then your need to add the javascript code _gaq.push(['_setDomainName', 'none']); please read this post
It can't work with file:// url
(Probably not) Check if you can download the JavaScript from Google Analytics. Maybe your proxy block Google analytics tracking ?
In your application:
You are using embedded HTML 5 page within your app. So the way your open a page is using file://PATH_TO_MY_DIR/index.html as it's on your hard drive you can't send data to Google analytics.
As you are probably using PhoneGap, you need to "jump out" of your HTML page into native Objective-c code and send the event from your Objective-C code. Read Google Analytics and PhoneGap and this google group thread
Hope it help.
The problem is third parameter in:
_gaq.push( ['_trackEvent', 'Share Twitter', ''] );
The 2nd element of the array should be the category and the 3rd should be the action. For example:
_gaq.push( ['_trackEvent', 'Share', 'Twitter'] );
You can verify this yourself by pasting each of the above into your developer console (F12 in Chrome, Ctrl-Shift-K in Firefox) and watching the network traffic.
Reference:
https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEventTracking
Right now I'm able to launch something like the mail app with this call:
NSURL* mailURL = [NSURL URLWithString: #"emailAddress#example.com?cc=&subject=Feedback"];
[[UIApplication sharedApplication] openURL: mailURL];
However, what I'd like is for mail to return control back to my app automatically once the user finishes sending an email. Is this possible?
This is not possible at the moment.
There has been a bit of an effort to try and get apps to accept a url callback parameter that would indicate which app control should be returned to.
See http://x-callback-url.com/
The goal of the x-callback-url specification is to provide a standardized means for iOS developers to expose and document the methods they make available to other apps. Using x-callback-url’s source apps can launch other apps passing data and context information, and also provide parameters instructing the target app to return data and control back to the source app after executing an action. Specific supported actions will be dependent on the individual apps and will not be discussed in the specification.
but obviously without an 'official' solution you'd never get control back from mail.app.
I've written up the various strategies for integrating iOS applications in some detail today. It sounds like MFMailComposeView is all you need, but in case anyone else comes to read this question on more general integration grounds:
http://blog.codiform.com/2011/04/integrating-applications-in-ios-and-x.html
X-callback-url is definitely the promising (but relatively new) entrant to the return-to-caller model you'd otherwise need.