*Coder Blog

Life, Technology, and Meteorology

Category: Coding (page 1 of 3)

GSGradientEditor

A fairly significant feature in Seasonality Pro is the ability to edit the gradients used to show weather data on a map.  When looking around for some sample open source gradient editors online, I didn’t come across anything I could really use.  So I decided to write my own and offer it under an MIT license.  I posted the source code (link below) on GitHub.  Here’s what it looks like:

I’ve included a lot of documentation as well as a sample Xcode project to show how to use it over on the GitHub page:

GSGradientEditor on GitHub

I looked at quite a few different graphics apps when working on the UI.  I wanted to see not only how other implementations looked, but how they worked.  With iOS 7 being more gesture-centric, I wanted to make sure that interaction with GSGradientEditor was intuitive.  I found the Inkpad app most helpful during this process.  In the end, I like how GSGradientEditor turned out.

Enjoy!

GSShapefile

It’s been awhile since I’ve open sourced any code (cough…XRG…cough) and I thought it was about time to contribute something new.

This code is a small collection of classes that will parse ESRI Shapefiles. As I’m getting further into the development of Seasonality Pro (which you can follow at the new Seasonality: Behind the Scenes blog), I thought it would be important to be able to show Shapefile data on a map. There are a few basic implementations out there (see iOS-Shapefile and Cocoa Shapefile), but I wanted a more modern code design, and something that was flexible enough to add expanded support in the future. So I dug up the Shapefile spec and got started.

The result after hacking on it for a few days is GSShapefile. It should work on both the Mac and iOS platforms, as long as you have ARC enabled in your project. GSShapefile takes an NSData object as its input, so it doesn’t make any assumption of whether the data is coming from a local file or somewhere online. After the file is parsed, you can retrieve the shape records and points associated with each shape. It really should be pretty easy to integrate with your own code.

I hope somebody finds it helpful.

Storyboard Change

This Storyboard change bit me this morning. I was trying to do some initial calculations from my view geometry before it is displayed at app launch. Previously, when using XIB files to define an interface, you would be able to see your view geometry as early as viewWillAppear: in your UIViewController.

With Storyboards, view geometry will not be ready until later. In my case, within viewWillAppear: the view bounds returned all zeros. Took a bit of Googling to find the answer, but eventually came across this question at StackOverflow:

Gettting [sp] subViews’ frames in Storyboard

In short, if you need your view geometry as early as possible, use viewDidLayoutSubviews in your UIViewController.

The intersection of two NSIndexSets

If you are looking for code to compare two NSIndexSets and return their intersection, this is for you. The algorithm is pretty easy, so I decided to wrap it together and post it to Gist. It’s implemented as a category on NSIndexSet.

NSIndexSet+GSIndexSetAddditions

Overhead while using GCD

Today I spent some time optimizing the Particle Mode simulation code in Seasonality Core. While doing some measurements, I discovered that quite a bit of time was spent in GCD code while starting new tasks. I use dispatch_apply to iterate through the particles and run the position and color calculations for the next frame. In the tests below, I was simulating approximately 200,000 particles on the Macs, and 11,000 particles on the iPad.

I decided to try breaking the tasks up into fewer blocks, and run the dispatch_apply for groups of around 50 particles instead of running it for each particle. After making this change, the simulation ran in up to 59% less CPU time than before. Here are some informal numbers, just by looking at Activity Monitor and roughly estimating:

CPU Usage
Device   Before   After   Time Savings
Mac Pro (2009, Oct 2.26Ghz Xeon)   390%   160%   59%
Retina MBP (2012, Quad 2.6Ghz i7)   110%   90%   18%
MacBook Air (2011, Duo 1.8Ghz i7)   130%   110%   15%
 
iPad 3 (fewer particles)   85%   85%   0%

As you can see, the benefits from the new code running on the Mac Pro are substantial. In my earlier code, I was somewhat suspicious of why the simulation took so many more resources on the Mac Pro than on the laptops. Clearly the overhead in thread creation was a lot higher on the older Xeon CPU. This brings the Mac Pro’s processing times closer to what the other more modern processors can accomplish.

Perhaps an even more surprising result is the lack of a speedup on the iPad. While measuring both runs, the two versions averaged about the same usage. Perhaps if I had a more formal way to measure the processing time, a small difference might become apparent, but overall the difference was minimal. I’m guessing that Apple has built logic into the A-series CPUs that allows for a near 0 cost in context switching. Makes you wonder how much quicker something like this would run if Apple built their own desktop-class CPUs.

