Updates

Saturday, July 18th, 2009

Wow, you all must be so sick of hearing from me. I was really trying to wait another week, until the one year anniversary of my last post before writing this, but I just could not resist.

And thus(ly) I begin a chronological blog-journey of what’s happened in my ongoing quest for awesomeness:

  • Epic entrance into the world that is iPhone with the release of People 1.0, a white pages application. One of the first 500 applications available on day one of the app store
  • People won the Best iPhone App in the the WhitePages.com API contest, and I got a sweet MacBook Pro out of it.
  • Did pretty much nothing for the rest of the summer. Just hung out with friends and feared my most certain impeding doom to come in mid August
  • Started my freshman year at MIT. Confirmed my suspicions that I can never be a mechanical engineer, in a single week, before classes even started.
  • Started classes. Woot exciting GIRs: 18.02, 8.01, 3.091, and 21M.011
  • Learned what GIRs are (classes all MIT students are required to take) and what those strange numbers mean (Multivar Calc, Physics (Mechanics), Solid State Chem, and Into to Western Music, respectively)
  • Unibody MacBook Pros, grrrr…….
  • Discovered that MIT is not impossible and that people in this horrid society stress way too much
  • Ahhh problem sets! What ever shall I do‽‽‽ (those are interrobangs for those less cultured than I).
  • Learned to love the amazing efficiency achieved by referring to everything with numbers. I need only say, “I’m off to 18.02 in 10-250″, and everyone will know exactly what I mean and will wish me luck in getting a seat close to the front of the giant lecture hall such that I might get a good view of Prof. Auroux’s dreamy eyes.
  • Finals… </pass no record>
  • Came back for IAP and competed in the 6.270 competition (autonomous lego robots). I had tons of fun and pulled my first work all nighter. We got like 5th place or something. Check out our super cute robot: http://web.mit.edu/adaml/www/6.270/
  • Second semester: 18.03 (diff eq), 8.02 (e&m), 6.01 (awesome but extremely vague “Intro to Computer Science and Electrical Engineering”), STS.003 (rise oF MOdern SCIENCE!!). Hard, but pretty awesome
  • Declared Course 6-1. I’m a CS major. hurrays!
  • Approaching finals….. disliking all those people who stress out too much, getting stressed out and thusly hating myself.
  • Summer!!! (for 2 weeks). Relaxed, hung out, and started and finished People 2.0.
  • My life officially begins; I start twittering. Please follow me. It will make me feel super cool and important. http://www.twitter.com/adamleonard.

Then, I started my internship at Apple. It’s been incredibly awesome and fun. Sorry, I can’t say anything about it, but I’m working on some really cool stuff. It super sucksies that I have to wait like 3 more years before I can start working at Apple (or somewhere as awesome as Apple) full time.

But yes, what inspired me to write today was to announce that finally(!) People 2.0 is available on the app store. It’s got some cool new stuff and is compatible with iPhone OS 3.0, so try it out!

I apologize for ignoring my other apps (PhotoBook and Google Importer). I really do still love them dearly, but understand that I work on these apps in my spare time, for free, just for fun, when I have a cool idea or something. Since said time is quite valuable, I find myself not working on them for months, or even decades (well, not decades).

And to all youz developerz out therez. Photobook is open source, so if you want to add a feature or fix a bug, please do so!

A super special, completely rewritten and ambitious, vaporware edition of Google Importer is also fairly far along, but I really have no idea when I will conjure up enough interest and time to finish it… I greatly apologize to those who have been persistently asking for it. Again, if there are any developers out there who want to work on it, just shoot me an email.

And finally, I will leave you with a detailed outline of my ambitious goals for the rest of my life:

  • Purchase 17″ Unibody MacBook Pro with employee discount

PhotoBook Has Gone Open Source!

Sunday, July 27th, 2008

I have an exciting announcement today.

I am releasing the source code for PhotoBook.
As you may know, PhotoBook is a Facebook photo browser for Mac.

