<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Codethink &#187; coding</title>
	<atom:link href="https://codethink.no-ip.org/posts/coding/feed" rel="self" type="application/rss+xml" />
	<link>https://codethink.no-ip.org</link>
	<description>A blog about coding, life, and other arbitrary topics</description>
	<lastBuildDate>Sun, 15 Mar 2026 21:30:15 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.29</generator>
	<item>
		<title>Matchbook &#8211; Multi-platform Realtime Gaming</title>
		<link>https://codethink.no-ip.org/archives/1046</link>
		<comments>https://codethink.no-ip.org/archives/1046#comments</comments>
		<pubDate>Sun, 26 Jan 2014 13:56:58 +0000</pubDate>
		<dc:creator><![CDATA[aroth]]></dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[objective-c]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[objc]]></category>
		<category><![CDATA[open-source]]></category>

		<guid isPermaLink="false">http://codethink.no-ip.org/wordpress/?p=1046</guid>
		<description><![CDATA[Have you ever thought that it would be cool if you could build a cross-platform or multi-platform game and connect from one platform to another without having to do all the heavy lifting with respect to matchmaking, communications, and related &#8230; <a href="https://codethink.no-ip.org/archives/1046">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Have you ever thought that it would be cool if you could build a cross-platform or multi-platform game and connect from one platform to another without having to do all the heavy lifting with respect to matchmaking, communications, and related tasks yourself?  Well now you can, thanks to <a href="https://github.com/adam-roth/matchbook" target="_blank">Matchbook</a>.</p>
<div id="attachment_1049" style="width: 650px" class="wp-caption aligncenter"><a href="http://codethink.no-ip.org/wordpress/wp-content/uploads/2014/01/running_small.jpg" rel="lightbox[1046]"><img src="http://codethink.no-ip.org/wordpress/wp-content/uploads/2014/01/running-1200x721.jpg" alt="Matchbook Example &quot;Game&quot;" title="Matchbook Example &quot;Game&quot;" width="640" height="384" class="size-large wp-image-1049" /></a><p class="wp-caption-text">Matchbook Example &quot;Game&quot;, 2x iOS and 2x Android clients</p></div>
<p>Matchbook is a lightweight and platform-agnostic matchmaking solution, intended for use in mobile applications (think games that require near-real-time, relatively-low-latency, persistent communications between two or more client devices).</p>
<p>At its core is a server component, which provides a JSON-based webservice allowing clients to find, create, and join matches. The server also acts as a proxy/relay when necessary, allowing client devices to tunnel through any firewalls that might exist between them.</p>
<p>In addition to the server component, Matchbook includes prebuilt SDK&#8217;s for both Java and Objective-C. These SDK&#8217;s are intended to support the development of native applications that make use of the Matchbook webservice on Android and iOS devices, respectively.</p>
<p>I could go on at length, but it&#8217;s simpler to just link to the project on Github (Matchbook is open-source, and permissively licensed, naturally):</p>
<p><a href="https://github.com/adam-roth/matchbook" target="_blank">https://github.com/adam-roth/matchbook</a></p>
<p>Note that Google is currently building comparable functionality into Google Play, although their realtime communications API is currently only available on Android (iOS support is under development).  </p>
<p>And although Apple already has realtime and turn-based gaming API&#8217;s for iOS, they natually have no intention of inviting Android devices to the party.  Ever.</p>
<p>So let the record show that Matchbook got there first.</p>
]]></content:encoded>
			<wfw:commentRss>https://codethink.no-ip.org/archives/1046/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NSObject+SimpleJson</title>
		<link>https://codethink.no-ip.org/archives/924</link>
		<comments>https://codethink.no-ip.org/archives/924#comments</comments>
		<pubDate>Wed, 08 Jan 2014 07:37:39 +0000</pubDate>
		<dc:creator><![CDATA[aroth]]></dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[objective-c]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[objc]]></category>

		<guid isPermaLink="false">http://codethink.no-ip.org/wordpress/?p=924</guid>
		<description><![CDATA[Certainly the subject of JSON processing on iOS is fairly old-hat at this point. After all, Apple has provided native support for JSON serialization and parsing since the release of iOS 5. So why bring it up now? Personally I&#8217;ve &#8230; <a href="https://codethink.no-ip.org/archives/924">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Certainly the subject of JSON processing on iOS is fairly old-hat at this point.  After all, Apple has provided native support for JSON serialization and parsing since the release of iOS 5.  So why bring it up now?  </p>
<p>Personally I&#8217;ve never been entirely happy with the implementation provided by Apple, and in particular their decision to build their API around <i>NSData</i> instead of <i>NSString</i>.  Granted there&#8217;s probably a reason why Apple chose to go that route, and also a reason why they didn&#8217;t bother to provide convenience methods that allow people to work with whichever type they prefer.  But as far as I&#8217;m concerned, JSON is a textual/non-binary data format, so therefore the natural iOS data structure to use for outputting and inputting JSON data is an <i>NSString</i>.  Using <i>NSData</i> instead is counter-intuitive, and clunky as a result.</p>
<p>So because of that clunkiness, I&#8217;ve stuck with the tried and true <a href="http://superloopy.io/json-framework/?utm_source=ipadsummary&#038;utm_medium=twitter">SBJson</a> library, even though the author of that library himself recommends just using Apple&#8217;s implementation.  This preference is largely based around the very helpful (and also very deprecated) &#8216;<i>NSObject+SBJson</i>&#8216; category that is included in the library.  This category imbues objects with a couple of very convenient methods:</p>
<pre class="brush: cpp; title: ; notranslate">- (NSString*)JSONRepresentation;
- (id)JSONValue;</pre>
<p>These methods can be used to turn an appropriate object (i.e. an <i>NSArray</i> or an <i>NSDictionary</i>) into a JSON string, and to turn a JSON string back into a proper object, respectively.  No parameters, no options, no boxing to or unboxing from <i>NSData</i>; just a simple API that is basically just &#8216;<i>[thing turnIntoJson];</i>&#8216; and &#8216;<i>[json turnBackIntoThing];</i>&#8216;.  It doesn&#8217;t get much more convenient than that.  </p>
<p>And that was all well and good, until I wanted to build a <a href="https://github.com/kstenerud/iOS-Universal-Framework">framework</a> which internally used SBJson, and then use that framework within a project that also needs to make its own use of SBJson.  Linker hell was the result.  And long story short, I decided that it would be faster to build a shim around Apple&#8217;s JSON API to preserve the convenience I&#8217;m after than it would be to appease the linker gods.  </p>
<p>So in case anyone else has been avoiding Apple&#8217;s JSON implementation because it&#8217;s too clunky to work with (or too painful to migrate away from SBJson), here&#8217;s my 5-minute shim:</p>
<p><b>NSObject+SimpleJson.h</b></p>
<pre class="brush: cpp; title: ; notranslate">#import &lt;Foundation/Foundation.h&gt;

@interface NSObject (SimpleJson)

- (NSString *)JSONRepresentation;  //turns an object into a JSON string
- (id)JSONValue;                           //turns a JSON string back into an object

@end</pre>
<p><b>NSObject+SimpleJson.m</b></p>
<pre class="brush: cpp; title: ; notranslate">#import &quot;NSObject+SimpleJson.h&quot;

@implementation NSObject (SimpleJson)

- (NSString*)JSONRepresentation {
    //write ourself to a JSON string; only works if we're a type that 'NSJSONSerialization' supports
    NSError* error = nil;
    NSData* tempData = [NSJSONSerialization dataWithJSONObject:self options:kNilOptions error:&amp;error];
    if (error) {
        return nil;
    }
    
    return [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding];
}


- (id) JSONValue {
    //converts from a string back into a proper object; only works if we're an NSString or NSData instance
    if (! [self isKindOfClass:[NSString class]] &amp;&amp; ! [self isKindOfClass:[NSData class]]) {
        return nil;
    }
    
    NSData* jsonData = nil;
    if ([self isKindOfClass:[NSData class]]) {
        jsonData = (NSData*)self;
    }
    else {
        //we must be an NSString
        jsonData = [((NSString*)self) dataUsingEncoding:NSUTF8StringEncoding];
    }
    
    return [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:nil];
}

@end</pre>
<p>If you have code that uses SBJson&#8217;s &#8216;<i>NSObject+SBJson</i>&#8216; category, the above category should be drop-in compatible.  And just as importantly (in my case) does not invoke linker hell when used as part of an iOS framework.</p>
<p>Note that the above code assumes you are using ARC.  If not, you&#8217;ll want to add an &#8216;<i>autorelease</i>&#8216; call in there.</p>
]]></content:encoded>
			<wfw:commentRss>https://codethink.no-ip.org/archives/924/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[JavaScript] Finding Equidistant Lat/Long Coordinates</title>
		<link>https://codethink.no-ip.org/archives/746</link>
		<comments>https://codethink.no-ip.org/archives/746#comments</comments>
		<pubDate>Sat, 03 Dec 2011 08:17:52 +0000</pubDate>
		<dc:creator><![CDATA[aroth]]></dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[hack]]></category>
		<category><![CDATA[location]]></category>
		<category><![CDATA[maps]]></category>

		<guid isPermaLink="false">http://codethink.no-ip.org/wordpress/?p=746</guid>
		<description><![CDATA[Here&#8217;s a quick one that was inspired by a StackOverflow question and built using the references posted here. It allows you to compute the position of an arbitrary number of points (expressed as latitude/longitude pairs) equidistant from a given centerpoint &#8230; <a href="https://codethink.no-ip.org/archives/746">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Here&#8217;s a quick one that was inspired by a <a href="http://stackoverflow.com/questions/6789992/need-to-find-lats-long-of-a-location-which-is-about-50km-away-from-a-location" target="_blank">StackOverflow question</a> and built using the references posted <a href="http://www.movable-type.co.uk/scripts/latlong.html" target="_blank">here</a>.  It allows you to compute the position of an arbitrary number of points (expressed as latitude/longitude pairs) equidistant from a given centerpoint (using a given radius/distance, of course) on the surface of the Earth.  The crux of the code goes like so:</p>
<pre class="brush: jscript; title: ; notranslate">window.pointsAround = function(centerLat, centerLng, radius, numInterpolations) {
    var result = [];

    //do annoying trig maths to work out the delta in latitude between our start and end points
    var targetD = radius;          //km
    var fractionalDistance = (targetD / radiusOfEarth) / 2;
    var sqrtA = Math.sin(fractionalDistance);
    var a = sqrtA * sqrtA;
    var sinHalfDLat = Math.sqrt(a);
    var dLat = Math.asin(sinHalfDLat) * 2;
    var dLatDegrees = degrees(dLat);
    
    var minLat = centerLat - dLatDegrees;              //furthest valid latitude above the origin
    var maxLat = centerLat + dLatDegrees;            //furthest valid latitude below the origin
    
    var centerLatRadians = radians(centerLat); 
    
    //topmost and bottommost points in the circle
    result.push({lat: minLat, lng: centerLng});
    result.push({lat: maxLat, lng: centerLng});
    
    //step from minLat to maxLat, interpolating coordinates that lie upon the circle
    numInterpolations = numInterpolations ? numInterpolations : 360;
    var step = (maxLat - minLat) / (numInterpolations / 2.0);
    for (var count = 0; count &lt; (numInterpolations / 2) - 1; count++) {
        minLat += step;
        var minLatRadians = radians(minLat);
        dLat = radians(centerLat - minLat);
        
        //more annoying trig to work out the delta in longitude for our interpolated coordinate
        var dLon = 2 * Math.asin(Math.sqrt((a - (Math.sin(dLat/2) * Math.sin(dLat/2))) / (Math.cos(minLatRadians) * Math.cos(centerLatRadians))));
        var dLonDegrees = degrees(dLon);
        
        var newLng = centerLng + dLonDegrees;
        var deltaLng = newLng - centerLng;
        result.push({lat: minLat, lng: newLng});
        result.push({lat: minLat, lng: centerLng - deltaLng});
    }
    
    return result;
};</pre>
<p>This code works out the northernmost and southernmost points that are the specified distance away from the given centerpoint.  It then steps between the minimum and maximum latitude of these two points, interpolating the appropriate longitudinal value(s) to satisfy the equidistant constraint.  The total number of coordinates returned can be controlled using the optional &#8216;<em>numInterpolations</em>&#8216; parameter.  When not specified a total of 360 points will be returned by default, because that is a nice, round number.</p>
<p>If you use something like the following code to plot some points on a map:</p>
<pre class="brush: jscript; title: ; notranslate">window.map = new GMap2(document.getElementById(&quot;mapDiv&quot;));
    var centerPoint = new GLatLng(37.4419, -122.1419);
    map.setCenter(centerPoint, 8);
    map.addControl(new GLargeMapControl3D());
    map.addControl(new GMapTypeControl());
    
    var marker = new GMarker(centerPoint);
    var markerText = &quot;Center @ 37.4419, -122.1419&quot;;
    GEvent.addListener(marker, &quot;click&quot;, markerClickHandler(marker, markerText));
    map.addOverlay(marker);
    
    var points = pointsAround(37.4419, -122.1419, 50);
    for (var index = 0; index &lt; points.length; index++) {
        var point = points[index];
        var latlng = new GLatLng(point.lat, point.lng);
        marker = new GMarker(latlng);
        markerText = &quot;Marker @ &quot; + point.lat + &quot;, &quot; + point.lng;
        GEvent.addListener(marker, &quot;click&quot;, markerClickHandler(marker, markerText));
        map.addOverlay(marker);
    }</pre>
<p>&#8230;you will get a nice circle of points around the hard-coded centerpoint.  You can see a working example of this here:  <a href="http://jsfiddle.net/HmchC/27/" target="_blank">http://jsfiddle.net/HmchC/27/</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://codethink.no-ip.org/archives/746/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>[iOS] Jira Mobile Connect</title>
		<link>https://codethink.no-ip.org/archives/788</link>
		<comments>https://codethink.no-ip.org/archives/788#comments</comments>
		<pubDate>Fri, 04 Nov 2011 06:48:08 +0000</pubDate>
		<dc:creator><![CDATA[aroth]]></dc:creator>
				<category><![CDATA[banter]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[jira]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://codethink.no-ip.org/wordpress/?p=788</guid>
		<description><![CDATA[Not long ago Atlassian released version 1.0 (now up to 1.0.7) of their Jira Mobile Connect plugin. This is a plugin for Jira (obviously) that aims to simplify testing, error-reporting, and feedback collection/management for iOS applications. Assuming that you are &#8230; <a href="https://codethink.no-ip.org/archives/788">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Not long ago Atlassian released version 1.0 (now up to 1.0.7) of their <a href="https://plugins.atlassian.com/plugin/details/322837" target="_blank">Jira Mobile Connect plugin</a>.  This is a plugin for Jira (obviously) that aims to simplify testing, error-reporting, and feedback collection/management for iOS applications.  Assuming that you are doing iOS software development and have a Jira server instance running (which you really should if you are doing any nontrivial amount of development work of any variety) then using this plugin in your apps is really a no-brainer.  Jira Mobile Connect includes a number of very cool features, such as the ability for users to attach annotated screenshots/images to their feedback reports, to record audio to attach with their feedback, and even to chat back and forth with the developer(s) working on their issue/ticket .  And of course it does basic crash logging and reporting, as well. </p>
<p>Previously if you wanted a free/open-source crash reporting framework for iOS your options were basically limited to <a href="http://quincykit.net/server.html" target="_blank">QuincyKit</a>, which is a serviceable but basic solution.  Sadly, the backing architecture used by the QuincyKit server is not well designed and scales very poorly with the number of crash reports in the system.  Once you have around 5,000 you&#8217;ll notice the server slowing down significantly, and go much beyond 10,000 that and the system grinds to an unusable halt.  With a day or so of database and code refactoring, these scalability issues can be resolved, creating a system performant enough to track millions of error logs or more.  But that&#8217;s a subject for another time.  The point for today is that despite its basic level of functionality and flawed server architecture, there are a few key areas in which QuincyKit blows the default implementation of Jira Mobile Connect out of the water:</p>
<ol>
<li>Automatic grouping of crash reports.</li>
<li>Automatic symbolication of error logs.</li>
</ol>
<p>If you have any experience whatsoever with supporting multiple high-volume iOS applications you will instantly realize that these are features that you want.  They might even be features that you want more than annotated screenshots, audio feedback, user chat, or seamless integration with Jira and all the awesomeness that Jira brings.  In short, Jira Mobile Connect&#8217;s lack of support for these two key features may cause serious developers to pass it over in favor of other solutions.  </p>
<p>Without grouping every single crash will be creating a new ticket in Jira that you need to track and resolve.  Multiple instances of the same crash will have to be manually flagged as duplicates within Jira.  And without symbolication trying to actually map back from an error log to the line of code that caused it is an exercise in futility, or at best, tedium.</p>
<p>In any case, rather than abandon the excellent potential shown by Jira Mobile Connect I decided that instead I would attempt to patch it up and add the missing features myself.  It&#8217;s all open-source code, after-all, and if the tangled mess of PHP that is QuincyKit server can provide these features then they can&#8217;t be that difficult to implement.  Unfortunately I had to change too many files in too many different places to show the code here, but if you want the short version of it I was able to implement both grouping and symbolication, and you&#8217;re welcome to view the complete diffs on bitbucket:</p>
<p><a href="https://bitbucket.org/aroth_iapps/jiraconnect-ios-iapps/compare/..atlassian/jiraconnect-ios" target="_blank">https://bitbucket.org/aroth_iapps/jiraconnect-ios-iapps/compare/..atlassian/jiraconnect-ios</a> (client/native iOS code)<br />
<a href="https://bitbucket.org/aroth_iapps/jiraconnect-jiraplugin-iapps/compare/..atlassian/jiraconnect-jiraplugin" target="_blank">https://bitbucket.org/aroth_iapps/jiraconnect-jiraplugin-iapps/compare/..atlassian/jiraconnect-jiraplugin</a> (server/Java code)</p>
<p>One interesting side-effect of adding groups was that it became possible for multiple client UID&#8217;s to be associated with a single Jira ticket.  This had an important implication for feedback notifications/chat in that the reference implementation allowed only a single UID to be associated with each Jira ticket.  Since the UID is used for determining what notifications/updates to send to the native client, this restricted update notifications to a single user per ticket.  Not too useful if you have a common crash that thousands of users have experienced.  The implementation above extends the data model to allow multiple UID&#8217;s to be stored against a single Jira ticket, allowing each UID to be updated when new feedback is posted by the developer on a ticket.  In essence, implementing grouping also required the implementation of group feedback/chat.</p>
<p>There is one caveat with my server implementation, in that it assumes the existence of the &#8216;<em>symbolicatecrash</em>&#8216; utility on the system&#8217;s runtime <em>PATH</em>.  This means that it will only work if your Jira server is hosted on a Mac, with the proper XCode developer tools installed (and with your application&#8217;s .app and .dSYM files copied to the local filesystem).  This is of course a requirement regardless if you want automatic symbolication to work on any sort of system; somewhere there needs to be a Mac with &#8216;<em>symbolicatecrash</em>&#8216; available.  In any case, it is a fairly simple matter to either turn this off or otherwise make it more intelligent, if your Jira server is incapable of running &#8216;<em>symbolicatecrash</em>&#8216;.</p>
<p>Also note that the native iOS code has been restructured to build a universal iOS framework as opposed to an architecture-specific static library.  This is done using Karl Stenerud&#8217;s excellent <a href="https://github.com/kstenerud/iOS-Universal-Framework" target="_blank">XCode4 Project Template</a>.  You will need to install this template in order to actually build the modified code.  Or you can just refactor it back to build a static library again, but why would you want to do that?</p>
<p>When using the iOS framework, be aware that you will need to set the &#8216;-<em>all_load</em>&#8216; linker flag and also include all the images and nibs in the framework&#8217;s &#8216;<em>/Resources</em>&#8216; folder as part of your build.  You will probably also want to include the &#8216;<em>JMCLocalizable.strings</em>&#8216; file in the same folder as well, to provide proper text and labels on Jira Mobile Connect&#8217;s UI elements.</p>
<p>Regardless, if you do a lot of iOS development and are already using Jira (like you should be) then I encourage you to check this out.  This is the Jira Mobile Connect plugin, now with automatic grouping and error log symbolication.</p>
]]></content:encoded>
			<wfw:commentRss>https://codethink.no-ip.org/archives/788/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[JavaScript] Creating an Animated Hurricane Tracker</title>
		<link>https://codethink.no-ip.org/archives/773</link>
		<comments>https://codethink.no-ip.org/archives/773#comments</comments>
		<pubDate>Sat, 10 Sep 2011 15:09:21 +0000</pubDate>
		<dc:creator><![CDATA[aroth]]></dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[hack]]></category>

		<guid isPermaLink="false">http://codethink.no-ip.org/wordpress/?p=773</guid>
		<description><![CDATA[Here&#8217;s a fun little diversion I came up with when the news was all abuzz with information about Hurricane Irene. Now I use Wikipedia almost exclusively for keeping track of this sort of thing as I find that it consistently &#8230; <a href="https://codethink.no-ip.org/archives/773">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Here&#8217;s a fun little diversion I came up with when the news was all abuzz with information about <a href="http://en.wikipedia.org/wiki/Hurricane_Irene_(2011)" target="_blank">Hurricane Irene</a>.  Now I use Wikipedia almost exclusively for keeping track of this sort of thing as I find that it consistently provides the most up to date data in the cleanest and most easy to digest format, but I also noticed a large volume of comments on other news sites complaining about the accuracy of hurricane forecasting.  And this got me to wondering, just how accurate are the historical forecasts when overlaid with the actual storm track?</p>
<p>Well Wikipedia doesn&#8217;t provide this information directly, but interestingly enough Wikipedia does in fact keep all the <a href="http://en.wikipedia.org/w/index.php?title=File:09L_2011_5day.gif&#038;limit=500#filehistory" target="_blank">historical data</a> necessary to answer this question.  Thanks to Chrome&#8217;s awesome JavaScript console (and the fact that Wikipedia uses jQuery on all of its pages) it is a very simple matter to extract this historical information in a format that is easy to work with later.  The following code will do the trick:</p>
<pre class="brush: jscript; title: ; notranslate">var result = []; 
jQuery.each($(&quot;.filehistory a[href$=5day.gif]&quot;), function(){
    result.push(this.href)
}); 
result;</pre>
<p>This simply constructs an array containing the URL&#8217;s of every historical forecast/storm track graphic (ordered by date from newest to oldest) and outputs it to the console.  We can then copy/paste the console output to wherever we like, such as <a href="http://jsfiddle.net" target="_blank">jsFiddle</a> for instance.  Combine this with some basic HTML structure:</p>
<pre class="brush: xml; title: ; notranslate">&lt;div class=&quot;title&quot;&gt;
    Hurricane Irene Animated Forecast Map
&lt;/div&gt;
&lt;img id=&quot;display&quot; src=&quot;http://fcf.teambeachbody.com/fcf/images/spinner5.gif&quot; /&gt;
&lt;div id=&quot;status&quot;&gt;
    (&lt;span id=&quot;numLoaded&quot;&gt;&lt;/span&gt; of &lt;span id=&quot;total&quot;&gt;&lt;/span&gt; frames loaded)
&lt;/div&gt;</pre>
<p>&#8230;and some simple JavaScript to handle the actual rendering:</p>
<pre class="brush: jscript; title: ; notranslate">document.getElementById(&quot;numLoaded&quot;).innerHTML = &quot;0&quot;;
document.getElementById(&quot;total&quot;).innerHTML = data.length;

//preload all the images so that they are cached for the animation
window.imagesLoaded = 0;
for (var index = data.length - 1; index &gt;= 0; index--) {
    var node = document.createElement(&quot;img&quot;);
    node.style.display = &quot;none&quot;;
    document.body.appendChild(node);
    $(node).bind('load', function() {  
        window.imagesLoaded++;  
    });
    $(node).attr('src', data[index]);
    //node.src = data[index];
}

//run the animation
window.currentImage = data.length - 1;
window.updateAnimation = function() {
    if (window.imagesLoaded &lt; data.length) {
        //keep waiting
        document.getElementById(&quot;numLoaded&quot;).innerHTML = window.imagesLoaded;
        return;
    }
    $(&quot;#status&quot;).hide();
    document.getElementById(&quot;display&quot;).src = data[currentImage];
    currentImage--;
    if (currentImage &lt; 0) {
        currentImage = data.length - 1;
    }
};

setInterval(updateAnimation, 200);</pre>
<p>&#8230;and you get an animated map of the entire forecast history, including the actual storm track.  The result looks something like this:</p>
<p><iframe src="http://fiddle.jshell.net/TqmVg/6/show/" width="425" height="380" frameBorder="0"><br />
</iframe></p>
<p>Not too bad for a few dozen lines of code and about 30 minutes worth of effort.  You can also access a full-scale, editable version of this animated storm history <a href="http://jsfiddle.net/TqmVg/5/" target="_blank">here</a>.</p>
<p>So what does this say about the accuracy of the hurricane forecasting for Irene?  Personally I think it shows that the forecasts are pretty damn accurate.  In fact, if you focus just on the solid white portion of the predicted storm track it appears that the storm never deviates from its forecast path.  It&#8217;s only when looking at the dashed white portion of the forecast that any significant deviation can be noted.  And the dashed white portion indicates forecasts that are more than 72 hours in the future.  </p>
<p>Of course we should always be working to improve our ability to predict and forecast these things, but I have to think that being able to accurately predict a storm&#8217;s movement for up to 72 hours into the future is adequate for most purposes.</p>
]]></content:encoded>
			<wfw:commentRss>https://codethink.no-ip.org/archives/773/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>[JavaScript] Naming Your Function &#8216;$&#8217; is not Clever</title>
		<link>https://codethink.no-ip.org/archives/720</link>
		<comments>https://codethink.no-ip.org/archives/720#comments</comments>
		<pubDate>Sat, 16 Jul 2011 10:44:26 +0000</pubDate>
		<dc:creator><![CDATA[aroth]]></dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[rant]]></category>

		<guid isPermaLink="false">http://codethink.no-ip.org/wordpress/?p=720</guid>
		<description><![CDATA[The atrocity that is the dollar function dates back at least 2005 and the introduction of the Prototype Framework. Back in those days, &#8216;$()&#8216; was simply a shorthand way of saying &#8216;document.getElementById()&#8216; without having to type all 24 characters. And &#8230; <a href="https://codethink.no-ip.org/archives/720">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>The atrocity that is the dollar function dates back at least 2005 and the introduction of the <a href="http://en.wikipedia.org/wiki/Prototype_JavaScript_Framework" target="_blank">Prototype Framework</a>.  Back in those days, &#8216;<em>$()</em>&#8216; was simply a shorthand way of saying &#8216;<em>document.getElementById()</em>&#8216; without having to type all 24 characters.  And don&#8217;t get me wrong, aliasing &#8216;document.getElementById&#8217; to something shorter is not a bad idea, in and of itself.  But why should &#8216;<em>$()</em>&#8216; be the alias?  Such a name gives no clue that the function is looking up a DOM element by its id.  Something like &#8216;<em>findById()</em>&#8216; (or even &#8216;<em>$findById()</em>&#8216; if there are concerns about naming collissions) would have been infinitely more clear, and still a good deal shorter than writing &#8216;<em>document.getElementById()</em>&#8216;.  </p>
<p>But sadly, Prototype decided to use &#8216;<em>$()</em>&#8216; as the name of their alias.  And even worse, other frameworks have followed in this pattern, adding their own dollar functions that have semantics that can be completely different from the original &#8216;<em>$()</em>&#8216; introduced by Prototype.  Consider <a href="http://en.wikipedia.org/wiki/Jquery" target="_blank">jQuery</a>, which introduces its own version of &#8216;<em>$()</em>&#8216; which can be used to find (and wrap and clone) elements in the DOM by a number of different criteria, but which sadly fails miserably when given a string containing a literal element id.  This creates a lovely situation where now the developer cannot even guess at what &#8216;<em>$()</em>&#8216; is actually doing without first checking to see which framework is being used by the code.  It also means that any code that was written using Prototype&#8217;s &#8216;<em>$()</em>&#8216; will be broken by including jQuery (and vice-versa).  </p>
<p>&#8220;But that&#8217;s what &#8216;<em><a href="http://api.jquery.com/jQuery.noConflict/" target="_blank">jQuery.noConflict()</a></em>&#8216; is for&#8221;, you say.  But you miss the point.  It shouldn&#8217;t be necessary for &#8216;<em>jQuery.noConflict()</em>&#8216; to exist in the first place.  If the jQuery developers hadn&#8217;t decided that it would be cute to define their own version of &#8216;<em>$()</em>&#8216;, then jQuery would never conflict with anything except possibly an older version of jQuery.  The thing that really baffles me is how so many developers (obviously talented developers, at that, considering the work that goes into something like Prototype or jQuery) can all decide that it is a good idea to try to lay claim to a name that exists in the global scope.  Granted JavaScript does not have any concept of a proper namespace, but if jQuery limited itself to only setting properties on an object named &#8220;jQuery&#8221; and Prototype did the same on an object named &#8220;Prototype&#8221; and so on, the current JavaScript landscape would look ever so much more sane.</p>
<p>And of course, this says nothing of the &#8216;<em>$$()</em>&#8216; and &#8216;<em>$F()</em>&#8216; functions that were also introduced by Prototype (and the &#8216;$E()&#8217; and &#8216;$ES()&#8217; functions inexplicibly introduced by <a href="http://solutoire.com/2007/09/20/understanding-mootools-selectors-e-and-es/" target="_blank">MooTools</a>) and whose names are so semantically worthless that a developer has no hope of inferring their function without consulting the reference documentation.  Which again can only be accomplished after checking the entire codebase to determine which framework it is that is defining these functions with its woefully awful naming convention.</p>
<p>If the developers of Prototype, jQuery, Mootools, and the like wanted to turn JavaScript into an unreadable and unmaintainable mess of a language like <a href="http://en.wikipedia.org/wiki/Perl_language_structure" target="_blank">Perl</a>, then I applaud them for their efforts.  But if not then I call them all out on their atrocious naming convention and design decisions.  There is no excuse for this nonsense.  If you&#8217;re building a JavaScript library or framework, then pick a reasonable &#8220;namespace&#8221; to use for it.  This namespace should be the only thing that you bind in the global scope.  Everything else that is part of your library should be bound as a property inside of this namespace.  And there should be no automatic creation of shorthand aliases in the global scope.  </p>
<p>I don&#8217;t think this is too much to ask.  And of course, any developer that wants to use a global shorthand alias can always do:</p>
<pre class="brush: jscript; title: ; notranslate">window.$J = jQuery;
window.$P = Prototype.$;  //or:  window.$P = document.getElementById;</pre>
<p>It takes all of one line of code to set up an alias.  But when you start doing this automatically for everyone that uses your framework you create nothing but problems.  Especially when you jump on the bandwagon and choose the same crappy shorthand alias names that other frameworks are already using.</p>
<p>There is absolutely no reason why multiple JavaScript frameworks should all be competing over the same terrible function names.  This isn&#8217;t a contest to see who can break the most legacy code, or who can come up with the most obtuse API.  <a href="http://en.wikipedia.org/wiki/International_Obfuscated_C_Code_Contest" target="_blank">They do have such contests</a> if you are interested in such a thing, but this is not one of them.  The only thing accomplished by parading the dollar sign around as an example of a well-chosen function name is an increased brittleness in the code that glues together the majority of our modern websites.  It muddies semantics, and creates cases where completely unrelated libraries are not easily compatible with each other, and where simply importing a new library can cause existing code to break.  Even worse, some novice developers get the wrong idea from all those dollar signs flying around think that they need to (or should) prefix all of their function names with &#8220;$&#8221;.  So let&#8217;s put this bad practice to bed before it&#8217;s too late.</p>
<p>So please, can well all stop writing frameworks that give people the impression that calling a function &#8220;$&#8221;, &#8220;$$&#8221;, &#8220;$ES&#8221;, or &#8220;$anything&#8221; is a good idea?  Is it so difficult to choose a reasonable namespace, minimize the number of things that you declare in the global scope, and allow the developer to define a global shorthand alias for this-or-that utility function if they decide they want to?  I think not.</p>
]]></content:encoded>
			<wfw:commentRss>https://codethink.no-ip.org/archives/720/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Resurrecting sun.misc.Unsafe</title>
		<link>https://codethink.no-ip.org/archives/712</link>
		<comments>https://codethink.no-ip.org/archives/712#comments</comments>
		<pubDate>Sat, 09 Jul 2011 15:21:48 +0000</pubDate>
		<dc:creator><![CDATA[aroth]]></dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[hack]]></category>

		<guid isPermaLink="false">http://codethink.no-ip.org/wordpress/?p=712</guid>
		<description><![CDATA[Here&#8217;s one that only the hardcore Java hackers will enjoy. Perhaps you are already familiar with sun.misc.Unsafe. This heavily protected internal class provides access to a number of low-level memory operations and system functions that are generally hidden away from &#8230; <a href="https://codethink.no-ip.org/archives/712">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Here&#8217;s one that only the hardcore Java hackers will enjoy.  Perhaps you are already familiar with <a href="http://www.docjar.com/docs/api/sun/misc/Unsafe.html" target="_blank">sun.misc.Unsafe</a>.  This heavily protected internal class provides access to a number of low-level memory operations and system functions that are generally hidden away from user-code executing in the JRE.  If you&#8217;ve never heard of it before (or even if you have) then I encourage you to read this <a href="http://javapapers.com/core-java/address-of-a-java-object/" target="_blank">article</a> to get an idea about the sort of things that can be done using &#8216;<em>Unsafe</em>&#8216;.  Note that we are talking about very low-level operations here.  Operations where if you make a mistake, you won&#8217;t just get a simple &#8216;<em>Exception</em>&#8216; complaining that you did something bad.  Instead you&#8217;re liable to bring the entire JVM to a crashing halt if you do something wrong (it&#8217;s called &#8216;<em>Unsafe</em>&#8216; for a reason, after all), so you should proceed only if you&#8217;re sure you know what you are doing.</p>
<p>Also note that the method described in the <a href="http://javapapers.com/core-java/address-of-a-java-object/" target="_blank">other article</a> for getting your hands on an instance of &#8216;<em>Unsafe</em>&#8216; no longer works.  Using the latest JDK, the following code will not work (you&#8217;ll get compiler errors for even trying to <em>import</em> sun.misc.Unsafe):</p>
<pre class="brush: java; title: ; notranslate">private static Unsafe getUnsafeInstance() throws SecurityException, NoSuchFieldException, IllegalArgumentException,
    IllegalAccessException {
    Field theUnsafeInstance = Unsafe.class.getDeclaredField(&quot;theUnsafe&quot;);
    theUnsafeInstance.setAccessible(true);
    return (Unsafe) theUnsafeInstance.get(Unsafe.class);
 }</pre>
<p>Here is a variant that still works:</p>
<pre class="brush: java; title: ; notranslate">	public static Object getUnsafe() {
		try {
			Class unsafeClass = Class.forName(&quot;sun.misc.Unsafe&quot;);
			Field unsafeField = unsafeClass.getDeclaredField(&quot;theUnsafe&quot;);
			unsafeField.setAccessible(true);
			Object unsafeObj = unsafeField.get(unsafeClass);
			
			return unsafeObj;
		}
		catch (Exception e) {
			return null;//UNSAFE_ERROR_CODE;
		}
	}</pre>
<p>Note that as importing sun.misc.Unsafe now causes a compiler error, it is only possible to return an Object reference to the &#8216;<em>Unsafe</em>&#8216; instance.  This means that any method that you want to call on it must be done through reflection, which is tedious at best.  Luckily, I&#8217;ve taken care of this tedious bit for you, and created a wrapper that provides the same public API as sun.misc.Unsafe and uses reflection to delegate method calls to the real &#8216;<em>Unsafe</em>&#8216; instance.  The source code for this utility is quite long and not very interesting, so I&#8217;m not going to post it here.  Instead you can use this <a href="http://aroth.no-ip.org/UnsafeUtil.java">direct link</a> to download a copy.</p>
<p>Here is a simple example of how to use this class to perform some basic tasks:</p>
<pre class="brush: java; title: ; notranslate">		System.out.println(&quot;Testing sun.misc.Unsafe...&quot;);
		double[] averages = {-777.0, -777.0, -777.0};
		UnsafeUtil util = new UnsafeUtil();
		System.out.println(&quot;addressSize=&quot; + util.addressSize() + &quot;, pageSize=&quot; + util.pageSize());
		
		long memPointer = util.allocateMemory(1024);
		long memPointer2 = util.allocateMemory(1024);
		System.out.println(&quot;1K memory blocks at addresses:  &quot; + memPointer + &quot;, &quot; + memPointer2);
		
		//valid copy
		util.copyMemory(memPointer, memPointer2, 1024);
		
		//invalid copy
		//util.copyMemory(memPointer, memPointer2, 1024000);
		
		int result = util.getLoadAverage(averages, 1);
		System.out.println(&quot;getLoadAverage:  Result=&quot; + result + &quot;, averages=&quot; + averages[0] + &quot;, &quot; + averages[1] + &quot;, &quot; + averages[2]);</pre>
<p>Do note that if you uncomment the &#8220;invalid copy&#8221; line then running this code will very likely crash your JVM.  As noted above, you should use this utility with caution, particularly if you are running it in an environment shared by other Java code.</p>
<p>And why do all this when Sun (now Oracle) clearly does not want people mucking around with this class?  Because ultimately power belongs in the hands of developers.  All developers, not just the select few chosen to work on privileged system code.</p>
]]></content:encoded>
			<wfw:commentRss>https://codethink.no-ip.org/archives/712/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>[Android] Installing Adobe AIR on the Android Emulator</title>
		<link>https://codethink.no-ip.org/archives/699</link>
		<comments>https://codethink.no-ip.org/archives/699#comments</comments>
		<pubDate>Sun, 03 Jul 2011 13:30:57 +0000</pubDate>
		<dc:creator><![CDATA[aroth]]></dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[configuration]]></category>
		<category><![CDATA[AIR]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[emulator]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://codethink.no-ip.org/wordpress/?p=699</guid>
		<description><![CDATA[I&#8217;ve got no idea why this process is so poorly documented, nor why many of the existing resources describing how to install the AIR runtime on the Android emulator are so needlessly circuitous, pointing you to links on the official &#8230; <a href="https://codethink.no-ip.org/archives/699">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve got no idea why this process is so poorly documented, nor why many of the existing resources describing how to install the AIR runtime on the Android emulator are so needlessly circuitous, pointing you to links on the official Adobe site that have moved or no longer exist.  </p>
<p>But suffice to say, if you want to install the Adobe AIR runtime on an emulated Android device for testing or development purposes without having to wade through a ton of fuss and nonsense, you have two basic options.  The first is described briefly <a href="http://renaun.com/blog/2010/12/finding-the-air-for-android-emulator-runtime/" target="_blank">here</a>, and basically involves installing the latest Flash/Flex/AIR SDK from the <a href="http://www.adobe.com/products/air/sdk/" target="_blank">official website</a> and then grabbing the AIR runtime package from &#8216;<em>&lt;AIR_SDK_ROOT&gt;/runtimes/air/android/emulator/Runtime.apk</em>&#8216;.  This file is the AIR runtime that must be installed on the emulator for any apps built with Adobe AIR to function.  And this method works fine if you don&#8217;t mind downloading and installing the entire AIR SDK (90 MB) just to grab this one file.</p>
<p>You other option is to use this <a href="http://aroth.no-ip.org/com.adobe.air.emulator.2.7.apk">direct link</a> to download just the AIR runtime package (6.1 MB).  Note that this is version 2.7 of the AIR runtime, and has been tested with an emulator running Android 3.1 only.  I cannot vouch for it working with any other configurations.  </p>
<p>In either case, once you have gotten a hold of the the runtime .apk file, installing it on your emulator is a relatively simple process.  All you need is the &#8216;<em>adb</em>&#8216; utility that is included in the &#8220;Android SDK Platform Tools&#8221; package (note that this package is not the same as the similarly named &#8220;Android SDK Tools&#8221; package).  If you don&#8217;t have this package installed yet, then use your Android configuration manager to install it.  Then simply navigate to &#8216;<em>&lt;ANDROID_SDK_ROOT&gt;/platform-tools</em>&#8216; and run the following command (while your Android emulator is running):</p>
<pre class="brush: plain; title: ; notranslate">
adb install /path/to/your/AIR/runtime.apk
</pre>
<p>This will install the AIR runtime on your emulated device, and you can now install and run AIR-based Android apps on your emulator.  Quite simple, really.  Which only doubles my confusion with respect to why this simple process seems to be so poorly documented online.</p>
]]></content:encoded>
			<wfw:commentRss>https://codethink.no-ip.org/archives/699/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>[XCode] Building Universal iOS Frameworks</title>
		<link>https://codethink.no-ip.org/archives/688</link>
		<comments>https://codethink.no-ip.org/archives/688#comments</comments>
		<pubDate>Sun, 26 Jun 2011 11:56:12 +0000</pubDate>
		<dc:creator><![CDATA[aroth]]></dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[objective-c]]></category>
		<category><![CDATA[process]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[xcode]]></category>

		<guid isPermaLink="false">http://codethink.no-ip.org/wordpress/?p=688</guid>
		<description><![CDATA[Here&#8217;s an excellent little project that I stumbled across not too long ago: https://github.com/kstenerud/iOS-Universal-Framework This project provides a single installation shell script that will add a new Project Template to XCode, one that can be used to build universal iOS &#8230; <a href="https://codethink.no-ip.org/archives/688">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Here&#8217;s an excellent little project that I stumbled across not too long ago:</p>
<p><a href="https://github.com/kstenerud/iOS-Universal-Framework" target="_blank">https://github.com/kstenerud/iOS-Universal-Framework</a></p>
<p>This project provides a single installation shell script that will add a new Project Template to XCode, one that can be used to build universal iOS frameworks.  Now this wouldn&#8217;t be a big deal (and in all rights, <em>shouldn&#8217;t</em> be a big deal), if not for the fact that for some unspecified and frustrating reason, XCode has no built-in ability to create such frameworks.  This means that up until recently if you had an iOS library that you wanted to use across many projects, or that you wanted to distribute for other developers to use in their projects, you had only a few not-very-good options:</p>
<ul>
<li>Include your source code in every project that uses your library (increased compilation time, hooray!).</li>
<li>Build two library versions, one for armv6/armv7 and one for x86.</li>
<li>Screw with the linker to <a href="http://mark.aufflick.com/blog/2010/11/19/making-a-fat-static-library-for-ios-device-and-simulator" target="_blank">manually create a &#8220;fat&#8221; library</a>.</li>
<li>Use the <a href="http://db-in.com/blog/2011/05/creating-universal-framework-to-iphone-ios/" target="_blank">Bundle Hack</a> and hope that it works.</li>
</ul>
<p>It&#8217;s worth noting here that the first three options listed above do nothing to help with the management of any shared header files needed to actually make use of the library code.  The fourth option does allow you to include public headers in your build artifact, but the setup to do so is tedious, and the entire thing is basically a giant hack besides.  None of these options are really very convenient or useful from the developer&#8217;s point of view.</p>
<p>What developers really need, what they should have access to, and what XCode should have supported at least since the release of iOS 4.0, is the ability to build true universal iOS frameworks that are &#8220;real&#8221; frameworks in the same sense that UIKit and CoreGraphics are real frameworks.  And that&#8217;s exactly what the <a href="https://github.com/kstenerud/iOS-Universal-Framework" target="_blank">iOS-Universal-Framework</a> project gives you.  Just install it and you get a handy XCode Project Template that can be used to build a universal iOS framework.  This lets you build real iOS frameworks that include libraries for both x86 and armv6/armv7 in a single package, automatically bundled with your public header files.  You (or anyone else) can then use the generated framework inside of other projects in the same way as any other iOS framework; just add it to your &#8220;Link With Binary Libraries&#8221; build phase, and away you go.</p>
<p>All told, the  <a href="https://github.com/kstenerud/iOS-Universal-Framework" target="_blank">iOS-Universal-Framework</a> Project Template is great and works exactly as advertised.  Just install it and you&#8217;re good to go.  I&#8217;ve only come across one minor caveat worth mentioning, which is that although the template does correctly produce a universal iOS framework, the build product shown in XCode will actually point to an architecture-specific version of the framework.  In order to find the correct, universal framework it is necessary to do a &#8220;Show in Finder&#8221; on the build product shown in XCode (or otherwise navigate to your project output directory), and then go up a level (or two) in the filesystem to find the folder that contains the universal version of the framework (<em>edit:  as per Karl&#8217;s comment below, this has now been fixed.  Hooray!</em>).  </p>
<p>Apart from that minor annoyance this little XCode extension is really quite excellent, and I encourage you to give it a try.  My personal thanks to Karl Stenerud for putting this together.</p>
]]></content:encoded>
			<wfw:commentRss>https://codethink.no-ip.org/archives/688/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>[Objective-C + Cocoa] iPhone Screen Capture Revisited</title>
		<link>https://codethink.no-ip.org/archives/673</link>
		<comments>https://codethink.no-ip.org/archives/673#comments</comments>
		<pubDate>Wed, 15 Jun 2011 13:36:12 +0000</pubDate>
		<dc:creator><![CDATA[aroth]]></dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[objective-c]]></category>
		<category><![CDATA[hack]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[recording]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://codethink.no-ip.org/wordpress/?p=673</guid>
		<description><![CDATA[Awhile back I posted a handful of simple iOS utilities. Among them was a basic ScreenCaptureView implementation that would periodically render the contents of its subview(s) into a UIImage that was exposed as a publicly accessible property. This provides the &#8230; <a href="https://codethink.no-ip.org/archives/673">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Awhile back I posted a handful of simple <a href="http://codethink.no-ip.org/wordpress/archives/541">iOS utilities</a>.  Among them was a basic <em>ScreenCaptureView</em> implementation that would periodically render the contents of its subview(s) into a <em>UIImage</em> that was exposed as a publicly accessible property.  This provides the ability to quickly and easily take a snapshot of your running application, or any arbitrary component within it.  And while not superbly impressive (the iPhone has a built-in screenshot feature, after all), I noted that the control theoretically allowed for captured frames to be sent off to an <em>AVCaptureSession</em> in order to record live video of a running application.  </p>
<p>Recently I returned to this bit of code, and the ability to record live video of an application is theoretical no longer.  To get straight to the point, here is the revised code:</p>
<pre class="brush: cpp; title: ; notranslate">//
//ScreenCaptureView.h
//
#import &lt;UIKit/UIKit.h&gt;
#import &lt;AVFoundation/AVFoundation.h&gt;

/**
 * Delegate protocol.  Implement this if you want to receive a notification when the
 * view completes a recording.
 *
 * When a recording is completed, the ScreenCaptureView will notify the delegate, passing
 * it the path to the created recording file if the recording was successful, or a value
 * of nil if the recording failed/could not be saved.
 */
@protocol ScreenCaptureViewDelegate &lt;NSObject&gt;
- (void) recordingFinished:(NSString*)outputPathOrNil;
@end


/**
 * ScreenCaptureView, a UIView subclass that periodically samples its current display
 * and stores it as a UIImage available through the 'currentScreen' property.  The
 * sample/update rate can be configured (within reason) by setting the 'frameRate'
 * property.
 *
 * This class can also be used to record real-time video of its subviews, using the
 * 'startRecording' and 'stopRecording' methods.  A new recording will overwrite any
 * previously made recording file, so if you want to create multiple recordings per
 * session (or across multiple sessions) then it is your responsibility to copy/back-up
 * the recording output file after each session.
 *
 * To use this class, you must link against the following frameworks:
 *
 *  - AssetsLibrary
 *  - AVFoundation
 *  - CoreGraphics
 *  - CoreMedia
 *  - CoreVideo
 *  - QuartzCore
 *
 */

@interface ScreenCaptureView : UIView {
   //video writing
   AVAssetWriter *videoWriter;
   AVAssetWriterInput *videoWriterInput;
   AVAssetWriterInputPixelBufferAdaptor *avAdaptor;

   //recording state
   BOOL _recording;
   NSDate* startedAt;
   void* bitmapData;
}

//for recording video
- (bool) startRecording;
- (void) stopRecording;

//for accessing the current screen and adjusting the capture rate, etc.
@property(retain) UIImage* currentScreen;
@property(assign) float frameRate;
@property(nonatomic, assign) id&lt;ScreenCaptureViewDelegate&gt; delegate;

@end



//
//ScreenCaptureView.m
//
#import &quot;ScreenCaptureView.h&quot;
#import &lt;QuartzCore/QuartzCore.h&gt;
#import &lt;MobileCoreServices/UTCoreTypes.h&gt;
#import &lt;AssetsLibrary/AssetsLibrary.h&gt;

@interface ScreenCaptureView(Private)
- (void) writeVideoFrameAtTime:(CMTime)time;
@end


@implementation ScreenCaptureView

@synthesize currentScreen, frameRate, delegate;

- (void) initialize {
   // Initialization code
   self.clearsContextBeforeDrawing = YES;
   self.currentScreen = nil;
   self.frameRate = 10.0f;     //10 frames per seconds
   _recording = false;
   videoWriter = nil;
   videoWriterInput = nil;
   avAdaptor = nil;
   startedAt = nil;
   bitmapData = NULL;
}

- (id) initWithCoder:(NSCoder *)aDecoder {
   self = [super initWithCoder:aDecoder];
   if (self) {
       [self initialize];
   }
   return self;
}

- (id) init {
   self = [super init];
   if (self) {
       [self initialize];
   }
   return self;
}

- (id)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
   if (self) {
       [self initialize];
   }
   return self;
}

- (CGContextRef) createBitmapContextOfSize:(CGSize) size {
   CGContextRef    context = NULL;
   CGColorSpaceRef colorSpace;
   int             bitmapByteCount;
   int             bitmapBytesPerRow;

   bitmapBytesPerRow   = (size.width * 4);
   bitmapByteCount     = (bitmapBytesPerRow * size.height);
   colorSpace = CGColorSpaceCreateDeviceRGB();
   if (bitmapData != NULL) {
       free(bitmapData);
   }
   bitmapData = malloc( bitmapByteCount );
   if (bitmapData == NULL) {
       fprintf (stderr, &quot;Memory not allocated!&quot;);
       return NULL;
   }

   context = CGBitmapContextCreate (bitmapData,
                                    size.width,
                                    size.height,
                                    8,      // bits per component
                                    bitmapBytesPerRow,
                                    colorSpace,
                                    kCGImageAlphaNoneSkipFirst);

   CGContextSetAllowsAntialiasing(context,NO);
   if (context== NULL) {
       free (bitmapData);
       fprintf (stderr, &quot;Context not created!&quot;);
       return NULL;
   }
   CGColorSpaceRelease( colorSpace );

   return context;
}


//static int frameCount = 0;            //debugging
- (void) drawRect:(CGRect)rect {
   NSDate* start = [NSDate date];
   CGContextRef context = [self createBitmapContextOfSize:self.frame.size];
   
   //not sure why this is necessary...image renders upside-down and mirrored
   CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, self.frame.size.height);
   CGContextConcatCTM(context, flipVertical);

   [self.layer renderInContext:context];
   
   CGImageRef cgImage = CGBitmapContextCreateImage(context);
   UIImage* background = [UIImage imageWithCGImage: cgImage];
   CGImageRelease(cgImage);
   
   self.currentScreen = background;

   //debugging
   //if (frameCount &lt; 40) {
   //      NSString* filename = [NSString stringWithFormat:@&quot;Documents/frame_%d.png&quot;, frameCount];
   //      NSString* pngPath = [NSHomeDirectory() stringByAppendingPathComponent:filename];
   //      [UIImagePNGRepresentation(self.currentScreen) writeToFile: pngPath atomically: YES];
   //      frameCount++;
   //}

   //NOTE:  to record a scrollview while it is scrolling you need to implement your UIScrollViewDelegate such that it calls
   //       'setNeedsDisplay' on the ScreenCaptureView.
   if (_recording) {
       float millisElapsed = [[NSDate date] timeIntervalSinceDate:startedAt] * 1000.0;
       [self writeVideoFrameAtTime:CMTimeMake((int)millisElapsed, 1000)];
   }

   float processingSeconds = [[NSDate date] timeIntervalSinceDate:start];
   float delayRemaining = (1.0 / self.frameRate) - processingSeconds;

   CGContextRelease(context);

   //redraw at the specified framerate
   [self performSelector:@selector(setNeedsDisplay) withObject:nil afterDelay:delayRemaining &gt; 0.0 ? delayRemaining : 0.01];   
}

- (void) cleanupWriter {
   [avAdaptor release];
   avAdaptor = nil;
   
   [videoWriterInput release];
   videoWriterInput = nil;
   
   [videoWriter release];
   videoWriter = nil;
   
   [startedAt release];
   startedAt = nil;

   if (bitmapData != NULL) {
       free(bitmapData);
       bitmapData = NULL;
   }
}

- (void)dealloc {
   [self cleanupWriter];
   [super dealloc];
}

- (NSURL*) tempFileURL {
   NSString* outputPath = [[NSString alloc] initWithFormat:@&quot;%@/%@&quot;, [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0], @&quot;output.mp4&quot;];
   NSURL* outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];
   NSFileManager* fileManager = [NSFileManager defaultManager];
   if ([fileManager fileExistsAtPath:outputPath]) {
       NSError* error;
       if ([fileManager removeItemAtPath:outputPath error:&amp;error] == NO) {
           NSLog(@&quot;Could not delete old recording file at path:  %@&quot;, outputPath);
       }
   }

   [outputPath release];
   return [outputURL autorelease];
}

