The Blog of Maxim Porges

Posts Tagged ‘Objective-C’

  • An Objective-C Tutorial for Enterprise Java Programmers

    As a programmer, the lingua franca for computers and I is mainly Java. And although I have no real work-related purpose for it, I’ve always had a hankering to learn Objective-C and OS X development.

    And so, over the years, I’ve acquired a handful of really nice books on Objective-C and OS X programming that generous relatives have bought for me as gifts from my Amazon wish list, including Aaron Hillegass’s Cocoa Programming for Mac OS X and Beginning Mac OS X Programming from Wiley. But after a handful of evening coding sessions, I’ve never been able to stick with it long enough to learn it, and every time I return to an old project I find that I’ve forgotten most of what I learned and have to start over at the early chapters again.

    Last night, I finally figured out why this is the case when I decided to give OS X development another try. All the books I have dive in and say: “Let’s build a program with a GUI!” And then I’m bouncing around between Xcode and Interface Builder, and I’m doing drag-and-drop UI building, and I’m hooking up selectors to class methods, and it’s totally alien from anything I’ve ever done before.

    The reason this is so alien to me is that I’m an enterprise Java programmer with years of experience writing web and desktop apps through code, with the IDE as a participant rather than the full-blown means. The last time I used a drag-and-drop editor to build a GUI was when I was forced to take a VB6 class in college; even in my programming salad days, I thought that a “program that builds a program” was way too far from the metal. I’ve got a solid foundation in ground-up MVC principles, and I never, ever start building an application GUI-first nor use drag-and-drop GUI builders.

    When building something in Java, what I do is code the core business logic of the app, write unit tests, and then hook the GUI in to it last once I know everything at the core is working perfectly. If I could just apply this pattern to development of an Objective-C project, I knew I would have a chance of actually retaining what I had learned. I know that the way things are done with OS X is to use Interface Builder and hook up the GUI to the classes visually (and that this is one of the things that makes it a rapid development environment), but for me it was just the wrong place to start.

    So, I decided to try building a test-only GUI-less app last night, and I had some success. I’d like to share what I did with other Java developers in case it can help them too. This won’t be a tutorial as much as a post with a bunch of links to other tutorials that helped me accomplish my goal along with notes about using Objective-C and Xcode that were helpful to somebody familiar with Eclipse. If you like, you can download the project that I put together for reference.

    Getting Started

    I’ll assume that if you are reading this, you (a) have a Mac and (b) have installed Xcode. If not, go and do these things now. Go ahead, I’ll wait.

    As I usually advise programmers learning JUnit to build the world’s simplest calculator application, I decided to apply this same principle to what I was doing. So that’s what I’ll be telling you about; riveting, I know…

    Making Xcode Livable

    Having worked in Eclipse so long, I’m used to having code completion on by default, all my editing happening in a single window, strong support for custom code formatting, and many other things that just seem like good plain common sense.

    Xcode in its default state appears to have none of this, and is really frustrating to use when you first dive in if you are familiar with Eclipse. Have no fear, though – these features are mostly there and just need to be turned on. before you do anything with Xcode, I suggest that you open the preference pane and set up the following options. Note that I am using Xcode 3.0 on Leopard so you may see the options under different headings depending on the version you are using.

    General Tab

    • “Layout”: set this to “All-In-One” to prevent tons of new windows from opening whenever you do anything at all.
    • Check “Open counterparts in same editor” to prevent Apple-Option-Up (used to switch between header and implementation files) from causing the files to open in a new window.
    • Check “Automatically open/close attached editor” to cause the in-window editor to be used when you select code files.
    • Check “Automatically clear log” to clear out the log between runs, a la Eclipse.

    Code Sense Tab
    For some reason, code sense is not on by default from what I could see, which just seems plain silly to me. The options for turning it on in this tab are self-explanatory .

    Debugging Tab
    Launching an app in debug mode doesn’t show the debug “perspective” in Xcode by default. I changed the “On Start” option to “Show Console & Debugger” to make this happen, since I am used to switching to debug mode in Eclipse when I launch an app to see what it’s doing. Press Apple-0 in Xcode to switch back to the project “perspective” after running your app.

    SCM
    Setting up a source control repo is totally painless, and Xcode has a built-in SCM browser that’s as nice as anything I’ve used elsewhere. I got it hooked up to my SVN repo running on my old G4 tower in about ten seconds using svn+ssh.

    Unit Testing in Objective-C

    In Java, I always use JUnit and its associated set of libraries (such as JMock). There’s a framework for Objective-C called OCUnit which is basically identical to JUnit in purpose and operation, and an associated mocking framework called OCMock. Apparently OCUnit is such a standard that Apple decided to include it in Xcode 3.x, so unless you are developing in Xcode 2.x or lower you don’t need to download and compile OCUnit yourself.

    To get a basic Xcode project set up with unit testing, I found this tutorial on the ADC site to be the easiest to follow. There are a few gotchas.

    1. If you are using an older version of Xcode 3.x like I am and the ADC site is being retarded and not letting you download a newer non-Snow Leopard version of Xcode, then the panels are not as nicely broken down as the screenshots in the tutorial. It’s very easy to accidentally pick the C++ Unit Test Bundle instead of the Cocoa Unit Test Bundle. If your project compiles fine but you get linking errors during your build, you picked the wrong framework. You’ll also be able to tell this by looking in the Info window for the Unit Tests target (select the target, right click, and select Get Info). Under the Build tab, if the Linking => Other Linker Flags section shows “-framework Carbon -framework SenTestingKit” instead of “-framework Cocoa -framework SenTestingKit”, you selected the wrong thing – you need the Cocoa version for a Cocoa app.
    2. The way that OCUnit works, it links in to your main application as a dependency during building. So, you’ll create a regular GUI app, create a unit testing target with a dependency on this app, and then when you build the unit testing target it fires up a unit test wrapper during the build and exercises your code; the build will simply fail if any tests fail. The nice thing about this is that your test errors appear as build errors in Xcode, which lets you see build error bubbles adjacent to the broken assertions (in other words, no custom OCUnit runner plugin is needed in Xcode like the JUnit plugin is needed in Eclipse). The key takeaway here is that you want to make sure that your Cocoa GUI app fires up in main.m (i.e. the default “return NSApplicationMain(argc, (const char **) argv);” code is present); if not, then the OCUnit hooks can’t work their magic.
    3. Each class implementation file (i.e. .m file) in an Xcode project can be built for one or more targets. You choose the target for your classes as you create them using the “New File…” wizard, but you can verify and/or change your build targets by right-clicking the .m file and selecting “Get Info” and looking in the “targets” tab. Like with JUnit, you don’t want your tests ending up in your main application, so make sure your app code is aimed at the main application target in your project, and your test code is pointed to your unit testing target.

    Besides this, OCUnit is very similar to JUnit with the convention for test method names to begin with “test…”, setUp() and tearDown() methods, and automatic introspection of the unit test class to determine the tests to run. Assertions are implemented as macros (which are kind of like Java’s static imports from what I can see).

    Writing Some Objective-C

    I’m told Objective-C is like SmallTalk, but having never learned SmallTalk I can’t tell you if this is true or not. In any event, it’s a piece of cake to learn for anybody who has a basic understanding of object-oriented development and C/C++. I’ve forgotten a lot of the C/C++ I learned in college, and haven’t refreshed on method overloading or language specifics yet since my primary goal last night was to get a unit testable project up and running, so forgive the basic-ness of the code presented below, but I wanted to share what I wrote so that people familiar with Java and JUnit can get the idea.

    I found a few useful tutorials on the language to get me going, in addition to referring to my books for sample code. Tristan O’Tierney has a nice language overview on his blog.

    Here’s the awful Objective-C code for my calculator app.

    Calculator.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    #import <Cocoa/Cocoa.h>
     
    @interface Calculator : NSObject
    {
     
    }
     
    - (int) addTwoNumbers: (int) first secondNumber:(int) second;
    - (float) addTwoDecimals: (float) first plus:(float) second;
     
    @end

    Calculator.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    #import "Calculator.h"
     
    @implementation Calculator
     
    - (int) addTwoNumbers: (int) first secondNumber:(int) second
    {
      return (first + second);
    }
     
    - (float) addTwoDecimals: (float) first plus:(float) second
    {
      return (first + second);
    }
     
    @end

    CalculatorTest.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    #import <SenTestingKit/SenTestingKit.h>
    #import "Calculator.h"
     
    @interface CalculatorTest : SenTestCase
    {
      // it's standard in Objective-C to declare class
      // members in the header file; this will be our fixture
      Calculator *calculator;
    }
     
    - (void) testAddTwoNumbers;
    - (void) testAddTwoDecimals;
     
    @end

    CalculatorTest.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    #import "CalculatorTest.h"
     
    @implementation CalculatorTest
     
    - (void) setUp
    {
      // I'm using Objective-C 2.0 which has built-in garbage collection
      // so I didn't use [[Calculator alloc] init] or use autorelease pools;
      // not sure if this is the right way to do this yet but I saw it in a
      // tutorial
      calculator = [Calculator new];
    }
     
    - (void) tearDown
    {
      // This resets the fixture reference for each test. Again, this
      // was recommended so I'm just towing the line
      calculator = nil;
    }
     
    - (void) testAddTwoNumbers
    {
      // I wanted to see where log output for tests goes; more on this later
      NSLog(@"Something in my test...");
      int result = [calculator addTwoNumbers:3 secondNumber:6];
     
      // This is how SenTest rolls for assertions... ugh. See my update
      // at the end of this blog post for a way to get JUnit-style
      // assertions with Hamcrest if you prefer them
      STAssertEquals(9, result, @"Expected 9, got %d", result);
    }
     
    - (void) testAddTwoDecimals
    {
      float result = [calculator addTwoDecimals:10.1 plus:1.2];
      STAssertEquals(11.3f, result, @"Expected 11.3, got %f", result);
    }
     
    @end

    Getting Output

    Any output from print statements in your main app code will show up in the console in Xcode, but output in the test classes themselves does not. However, everything you NSLog shows up in the OS X Console, so if you really need to print something in your unit test and see it come out as output, open up the Console application. There is a little lag since Console polls its logs. There might be a way to get the output of the unit test inside Xcode but I couldn’t find it.

    Next Steps

    Now that I have all that working, for my next trick I want to do something else that is familiar to me: talk to a web service. Apple has a few tutorials on their web service APIs as well as their tree-based XML parsing. I haven’t gone through these yet, but they look pretty thorough and have tutorials. They also provide a sample Java web service app (linked to in the web service document) which will echo back whatever is sent to it, so you can use this to test out your Cocoa calls to SOAP.

    I hope you find this useful. I’m looking forward to corrections/tips from anybody who really knows what they are doing with this stuff, so please add them in the comments.

    UPDATE 2010.02.06:The “STAssertEquals”-style syntax for assertions in SenTest is really annoying if you are used to JUnit-style assertXXX()-style assertions. I just found a blog post by the guys at CarbonFive where they describe how to set up Hamcrest matches for Obj-C. Check it out! The source for the Obj-C implementation of the Hamcrest project has an Xcode build that works flawlessly.

    2010.01.23 / 12 responses / Category: Uncategorized