All code is being released under the MIT license (guess why :) ). Unlike the GPL, this license allows for a lot of flexibility with what can be done with the code.

Visit the Google Code project page for more details and to download the source.

I have two main goals in doing this:

  • First, PhotoBook is freeware and I don’t have a particularly strong reason as to why I should hold onto the source. By releasing it into the wild, I hope other Cocoa developers will find some use for it in their own projects. The license allows pieces of code to be used in other applications (free or for profit) with few restrictions. It is a small thank you for all the other open source code I use and love.
  • Second, I hope this will help foster faster development. There has not been a new release in nearly seven months due to school and other projects. I know the time I will be able to spend working on PhotoBook will decrease even more next year. So, by making it a community based project, others will be able to help fix bugs, add new features, etc. I have already been in contact with one developer working on an amazing, revolutionary feature that you have never seen before in a photo application.

If you are interested in joining the project, checkout the code. Post patches on the Google Code page, or send me an email (adam [at] [this domain].com), and I’ll add you as a developer so you can make commits.

I should note that I did not originally plan to release the source to PhotoBook, so it is not as commented and such as I would like, and some features are implemented in special (read:lazy) ways. I will work on this soon.

The code currently in the repository is basically the 1.1 source with a few bug fixes.

If the project takes hold, it should be really exciting. I welcome any feedback.

Also, look for some more open source announcements in the near future.

Antisocial Sudoku Fun

Saturday, May 24th, 2008

Hurray, I’m not dead!
Yes, that’s right. Despite an impossibly long period of total invisibility on the interwebz, I am still here.
Basically, I have been busy with school, and all my programming stuff has been pushed down to a pretty low priority.
(By the way, free iPhones, computers, etc., might very well prevent this from happening again :)).

So, you might have noticed that I chose a new theme for this blog. It is called Abstracta, and was designed by Rob Goodlatte. I love it. Very pretty and clean.

Ok, so update time:

  • I am officially a member of the MIT class of 2012! I’ll be off to Boston in the fall to meet lots of people smarter than me as I learn that I HTFP
  • Almost as exciting, I have not gotten anything done on Web Importer or PhotoBook. You’ll see so many updates this summer, I swear! I have like totally nothing to do until the end of August.
  • But I have done a little work on Adium. Look for live contact list searching in the next release (1.3)

And now… story time!

So, a couple of weeks ago, I finished all my AP tests (which, if you don’t know, are classes+tests high school students take in particular subject areas that are supposed to be “college level.” I took AP English Lit, AP Calculus BC, and AP Physics C). After those tests, we have very little work left to do for the last 5ish weeks of school, so my physics teacher gave us a Sudoku (probably to keep us quiet). I have never done a Sudoku before, and needless to say, I could not get the 5 star one she gave us. Frustrated, I sort of forgot about it.

Then, last weekend was Prom. All the girls, and I suppose some of the guys, felt the need to go through the predictable ritual of spending hundreds on hair and plastering themselves in makeup. I was quite bored, and instead of doing something normal like going outside or seeing a movie or something, I decided to revisit that Sudoku. Now, instead of doing something a typical antisocial sudoku player would do, like spending another hour trying to solve it, or something a self-conscious antisocial sudoku player would do, like going online and cheating to find the solution, I decided to be a self-conscious, antisocial, geeky sudoku player and write my own Sudoku solver.

Luckily, I had recently seen this xkcd comic, so I felt a bit better about myself.

And just before I left for fun pictures and such, I had a working Cocoa sudoku solver that solved the puzzle in about a quarter of a second.

I added some finishing touches this week, so I thought I might as well put it up here and release the code.

Don’t expect anything amazing or original. This just uses what is basically a brute force algorithm to solve the puzzle. And, even that algorithm is written very poorly and is quite ugly. If you are looking for a useful Sudoku solver, just search Google. There are tons of really good human logic sudoku solvers available.