-(BOOL) setUpWriter {
   NSError* error = nil;
   videoWriter = [[AVAssetWriter alloc] initWithURL:[self tempFileURL] fileType:AVFileTypeQuickTimeMovie error:&amp;error];
   NSParameterAssert(videoWriter);

   //Configure video
   NSDictionary* videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:
                                          [NSNumber numberWithDouble:1024.0*1024.0], AVVideoAverageBitRateKey,
                                          nil ];

   NSDictionary* videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                  AVVideoCodecH264, AVVideoCodecKey,
                                  [NSNumber numberWithInt:self.frame.size.width], AVVideoWidthKey,
                                  [NSNumber numberWithInt:self.frame.size.height], AVVideoHeightKey,
                                  videoCompressionProps, AVVideoCompressionPropertiesKey,
                                  nil];

   videoWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings] retain];

   NSParameterAssert(videoWriterInput);
   videoWriterInput.expectsMediaDataInRealTime = YES;
   NSDictionary* bufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys: 
                                     [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil];

   avAdaptor = [[AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:videoWriterInput sourcePixelBufferAttributes:bufferAttributes] retain];

   //add input
   [videoWriter addInput:videoWriterInput];
   [videoWriter startWriting];
   [videoWriter startSessionAtSourceTime:CMTimeMake(0, 1000)];

   return YES;
}



