Tuesday, January 12, 2016

Opening App Transport Security failures in Safari

One of my client projects is an iOS application which, like many iOS Applications, has an embedded web view, which it uses in this case to display help content from the web. Because this app targets iOS 7.x as a baseline, it uses UIWebView instead of, say, SFSafariViewController.

The help content is part of the company's website, and the web page footer has social media links, and some of these links, if you clicked on them, wouldn't load.

Initially, I thought this might be because of the target="_blank" attribute, which has been a problem with UIWebView in the past, so I built a UIWebViewDelegate that would inject a little bit of JavaScript to remove the targets. It didn't work. So I changed the JavaScript to set the target to "_self". Still didn't work:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *injectedJavascript = @"function "
      "AppPrefix_Injected_suppressBlankTargets() {\n"
      "  var links = document.getElementsByTagName('a');\n"
      "  var hrefs = [];\n"
      "  for( index = 0; index < links.length; index++ ) {\n"
      "    var link = links.item( index );\n"
      "    if( link.getAttribute( 'target' ) == '_blank' ) {\n"
      "      link.setAttribute( 'target', '_self' );\n"
    "      hrefs.push( link.outerHTML );\n"
      "    }\n"
      "  }\n"
      "  return hrefs.join();\n"
      "}"
      "AppPrefix_Injected_suppressBlankTargets();";
    NSString *result = [webView 
      stringByEvaluatingJavaScriptFromString:injectedJavascript];
    DLog( "Injected javascript to suppress blank targets on links: %@", result );
}

Having failed on two assumptions, I realized that I should verify my assumption instead, and implemented webView:didFailLoadWithError, which quickly showed me that the actual problem was App Transport Security.

The social media links? Some of them are HTTP rather than HTTPS, and those ones won't load because App Transport Security doesn't like it. That left options.

Disable App Transport Security?
I don't like going this route until I have to.  I don't want to whitelist certain sites because the social media links might lead to other sites which would also need to be whitelisted and then eventually you just end up disabling app transport security anyway.

Open ATS Failures in Safari
What I ended up doing instead is listening for errors (webView:didFailLoadWithError) and then check the error. If it was an app transport security failure, pull the URL out of NSError's userInfo and then open that link in mobile safari instead.

It's fairly straightforward. Write a UIWebViewDelegate like this:

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    NSURL *failedUrl = [self parseATSError:error];
    if( failedUrl != nil ) {
        DLog( "ATS Failure, opening in Safari: %@", failedUrl );
        [[UIApplication sharedApplication] openURL:failedUrl];
    } else {
        DLog( "Failed to load, with non-ATS error: %@", error );
    }
}

- (NSURL *)parseATSError:(NSError*)error {
    if( error == nil )
        return nil;
    if( ![error.domain isEqualToString:NSURLErrorDomain] )
        return nil;
    if( error.code != -1022 )
        return nil;
    NSString *url = error.userInfo[ NSURLErrorFailingURLStringErrorKey ];
    return [NSURL URLWithString:url];
}

This seems to work. So if you're using an UIWebView and running afoul of App Transport Security, maybe this will help you.

Tuesday, January 5, 2016

AppStore Release Joy

After what felt like an eternal delay, one of the my client's apps has finally gone live in the App Store. It is always nice when something you write goes live, but somehow the app store process, painful though it may be, makes the final victory of "Hey, there's my App, live in the App Store" feel that much sweeter.

I've already seen some press for the app (and related wearables), so it's already somewhat promising, but it'll be interesting to watch it grow.

Thursday, November 5, 2015

Fabric / Crashlytics ADD in Images

Remember how I commented that Crashlytics' daily email was so focused on the daily delta that it lacked a sense of the overall trends?

Here's two daily emails back to back. October 30th:


October 31st:


App name pixelated because I haven't cleared sharing this information, so I'm sharing it without the identifiable information.

These kinds of transitions, from "growing like crazy" to "growth slows" and back are a daily occurrence which totally obscures any real trends about how many users are actually using your app.

Tuesday, October 27, 2015

GWT Plugin Still Not Ready for Eclipse 4.5?

Eclipse Mars / v4.5 was released June 24, 2015. The Google Plugin for Eclipse 4.5 has not yet been released. If you go to the Google Plugin for Eclipse quickstart page, you can see that it's still showing links for Eclipse 4.4, 4.3 and 4.2 / 3.8.