But mine, of course, is better.

If you are learning Cocoa, this project demonstrates these topics:

  • Interface Builder and standard controls
  • Using an NSMatrix to lay out a grid of NSCells (81 Sudoku boxes)
  • Using NSNumberFormatters (to ensure that only the integers 1-9 can be entered into a box)
  • Basic threading with NSThread (the solving algorithm runs in the background in its own thread)
Download the pre-built application
Download the Source Code (BSD License)

Accessibility for The Rest Of Us

Saturday, January 26th, 2008

So, in addition to giving you an update on Google Importer, it is about time for another Cocoa post.

Google(Web) Importer for Leopard makes extensive use of the Accessibility API. This may seem a bit weird. Most people would think it would be for things like screen readers and speech recognition, but it can be used for a lot more. For example, the Watch Me Do feature in Automator that lets you record and replay every action you do in an application uses the Accessibility API. Google(Web) Importer will use it to get the search terms from Spotlight and the Finder.

The Accessibility API is great for getting the state of the GUI, such as the location of the mouse, the text typed into a field, or the button a user clicks. It is also great for controlling the GUI, like actually clicking a button or entering text. So, it can really be used in a lot of applications.

(By the way, the front end to the Accessibility API in Applescript is called UI Scripting. Same idea.)

Also, unlike Input Managers (which Google Importer for Tiger used), using the Accessibility API:

  • Is fully supported and documented
  • Works with practically every application (not just Cocoa apps)
  • Should not break between releases, if coded carefully

Now, I don’t think it is necessary to write about how to use the API. It is a C API, but just read the documentation and look at this sample code that demonstrates just about everything.

Instead, I will focus on something else that probably will be purely for the benefit of the mighty search engines…

Aside from being really useful, the Accessibility API is also really powerful and therefore subject to abuse. For example, one could very easily write a keylogger with it. That is probably why Apple decided to disable it by default. To enable it, the user has to open System Prefrences, click on Universal Access, and check the box labeled “Enable access for assistive devices.” Now, there are a couple problems with that approach:

  • It enables access system wide (or user wide, I’m not sure). That means that any application can use it if that little box is checked. In the keylogger example, if the user checked that box to let Automator use it legitimately, the keylogger can also use it without any sort of notification to the user.
  • It is annoying. A user should not be asked to change their preferences just to run your application.

So, Apple came up with another solution in Tiger that solves these problems: the magic function AXMakeProcessTrusted. That will enable the API just for your application and needs to be called from a process running as root, so it is secure. It is also all automatic, so aside from asking the user for his/her password, there is nothing the user needs to do.
The problem is that it seems no one uses it. Every third party application I have seen and even Automator just asks the user to manually check the box in System Preferences. It is more work to implement and has one huge undocumented bug (the application must be relaunched before it is actually trusted Update: reported as #5711990), but I really think people should be using it. So, I thought I would release the code to make it easy to implement it in your app. It includes a helper agent that you should be able to just drop into your project.

Download TrustMe

Image Metadata

Saturday, October 6th, 2007

Digital cameras are really cool in that whenever you take a picture, it saves a lot more than just the image data. Right in the image file, it adds the date it was taken, the length of the flash, the exposure time, the model of the camera, and much more. If you have an expensive camera with GPS, it will even save the location at which the photo was taken. 

Most of this information is stored with EXIF (Exchangeable image file format). This is a pretty much universally adopted standard, so most cameras and photo programs use and save the data. iPhoto and Preview, for example, allow you to view this information easily by opening the Info window.

The problem is that Cocoa does not currently have great support for EXIF. Also, the support it does have is horribly documented. If you are working with photos in a cocoa app, however, it is important to understand and support it. Even if you have no intention to work with the EXIF data at all, if you mess with the image data, you need to make sure you preserve all the metadata when you save the image. Depending on what you do, this may not happen automatically.

To work with EXIF in a cocoa app, you will need to dive into Carbon a bit, but it is really not that bad. Also, note that the code in this article requires Tiger or later. You will need to use another tool to get metadata out of images if you need to support Panther.

Finally, I hear Leopard will have a new framework for working with images, ImageKit. This might make some of this stuff easier to do. I don’t know, as I am not lucky enough to have the beta :)