- (void) completeRecordingSession {
   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

   [videoWriterInput markAsFinished];
   
   // Wait for the video
   int status = videoWriter.status;
   while (status == AVAssetWriterStatusUnknown) {
       NSLog(@&quot;Waiting...&quot;);
       [NSThread sleepForTimeInterval:0.5f];
       status = videoWriter.status;
   }

   @synchronized(self) {
       BOOL success = [videoWriter finishWriting];
       if (!success) {
           NSLog(@&quot;finishWriting returned NO&quot;);
       }

       [self cleanupWriter];

       id delegateObj = self.delegate;
       NSString *outputPath = [[NSString alloc] initWithFormat:@&quot;%@/%@&quot;, [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0], @&quot;output.mp4&quot;];
       NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];

       NSLog(@&quot;Completed recording, file is stored at:  %@&quot;, outputURL);
       if ([delegateObj respondsToSelector:@selector(recordingFinished:)]) {
           [delegateObj performSelectorOnMainThread:@selector(recordingFinished:) withObject:(success ? outputURL : nil) waitUntilDone:YES];
       }

       [outputPath release];
       [outputURL release];
   }

   [pool drain];
}



- (bool) startRecording {
   bool result = NO;
   @synchronized(self) {
       if (! _recording) {
           result = [self setUpWriter];
           startedAt = [[NSDate date] retain];
           _recording = true;
       }
   }

   return result;
}