Living in a Sandboxed World

No matter what your view on Apple’s new sandboxing requirement for the Mac App Store, if you want to keep updating your MAS apps, you’re going to need to sandbox them. I was able to sandbox Seasonality Core pretty easily. I don’t access any files outside of Application Support and the prefs file. My entitlements just require outgoing network connections, which was pretty easy to enable in the target settings.

However, I distribute two versions of Seasonality Core. One is the Mac App Store application, and the other is a version for my pre-Mac App Store customers. The question arose: should I sandbox the non-Mac App Store application? I wanted the answer to this question to be yes, but unfortunately the serial number licensing framework I am using kept me from doing this. So I was forced to sandbox the Mac App Store version, but keep the non-Mac App Store version outside the sandbox. Crap.

You might be wondering what the big deal is here. Can’t my Mac App Store customers just use one app, and pre-Mac App Store customers use the other one? Well, yes, but there are a few situations where some customers might use both versions of the app.

If someone uses both the Mac App Store version and the non-Mac App Store version, things go south quickly. The first time the sandboxed Mac App Store version is run, all of Seasonality Core’s data files will be migrated into the sandbox. That means the next time the non-Mac App Store version is opened, it won’t be able to see any of the past data Seasonality Core has collected. That’s not good.

So how did I get around this? After taking a quick poll on Twitter, it sounded like the best option for me would be to have the non-Mac App Store version look reach inside my app’s sandbox if it existed. To do this, I just had to build some extra code into the method that returns my Application Support path. Here’s the new implementation:

+ (NSString *) seasonalityCoreSupportPath { NSFileManager *fm = [NSFileManager defaultManager]; #ifndef MAC_APP_STORE // Check if ~/Library/Containers/BundleID/Data/Library/Application Support/Seasonality Core exists. NSString *sandboxedAppSupportPath = [NSString pathWithComponents: [NSArray arrayWithObjects:@"~", @"Library", @"Containers", [[NSBundle mainBundle] bundleIdentifier], @"Data", @"Library", @"Application Support", @"Seasonality Core", nil] ]; sandboxedAppSupportPath = [sandboxedAppSupportPath stringByExpandingTildeInPath]; BOOL isDir; if ([fm fileExistsAtPath:sandboxedAppSupportPath isDirectory:&isDir]) { // We found a sandboxed Application Support directory, return it. if (isDir) return sandboxedAppSupportPath; } #endif NSArray *appSupportURLs = [fm URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask]; NSString *appSupportDirectory = nil; if (appSupportURLs.count > 0) { NSURL *firstPath = [appSupportURLs objectAtIndex:0]; appSupportDirectory = [firstPath path]; } return [appSupportDirectory stringByAppendingPathComponent:@"Seasonality Core"]; }

The new code only runs if the MAC_APP_STORE isn’t defined (these are project definitions I have set elsewhere for the different builds). We check to see if there is a sandbox for the app, and if so it will return the sandboxed directory. Otherwise it returns the standard Application Support directory.

This is a pretty complete solution, except that I wanted to make sure the user’s preferences were saved between the two app versions as well. NSUserDefaults won’t know to check for the existence of a sandbox. Daniel Jalkut gracefully offered this solution, which I have since adapted into my own code as follows:

+ (BOOL) gsImportNewerPreferencesForBundle:(NSString *)bundleName fromSandboxContainerID:(NSString *)containerID { BOOL didMigrate = NO; NSArray *libraryFolders = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); if (libraryFolders.count) { // Get a path to our app's preference file. NSString *prefsFile = [NSString pathWithComponents:[NSArray arrayWithObjects: [libraryFolders objectAtIndex:0], @"Preferences", bundleName, nil ]]; prefsFile = [prefsFile stringByAppendingPathExtension:@"plist"]; // Get a path to the same preference file in the given sandbox container. NSString *containerPrefsFile = [NSString pathWithComponents:[NSArray arrayWithObjects: [libraryFolders objectAtIndex:0], @"Containers", containerID, @"Data", @"Library", @"Preferences", bundleName, nil ]]; containerPrefsFile = [containerPrefsFile stringByAppendingPathExtension:@"plist"]; NSFileManager* fm = [NSFileManager defaultManager]; if ([fm fileExistsAtPath:containerPrefsFile]) { NSDate *prefsModDate = [[fm attributesOfItemAtPath:prefsFile error:nil] objectForKey:NSFileModificationDate]; NSDate *containerModDate = [[fm attributesOfItemAtPath:containerPrefsFile error:nil] objectForKey:NSFileModificationDate]; if ((prefsModDate == nil) || ([prefsModDate compare:containerModDate] == NSOrderedAscending)) { // Copy the file. [fm copyItemAtPath:containerPrefsFile toPath:prefsFile error:nil]; // Reset so the next call to [NSUserDefaults standardUserDefaults] // recreates an object to the new prefs file. [NSUserDefaults resetStandardUserDefaults]; NSLog(@"Found newer preferences in %@ - importing", containerPrefsFile); didMigrate = YES; } } } return didMigrate; }

I call the above preferences migration code directly from main(), so it executes before the any part of the main app might hit NSUserDefaults. Works pretty well thus far.

Using IOKit to Detect Graphics Hardware

After Seasonality Core 2 was released a couple of weeks ago, I received email from a few users reporting problems they were experiencing with the app. The common thread in all the problems was having a single graphics card (in this case, it was the nVidia 7300). When the application launched, there would be several graphics artifacts in the map view (which is now written in OpenGL), and even outside the Seasonality Core window. It really sounded like I was trying to use OpenGL to do something that wasn’t compatible with the nVidia 7300.

I’m still in the process of working around the problem, but I wanted to make sure that any work-around would not affect the other 99% of my users who don’t have this graphics card. So I set out to try and find a method of detecting which graphics cards are installed in a user’s Mac. You can use the system_profiler terminal command to do this:

system_profiler SPDisplaysDataType

But running an external process from within the app is slow, and it can be difficult to parse the data reliably. Plus, if the system_profiler command goes away, the application code won’t work. I continued looking…

Eventually, I found that I might be able to get this information from IOKit. If you run the command ioreg -l, you’ll get a lengthy tree of hardware present in your Mac. I’ve used IOKit in my code before, so I figured I would try to do that again. Here is the solution I came up with:

// Check the PCI devices for video cards.  
CFMutableDictionaryRef match_dictionary = IOServiceMatching("IOPCIDevice");

// Create a iterator to go through the found devices.
io_iterator_t entry_iterator;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, 
                                 match_dictionary, 
                                 &entry_iterator) == kIOReturnSuccess) 
{
  // Actually iterate through the found devices.
  io_registry_entry_t serviceObject;
  while ((serviceObject = IOIteratorNext(entry_iterator))) {
    // Put this services object into a dictionary object.
    CFMutableDictionaryRef serviceDictionary;
    if (IORegistryEntryCreateCFProperties(serviceObject, 
                                          &serviceDictionary, 
                                          kCFAllocatorDefault, 
                                          kNilOptions) != kIOReturnSuccess) 
    {
      // Failed to create a service dictionary, release and go on.
      IOObjectRelease(serviceObject);
      continue;
    }
				
    // If this is a GPU listing, it will have a "model" key
    // that points to a CFDataRef.
    const void *model = CFDictionaryGetValue(serviceDictionary, @"model");
    if (model != nil) {
      if (CFGetTypeID(model) == CFDataGetTypeID()) {
        // Create a string from the CFDataRef.
        NSString *s = [[NSString alloc] initWithData:(NSData *)model 
                                            encoding:NSASCIIStringEncoding];
        NSLog(@"Found GPU: %@", s);
        [s release];
      }
    }
		
    // Release the dictionary created by IORegistryEntryCreateCFProperties.
    CFRelease(serviceDictionary);

    // Release the serviceObject returned by IOIteratorNext.
    IOObjectRelease(serviceObject);
  }

  // Release the entry_iterator created by IOServiceGetMatchingServices.
  IOObjectRelease(entry_iterator);
}

Catchup

Wow, I think this is the first time I’ve opened MarsEdit in months. Looks like my last post was back in February, so I figure an update here is long overdue. I don’t have any particular topic to talk about today, so this post will be a catchup of everything happening here in the past 3 months.

The biggest change has been a new consulting gig I picked up back in March. Clint posted on Twitter about a contract position for an iPhone developer on the Ars Technica Job Board. The kicker is that the job was to code a weather application. I had been curious about iPhone coding, but didn’t have time in my development schedule to fit another pet project. On the other hand, if I could learn iPhone development while getting paid, I could definitely shift some projects around. Being a weather app, this job matchup was too good to pass up; so I sent in my resume one morning back in March. That afternoon, the company got in touch with me for an interview, and the following week I flew out to their headquarters to get up to speed on the project.