Getting Metadata

So, most commonly, you just want to read the metadata from a photo and use in some manner. Magrathea, for example, would see if any photo you tried to import contained GPS EXIF properties, and if so, place the photo on the map automatically for you.

You will need to use the ImageIO framework, so first add ApplicationServices.framework to your project. Then, import it into your file.

Next, you need to create a CGImageSourceIf you are reading the image from disk (or the internet) use:

 CGImageSourceRef source = CGImageSourceCreateWithURL( (CFURLRef) aUrl, NULL);

Note that, thanks to Toll-Free Bridging, aUrl can be a NSURL, even though the function takes a CFURLRef. There is no need to convert the URL first. The cast just calms the compiler down so it doesn’t throw a warning.

If you already have the image as a NSImage, you will need to get the data out of the image first. The easiest way to do this is just to use -TIFFRepresentation, and that will give you a NSData object you can use. Then, create a CGImageSourceRef with:

 CGImageSourceRef source = CGImageSourceCreateWithData( (CFDataRef) theData, NULL);

Again, Toll-Free Bridging allows you to pass the NSData object right in.Now, you can get a NSDictionary with all the image’s metadata:

 NSDictionary* metadata = (NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source,0,NULL);

Toll-Free Bridging works both ways. Yay!

Now, you are pretty much done with Carbon and you can start getting the metadata you want.

All the keys in the metadata dictionary are listed here.

So, say you want to get the date the photo was taken. You would just use the key kCGImagePropertyExifDateTimeOriginal

(Note that all the keys are CFStringRef’s, so you should cast them to an NSString * before using them with NSDictionary)

You might wonder what the format of the date is. Well, just look what the documentation says:kCGImagePropertyExifDateTimeOriginalSpecifies the original date and time.

Thanks! In fact, none of the keys have any documentation on what the hell they actually contain.

Most of it should be pretty self explanatory, but if you are unsure you can check the spec (the good stuff starts on page 17) Core Graphics uses the same terminology for the names of the keys, so it is not that hard to find.Also, something important to note, is that unless the documentation says otherwise, all the properties are returned as CFStringRef’s/NSString’s. That means you will need to do a little extra work if you want to use a property that contains a date or a number.

So, with dates for example, the spec says dates are returned in the format YYYY:MM:DD HH:MM:SSThis is slightly different than the format NSDate expects (YYYY-MM-DD HH:MM:SS ±HHMM). Here is some quick code to convert it to that format (unformattedDateAsString is the NSString returned from the metadata dictionary): 