- (void) stopRecording {
   @synchronized(self) {
       if (_recording) {
           _recording = false;
           [self completeRecordingSession];
       }
   }
}

-(void) writeVideoFrameAtTime:(CMTime)time {
   if (![videoWriterInput isReadyForMoreMediaData]) {
       NSLog(@&quot;Not ready for video data&quot;);
   }
   else {
       @synchronized (self) {
           UIImage* newFrame = [self.currentScreen retain];
           CVPixelBufferRef pixelBuffer = NULL;
           CGImageRef cgImage = CGImageCreateCopy([newFrame CGImage]);
           CFDataRef image = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));

           int status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, avAdaptor.pixelBufferPool, &amp;pixelBuffer);
           if(status != 0){
               //could not get a buffer from the pool
               NSLog(@&quot;Error creating pixel buffer:  status=%d&quot;, status);
           }
                       // set image data into pixel buffer
           CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
           uint8_t* destPixels = CVPixelBufferGetBaseAddress(pixelBuffer);
           CFDataGetBytes(image, CFRangeMake(0, CFDataGetLength(image)), destPixels);  //XXX:  will work if the pixel buffer is contiguous and has the same bytesPerRow as the input data

           if(status == 0){
               BOOL success = [avAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:time];
               if (!success)
                   NSLog(@&quot;Warning:  Unable to write buffer to video&quot;);
           }

           //clean up
           [newFrame release];
           CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 );
           CVPixelBufferRelease( pixelBuffer );        
           CFRelease(image);
           CGImageRelease(cgImage);
       }

   }

}