The development cycle for this app was pretty quick. With the first deadline of a working demo only 3 weeks from the day I started, I really booked it and started pumping out code. My life was pretty much coding, from time I woke up until going to bed. A rough, but fairly good demo was completed, with 10k lines of code in those first 3 weeks. I had about a week off, which incidentally was the same week of my 30th birthday. It was great to take a little bit of time off, party with some friends, and enjoy life.

Then the second stage of the project kicked in, which needed to be completed in only 2 more weeks time. The second stage was definitely slower, so I was able to sleep a little bit more, and see Katrina from time to time. 🙂 The resulting stage 2 app was pretty polished. The company I’m working with has a few contacts at Apple, so they arranged to demo it in Cupertino. That was a couple of weeks ago and from what I heard, the demo went pretty well. All the work definitely paid off. You should be seeing this product hit the market some time this summer. I’ll definitely post more about this when the time comes.

Our Moke After all that work and Katrina’s semester coming to a close, we decided to take off on a vacation. We found a great deal on airfare and hotel down to Barbados, so we decided to jump on it. We spent last week on the south coast of the island soaking up the sun, learning the culture, having a blast driving around in our little moke (see photo), and just getting some good R&R. There’s not a ton of stuff to do on the island, but definitely enough to keep you occupied for a week or two. We toured one of the 14 Concorde jets in existence, visited some caves, walked a historical museum, snorkled with some sea turtles, and enjoyed some excellent food.

With a constant 15 mph trade wind, the surf on Barbados was better than any other Caribbean island I’ve visited. Furthermore, our hotel room opened up onto the beach, so I was able to walk about 50 feet from our patio and paddle out to bodyboard. Needless to say, several surf sessions took place that week.

With summer finally finding it’s way to central Michigan, the mountain biking season has now begun. Bodyboard being a fairly difficult activity in Michigan, mountain biking has become my main form of exercise. For the past 10 years, I’ve been riding a Trek hardtail. I’ve put over 3000 miles on it, and the gears are almost completely shot. So I was posed with a decision of either spending a couple hundred bucks on a new set of cogs, bearings, and a chain, or breaking down and purchasing a whole new bike.

I had been looking at getting a full suspension bike for the past few years, so I started visiting bike shops around here to ride some different models. I had hit every bike shop in a 30 mile radius, without any luck. Finally, while we were down in Lansing for the day, I checked a few bike shops down there and found my new ride. Of course the bike shop didn’t have the right frame size, so I had to order it.

New Bike

A week later, it arrived, and I picked it up the day after we got back from Barbados. So far, I love it. It’s a Trek Fuel EX 5.5 complete with disc brakes, 3-5 inches of adjustable travel in front, and 5 inches of travel in back. Clipless pedals were not included so I swapped mine out from the old bike. I also added a seat pack (with tools to fix a flat and a few other necessities) and installed a new speedometer. My previous bike was so old, that even with the full suspension upgrade and a much beefier frame, this bike is lighter than my last. This weekend will be the first time I take it on the trail…definitely looking forward to it.

Looking toward the summer, I’ll be headed out to WWDC in San Francisco next month. A lot of good parties are starting to fall into place, so it should be a fun week. After that, we’re heading over to camp in Yosemite for a few days before coming home and spending the rest of the summer here working.

CFNetwork Versions

Sometimes it’s important to know what version of Mac OS X your users are running, especially when making decisions on what versions of OS X to support in future software releases. In the case of Seasonality 2.0, I have decided to take advantage of all the developer changes in Leopard, both to make Seasonality a better application, and to shorten my development time (thus giving me more time to work on additional features).

Previously, I haven’t performed any OS statistics on user data. It would be easy to do, since Seasonality downloads forecast and image data from a web server here at Gaucho Software, but I haven’t written the code required. However, some of these Seasonality data requests are using the typical CFNetwork methods of downloading data, and these connections provide the current CFNetwork version in the HTTP UserAgent, and thus will show up in my web server logs.

The problem is that I have been unable to find any kind mapping between CFNetwork versions and the corresponding version of Mac OS X. I decided to take it upon myself to generate (and hopefully maintain) such a list here. Most of this data is from viewing the Darwin source code on Apple’s web site, but some of these are just from personal observations, and some are educated guesses (marked with a question mark).