NSDate date = nil;
*NSMutableString *unformattedDateAsMutableString = [[unformattedDateAsString mutableCopy]autorelease];
//make sure the date stored in the metadata is not nil, and contains a meaningful date
if(unformattedDateAsMutableString && ![unformattedDateAsMutableString isEqualToString:@""] && ![unformattedDateAsMutableString isEqualToString:@"0000:00:00 00:00:00"])
{

    //the date (not the time) part of the string needs to contain dashes, not colons, for NSDate to read it correctly
    [unformattedDateAsMutableString replaceOccurrencesOfString:@":" withString:@"-" options:0 range:NSMakeRange(0, 10]; //the first 10 characters are the date part

    //the EXIF spec does not allow the time zone to be saved with the date,
    // so we must assume the camera’s clock is set to the same time zone as the computer’s.
    [unformattedDateAsMutableString appendString:@" +0000"];

    date = [NSDate dateWithString: unformattedDateAsMutableString];

}

And finally, don’t forget to release the metadata dictionary and the image source when you are done.

Setting Metadata

You may also want to add metadata to an image, for instance the program that created the image. Or, in the case of PhotoBook, I add the date a photo was uploaded to facebook in the metadata before adding it to iPhoto.

It is also really important that you preserve any metadata that has already been set for an image when you add new properties.

To start, you go through the same process to get the metadata. Create a CGImageSource using one of the methods above and get the metadata as a dictionary with CGImageSourceCopyPropertiesAtIndex

Now it gets a bit more complicated.First create a mutable copy of the metadata dictionary and make any changes you’d like (add properties, override properties, etc.) You can use all the same keys listed here (just make sure you cast them to a NSString* first).

Note that in order to be useful, all the values you set must be NSStrings (unless otherwise documented) and conform to the format of the spec

Next, you create a CGImageDestination with some path or data where you want the modified image to be written. Use CGImageDestinationAddImageFromSource(destination,source,0, (CFDictionaryRef) metadata); to add the image you are working with to the destination, and override the old metadata with the modified metadata. Finally, you call CGImageDestinationFinalize(destination); to either write to data or write to file (depending on what you initialized the destination with).

Instead of showing little code samples of all of these steps, I think it is better to give one big example that demonstrates everything.

This method takes in a NSDate and a path to an image file, and sets the date as the date digitized in the metadata. It then writes the modified image back to its original path. It also demonstrates how to convert a NSDate into a string that is the right EXIF format. Enjoy!

Available for download here.

- (BOOL)setDateDigitized:(NSDate *)date forPhotoWithURL:(NSURL *)URL;
{
    CGImageSourceRef source = CGImageSourceCreateWithURL( (CFURLRef) URL,NULL);
    if (!source)
    {
        NSLog(@"***Could not create image source ***");
        return NO;
    }
    
    //get all the metadata in the image
    NSDictionary *metadata = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);
    
    //make the metadata dictionary mutable so we can add properties to it
    NSMutableDictionary *metadataAsMutable = [[metadata mutableCopy]autorelease];
    [metadata release];
    
    NSMutableDictionary *EXIFDictionary = [[[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]autorelease];
    
    if(!EXIFDictionary)
    {
        //if the image does not have an EXIF dictionary (not all images do), then create one for us to use
        EXIFDictionary = [NSMutableDictionary dictionary];
    }
    
    
    //we need to format the date so it conforms to the EXIF spec and can be read by other apps

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
    [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
    [dateFormatter setDateFormat:@"yyyy:MM:dd HH:mm:ss"]; //the date format for EXIF dates as from http://www.abmt.unibas.ch/dokumente/ExIF.pdf
    NSString *EXIFFormattedCreatedDate = [dateFormatter stringFromDate:date]; //use the date formatter to get a string from the date we were passed in the EXIF format
    [dateFormatter release];
    
    [EXIFDictionary setObject:EXIFFormattedCreatedDate forKey:(NSString *)kCGImagePropertyExifDateTimeDigitized];
    
    
    //add our modified EXIF data back into the image’s metadata
    [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary];
    
    CFStringRef UTI = CGImageSourceGetType(source); //this is the type of image (e.g., public.jpeg)
    
    //this will be the data CGImageDestinationRef will write into
    NSMutableData *data = [NSMutableData data];
    
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)data,UTI,1,NULL);
    
    if(!destination)
    {
        NSLog(@"***Could not create image destination ***");
        return NO;
    }
    
    //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
    CGImageDestinationAddImageFromSource(destination,source,0, (CFDictionaryRef) metadataAsMutable);
    
    //tell the destination to write the image data and metadata into our data object.
    //It will return false if something goes wrong
    BOOL success = NO;
    success = CGImageDestinationFinalize(destination);
    
    if(!success)
    {
        NSLog(@"***Could not create data from image destination ***");
        return NO;
    }
    
    
    //now we have the data ready to go, so do whatever you want with it
    //here we just write it to disk at the same path we were passed
    [data writeToURL:URL atomically:YES];
    
    //cleanup
    CFRelease(destination);
    CFRelease(source);
    
    return YES;
}   