In essence, if you'd like to use GWT and you want to use the latest Eclipse, there's no officially supported option nearly four months later.

If you do a little searching, you'll find a few threads on the subject, :

A friend tried Eclipse 4.5 and the Google Plugin for Eclipse 4.4 together and hit a NoClassDefFound error for Java2HTMLEntityReader. A quick search found that it was a bug, but that raises further questions about a plugin fork, which looks like it might have been around since 2013. It looks like there's a lot of work underway.

Ultimately, between this and some of the posts about GWT 2.8, this adds to my long-growing sense that GWT is stagnating. It hasn't descended into a terrible state yet by any means. It's quite usable for existing projects. Still, I wouldn't recommended it for a new project, and I'd recommend that people using it on existing projects start thinking about the long-term plan for their projects and consider a plan to move away from GWT at some point and how to manage that transition.

Thursday, October 22, 2015

Fabric / Crashlytics Growth ADD

Crashlytics' / Fabric has a daily email which gives you a sense of recent crashes, and also some basic stats.  It's useful, but they don't seem to consider that there can be a fair amount of day-to-day variance and attempt to smooth that out and give you a sense of real trends. Instead, everything is about yesterday's change:

  • Day N:
    • New sessions up 20%!
    • Your app is on fire!
    • Seek investment immediately!

  • Day N+1:
    • New sessions down 15%!
    • Your app is beginning to stagnate!
    • Oh noes!
UPDATE:
I captured images from two back-to-back emails as a good example.

Thursday, September 24, 2015

Searching for Android Documentation in Dash

Android developers: are you using Kapeli's Dash to read Android docs on a Mac? Want to be able to search for inherited methods without hitting "expand all" first? Request it!


Tuesday, September 22, 2015

Limited Integration between MailChimp and Mandrill

I'm surprised by how limited the integration is between MailChimp and Mandrill. Given that Mandrill is a product developed by MailChimp, I was expecting the integration between the two to be pretty strong, and it really doesn't seem to be.

They're not the same use case, I admit. MailChimp is more of a broadcast / marketing email platform, with elements like campaigns to consider, whereas Mandrill is a transactional email service, for personalized delivery of email messages like notifications from an application. I'm not surprised that they have somewhat different feature sets as a result. 

I was a little surprised to discover that Mandrill and Mailchimp have a totally different account system, and that while you can link your MailChimp account to your Mandrill account, it's not like having a single account with multiple products/services.

The more you look, the deeper the differences get, and the more surprised I was.

For instance, look at templates. MailChimp allows you to define email templates that you might send to a mailing list. Think of something like a sale email, where you might configure a template and then merge in a few specifics as you're sending to each person. MailChimp has a sophisticated WYSIWYG editor that you can use to define these email templates.

Mandrill also has templates. You might want to configure a template for the welcome email for your application in Mandrill rather than having the application encode all the details of what's in the email. This means that later, you could come back in and modify the template without changing the application.

However, Mandrill doesn't have a WYSIWYG editor. If you wanted to have someone on your growth hacking team change the email, they'll have to know HTML to do it. That may not be the end of the world, depending on your company structure but it was a surprising inconsistency for me, and a bit of a missed opportunity.

There are integrations between the two. For instance, you can actually create an HTML template in MailChimp using their editor and then send the template over to Mandrill. But when you do so, you don't seem have the option of choosing which template to replace, or to make use of Mandrill's template versioning. And if you make use of Merge Codes (think personalized fields) in MailChimp's designer, some, but not all of those codes are supported in Mandrill, but neither MailChimp nor Mandrill seems to mind if you make use of them and then send the template from MailChimp to Mandrill. Those codes won't do their job anymore, but neither system tells you that, from what I can see.

I just think MailChimp has missed an opportunity, both to integrate these two systems more tightly, encouraging MailChimp customers to consider Mandrill and vice versa, and also, frankly, to improve the experience of both systems by leveraging the work done by the other. Why doesn't MailChimp offer their WYSIWYG designer in the Mandrill interface? Can MailChimp templates by sent to Mandrill to become alternate versions of existing templates rather than replacing them? If not, why not?