HTTP UserAgent Version of Mac OS X
CFNetwork/454.4 Mac OS X 10.6.0
CFNetwork/438.14 Mac OS X 10.5.8
CFNetwork/438.12 Mac OS X 10.5.7
CFNetwork/422.11 Mac OS X 10.5.6
CFNetwork/339.5 Mac OS X 10.5.5
CFNetwork/330.4 Mac OS X 10.5.4
CFNetwork/330 Mac OS X 10.5.3
CFNetwork/221.5 Mac OS X 10.5.2
CFNetwork/221.2 Mac OS X 10.5.2 Developer Seed?
CFNetwork/220 Mac OS X 10.5.1?
CFNetwork/217 Mac OS X 10.5?
CFNetwork/129.22 Mac OS X 10.4.11
CFNetwork/129.20 Mac OS X 10.4.9 – 10.4.10
CFNetwork/129.18 Mac OS X 10.4.8
CFNetwork/129.16 Mac OS X 10.4.7
CFNetwork/129.13 Mac OS X 10.4.6
CFNetwork/129.10 Mac OS X 10.4.4 – 10.4.5 (Intel)
CFNetwork/129.9 Mac OS X 10.4.4 – 10.4.5 (PPC)
CFNetwork/129.5 Mac OS X 10.4.3
CFNetwork/128.2 Mac OS X 10.4.2
CFNetwork/128 Mac OS X 10.4.0 – 10.4.1
CFNetwork/4.0 Mac OS X 10.3 or earlier

Edit: A more modern list of UserAgents can be found here.

Inside DynDNS Updater 2.0

I’m thrilled to link to a major update of DynDNS’s Mac client, DynDNS Updater 2.0. I’ve been working on the DynDNS Updater for quite some time, and this release is a complete re-write that has a lot of new functionality. Looking at 1.x and 2.0, you would never know the projects were related; there are far too many changes to even list. To try and summarize, DynDNS Updater 2.0 is built from the ground up to give a completely modern Mac experience to people who use the services provided by DynDNS. The updater now has auto Sparkle updating built-in, Growl integration, a custom Dashboard Widget, and an interface that looks right at home on both Tiger and Leopard. Core technologies like OpenSSL, libcurl, and pthreads are used on the back-end.

In case you aren’t familiar with DynDNS as a company, their products solve the problem of managing DNS for just about every type of user, from individuals to large companies. I believe Dynamic DNS is the project that is most well-known (at least that is how I heard of them years ago), giving users with dynamic IPs a method of having a static name point to their computer. The DynDNS Updater’s primary function is to watch out for IP address changes on your Mac, and let the DynDNS servers know when there is a change. The updater is broken up into two separate executables, one is a Cocoa application where you configure your accounts and hosts. The other executable is a background daemon running 24/7 that performs all the grunt work.

I started working on version 2.0 last year. Jeremy Hitchcock and the rest of the DynDNS crew had some nice feature ideas for the next version, and I mocked up a design that would implement some of these features. I was pretty excited about the project and started working on what was to be a fairly advanced update. Fast forward to around March or April of this year when the first beta was being wrapped up. This beta had the foundation of a solid background daemon where I tried to stick to the basic POSIX libraries, so it could run on multiple platforms. At one point, I was regularly compiling it on Linux and it was working well (I haven’t tried this for quite some time since then). The daemon was completely threaded, and supported multiple network connections. Unfortunately, while the code design was solid and the product had a lot of nice new features, the UI was horribly difficult to use and the interface to configure these complex setups was not pretty. The mock-ups looked nice, but actually using it is where we ran into problems. Seemingly simple operations took multiple steps, because a lot of advanced options had to be taken into account.

Enter FJ de Kermadec and his team at Webstellung. They suggested bringing the project back to basics–thinking more about the typical use case and not all the features we could give to power users in unique situations. Webstellung put together some very impressive UI design mock-ups to get things started. Jeremy gave the go-ahead, and it was back to the drawing board as I began coding up the fresh interface. Fortunately, since the daemon code was designed with flexibility in mind, it could be re-used by dropping the new interface on top of it. The initial UI creation went pretty quickly, and within a few months a new application was born. The first beta was ready just before WWDC this year. As soon as I started using this UI, I knew it was a keeper. There were a few adjustments that had to be made, but overall the interface is very intuitive. After the functionality was mostly complete, it was time to focus on the interface polish. We went through several iterations of the software, catching small changes here and there that all added up to a nice shiny app.

If you’re currently using DynDNS Updater 1.2, definitely upgrade to version 2.0. And if you aren’t, download the app anyway and check it out. Dynamic DNS accounts are free for up to 5 hosts, and it’s pretty easy to set everything up.

Older posts

© 2017 *Coder Blog

Theme by Anders NorenUp ↑