YABFR

Saturday, September 22nd, 2007

PhotoBook 1.0.2 is out!

Hurray! I hope you are excited as me!

Sorry for Yet Another Bug Fix Release, but I needed to get some things fixed, and there are a few new minor features in there.

One bug in particular bugged the hell out of me. The “back” button on your browser has been remotely disabled to ensure you will listen to this pointless story.
A very small number of users were either getting an Unknown error when logging in, or were getting stuck on loading screens. This small subset users just happened to include people reviewing apps for facebook, which is why PhotoBook is not currently in the Application Directory

It was truly a bug like Steven Frank writes about.
I, of course, could not reproduce it myself, but a patient user with the problem offered to help track it down (thanks!)

Basically, I make a request to facebook using their REST API (with their awesome pseudo-SQL language FQL) and it sends back the response in XML. I then parse this XML with NSXMLDocument.

The “unknown error” indicates the NSXMLDocument is nil, and somehow no error from the download, facebook, or the NSXMLDocument was returned.

So, going down the list, I first assumed the FQL query was bad (maybe some object used in the query was returning nil and facebook freaked out and returned no data). Nope, the query looks fine.

Next, I thought the problem might be on facebook’s side, and it was returning no data. Nope, it returns something that looks like XML.
So, I guess the XML might be invalid. I get a sample response from the user (as a NSString) and feed it into a NSXMLDocument. It is parsed fine.
Maybe there is something wrong with my download code? I stick the data hidden on a server, and try to download it with the same code. Works fine, and is parsed correctly.
Finally, through some trickery, I get facebook’s servers to return the same data. This data looks like the same, but is actually a few bytes longer. I feed it into NSXMLDocument, and yay! It returns nil, and it does not even set the NSError object I pass to it (even stranger).

So, finally, I set the option NSXMLDocumentTidyXML and everything works fine.

I still have no idea why (a)facebook is returning a few extra bytes or (b)NSXMLDocument returns nil without setting the error object I pass to it with anything. But whatever. I guess the lesson is never trust any data you do not have complete control over. Programming 101, but whatever.

I have had plenty of other problems with trusting data too much (FQL randomly not returning everything I ask from it, queries that are too long for GET and return 414-Request URI too long). It is still loads of fun.

I promise some interesting cocoa posts soon, and lots of cool features for PhotoBook 1.1.

Intro + Updates

Thursday, June 28th, 2007

Hello…
Well, I am Adam Leonard, the sole developer for Caffeinated Cocoa.

I live in the Los Angeles area, and will enter my senior year of high school in the fall.

As you probably have not guessed, this is my blog where I might post about Magrathea, my geotagging app, and Google Importer, a plugin for Spotlight to add Google results. I also want to blog a bit about programming with Cocoa, the library used in both of my apps.

I know there are so many cocoa blogs on the internets (wow, the OS X spell checker says that is a word) so I probably will not have much to contribute, but, eh. I will be happy if I reach 10 posts this year :)

Anyway, I finished school last week (yay!), so now I have much more time to work on coding.

I also just released Magrathea 0.5 . There is a ton of new stuff in this version, like Flickr uploads and a beautiful new icon by Fernando Lins. In my desperate attempt to join the Delicious Generation I have tried to clean up the UI and add superfluous effects and gradients. Leopard is going to be awesome for this.

0.5 will almost certainly be the last beta of Magrathea. After 0.5, I just want to fix bugs, add some polish, and maybe finish a couple of features if I can work around some incredibly annoying Yahoo bugs. I will try to get 1.0 out next week.

And no, I have not forgotten about Google Importer. I haven’t worked on it in a forever, but I want to change that. I am considering doing a complete rewrite of it and use Yahoo’s APIs so I can add a bunch of cool features.

Also, several people have asking about how I brutally hacked Spotlight to search Google, so I will write a post about that soon.