@end</pre>
<p>This class will let you record high-quality video of any other view in your application.  To use it, simply set it up as the superview of the <em>UIView(s)</em> that you want to record, add a reference to it in your corresponding <em>UIViewController</em> (using Interface Builder or whatever your preferred method happens to be), and then call &#8216;<em>startRecording</em>&#8216; when you are ready to start recording video.  When you&#8217;ve recorded enough, call &#8216;<em>stopRecording</em>&#8216; to complete the process.  You will get a nice .mp4 file stored under your application&#8217;s &#8216;Documents&#8217; directory that you can copy off or do whatever else you want with.</p>
<p>Note that if you want to record a <em>UIScrollView</em> while it is scrolling, you will need to implement your <em>UIScrollViewDelegate</em> such that it calls &#8216;<em>setNeedsDisplay</em>&#8216; on the <em>ScreenCaptureView</em> while the scroll-view is scrolling.  For instance:</p>
<pre class="brush: cpp; title: ; notranslate">- (void) scrollViewDidScroll: (UIScrollView*)scrollView {
       [captureView setNeedsDisplay];
}</pre>
<p>I haven&#8217;t tested this code on a physical device yet, but there&#8217;s no reason why it should not work on any device that includes H.264 video codec support (iPhone 3GS and later).  However, given the amount of drawing that it does, it&#8217;s safe to say that the more horsepower behind it, the better.  </p>
<p>Here is a rather unimpressive 30-second recording of a <em>UITableView</em> that I created using this class (if your browser doesn&#8217;t support HTML5, use the link below):<br />
<video width="320" height="460" controls><source src="http://codethink.no-ip.org/wordpress/wp-content/uploads/2011/06/output.mp4"></video><br />
<a href='http://codethink.no-ip.org/wordpress/wp-content/uploads/2011/06/output.mp4' target="_blank">Example iPhone Recording</a></p>
<p>Lastly, I haven&#8217;t tested this class with any OpenGL-based subviews, so I can&#8217;t say if it will work in that case.  If you try it in this configuration, please feel free to reply with your results.</p>
<p><strong>Update</strong></p>
<p>For anyone looking for a working example, you can download this <a href="http://codethink.no-ip.org/ScreenCaptureViewTest.zip">sample project</a>.  This project simply creates a 30-second recording of a &#8216;<em>UITableView</em>&#8216;.</p>
]]></content:encoded>
			<wfw:commentRss>https://codethink.no-ip.org/archives/673/feed</wfw:commentRss>
		<slash:comments>156</slash:comments>
<enclosure url="http://codethink.no-ip.org/wordpress/wp-content/uploads/2011/06/output.mp4" length="700174" type="video/mp4" />
		</item>
	</channel>
</rss>
