Blog

Welcome to my blog.

Always set NSError pointers to nil, Duh!

This is one of those errors that just make you go, duh!

Can you spot the problem with the following two snippets of code?

Snippet 1

    NSError *error;

    if (![someObject performFetch:&error])

        NSLog(@"%@: %@", [error description], [error userInfo]);


Snippet 2


    NSError *error;

    if (![self.someObject performFetch:&error])

        NSLog(@"%@: %@", [error description], [error userInfo]);


At first this does not appear to be a problem, but what if someObject is nil in the first case or the accessor self.someObject returns nil in the second?  If either is nil then the message is sent to the nil object which will cause the conditional to fire (a message sent to the nil object returns 0 or nil). Now when error is used it has an uninitialized value, which will probably cause things to blow up, but now always.

The solution to initialize error to nil,

    NSError *error = nil;

    if (![self.someObject performFetch:&error])

        NSLog(@"%@: %@", [error description], [error userInfo]);


Or in some cases add a check for the NSError also,

    NSError *error = nil;

    if (![self.someObject performFetch:&error])

        if (error)

            NSLog(@"%@: %@", [error description], [error userInfo]);


Always do one of these as the above mistake is simple to make.


No Love in the App Store

One of the problems, the main problem. of Apple's App Store is hierarchy.

What? What does structure have to do with it. I am not talking about structure as in a folder hierarchy, but more to do with the pecking order. I have to explore this idea a little before I can tie it to the App Store.

I am an Indie developer. I don't do this for no other reason but that I love crafting software. I am in someways like that stereotypical computer geek. I dislike hierarchy. The hierarchy in the pecking order. I want to do things for the heck of it, because I love to do them, to explore, to learn. In some ways this is antiauthoritarian. It is the way of the creative.

Becoming part of the pecking order starts to define who you are. It becomes a status thing. It defines your position in the company. You start making decisions with regard to the hierarchy. You might not even realize this, but how many decisions are made because of your place in the company, of your place in the hierarchy. Think about it.

Now what does this do to your creativity in creating great software. How constrained are we to create software the way it should be? It is easier to create great software when the hierarchy is small than when it is large. The hierarchy limits creativity. It is in fact even antagonistic to it.

This is why some developers start going indie. They want to have the control, the freedom, to create software the way it should be created. To make the decisions for good or for bad. Not to be limited by hierarchy. In hierarchy, the love of crafting software is stifled. Crushed.

Hierarchy does have its place though. I am not going to mention the positives of it. I am more interested here in what it does to the creative process.

Now what does this have to do with the App Store? It is simple. The App store has a hierarchy and people make decisions based of this hierarchy.

People have been condescending to the hierarchy of the App Store. What app can I make that will get me in the top 100, the top 10. Yes, money can be made this way. Lots of it. But look at all the crap apps we have.

I hate it. I started making decisions based on it. I created some apps with the app market place in mind. I fell into the trap of the hierarchy. I condescended to the market place. The apps were, kind a, well OK. But I didn't like them. One remains half finished. The other is unreleased.

This is why I dislike the App Store. I don't want the decisions to come from the hierarchy, I want them to come from the freedom I have to make them myself. I get this sense from the Mac market, as each app is treated mostly on its merits as it is sold individually by each company (yes, there is a hierarchy here tool, but it is small). I don't get this sense from the App Store market.

I love developing for the Mac. The iPhone OS not as much, until I realized this. Now my focus has changed and I am loving the iPhone OS development also.

All this has to be kept in the context of a professional Indie developer. This is a lopsided view of the hierarchy and how it affects crafting software.

A Simple Way to Animate a UIBarButtonItem

My first pass solution to animate a UIBarButtonItem was to initialize it's custom view with a UIImageView. The UIImageView can then be set up for animation with multiple images. Calling startAnimaiton/stopAnimation on the UIImaveView will then animate. This is great for animating buttons in tool and tab bars.

One problem is that the when initializing the UIImageView into the UIBarButtonItem, the UIBarButtonItem does not respond to touch events. Yuk.

A simple solution is to initialize the UIBarButtonItem custom view with a UIButton, then touch events for the UIBarButtonItem item are handle correctly.

But the UIButton does not have a way to use a UIImageView only UIImages. We want to use the UIImageView for its simple animation.

The solution is to add the UIImageView as a subview of the UIButton.

Here is the code.

NSArray *images = [NSArray arrayWithObjects:

        [UIImage imageNamed:@"image0.png"],

        [UIImage imageNamed:@"image1.png"],

        nil];

imageView = [[UIImageView allocinitWithImage:[UIImage  imageNamed:@"image0.png"]];

imageView.animationImages = images;


UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

button.bounds = self.imageView.bounds;

[button addSubview:self.imageView];

[button addTarget:self action:@selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];

UIBarButtonItem *barButton = [[[UIBarButtonItem alloc] initWithCustomView: button] autorelease];



Some things to notice:

The UIButton is of zero area as it does not have its bounds set upon initialization, thus the bounds are initialized with the bounds of the UIImageView (which has its bounds initialized from the image).


The UIButton handles the action/target for the touch event. The UIBarButtonItem's action/target are not set.



To animate:

    [imageView startAnimating];


Have fun,
Sean

How to Debug iPhone Unit Tests

[Updated the troubleshooting section 10/4/2009]

The unit tests are finally set up for the iPhone. You can start doing some Test Driven Development, but one is failing. I've read Apple's documentation, but how do I debug the blasted thing?

NSLog messages can be scattered throughout the code. Their output is sent to the console, but this is a pain. There must be a better way.

This is one area that needs to be improved in Xcode. There is not even documentation on how to debug unit tests.

Here's how to do it.

The following assumes that you have set up the unit tests using the templates in Xcode and follows along with Apple's example in their iPhone Development Guide. If you have not already set up your units test then the iPhone Development Guide is a good place to start. I am also using Xcode 3.2.

Three steps that need to be performed.

    1. Setup a target that contains the unit tests, but does not run them.

    2. Setup the otest executable to run the tests.

    3. Setup the otest environment so that otest can find your unit tests.

Setting up the Target

When the target is set to run unit tests, Xcode runs a script that runs the unit tests. We need a similar target, but one that does not run the script.

The easiest thing to do is duplicate the existing target (in Apple's examples LogicTests). Right click (or control left click) on the LogicTests target and select Duplicate from the context menu. This will create a new target with a name like LogicTest copy. Now in the LogicTests copy target, delete the Run Script build phase (select the Run Script item and press the delete key).

The target can also be setup as if creating a new unit test target. Just set it up and delete the Run Scripts build phase. I find it easier to copy the existing Unit Tests. This makes it easier to duplicate any changed build settings and files/frameworks that were added. If I am feeling good I then change the LogicTests copy to LogicTest Debug wherever I find it.

Important: This step will create a duplicate Product that will have to be renamed. For example, after the above duplication, there will be a two LogicTests.octest. Rename the second one to LogicTests coy.octest in the Product group in the project window.

Now to setup otest.

Setting up otest

Xcode places the unit tests in a bundle then uses otest to run them. What needs to be done is to create a Custom Executable that will run otest with our LogicTests copy bundle. The tricky part is finding the correct otest.

If you go to /Developer/Tools, you will find that there is an otest there. This is the wrong otest. This is one is for the Mac. We need one for the iPhone simulator. 

Execute the following command in a Terminal window to find the correct otest.

    find /Developer -name otest

This will hopefully give you a list of otest found. For example, in my development environment the correct otest is:

/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.sdk/Developer/usr/bin/otest

What a mess. Not as nice as /Developer/Tools/otest

Now a custom executable needs to be set up.  In your projects project window select Executables and bring up the context menu. Select add then New Custom Executable... I put otest as the executable name, but most importantly place the path to the above found otest. Again in my environment:

/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.sdk/Developer/usr/bin/otest

Press Finish and a panel will pop up. We need to configure this panel so that otest will use our unit test bundle.

Setting up the otest Environment

The environment needs to be set up so that otest can find our unit test. In the otest info panel, select the arguments tab. We need to place the name of our unit test bundle as an argument to otest. In our example, "LogicTests copy.octest" (quotes are needed if there are spaces in the name).  Go ahead and click the little plus sign and add this to the arguments list.

You can do man otest in a Terminal window to find out more on otest.

Now we need to set up a mess of environment variables. I have not checked if this is the minimum set, but it works. Add the following to the environment variables list.

DYLD_LIBRARY_PATH

${BUILD_PRODUCTS_DIR}:${DYLD_LIBRARY_PATH}

DYLD_FRAMEWORK_PATH

"${BUILD_PRODUCTS_DIR}:${DEVELOPER_LIBRARY_DIR}/Frameworks:${DYLD_FRAMEWORK_PATH}"

DYLD_NEW_LOCAL_SHARED_REGIONS

YES

CFFIXED_USER_HOME

"${HOME}/Library/Application Support/iPhone Simulator/User"

IPHONE_SIMULATOR_ROOT

$SDKROOT

DYLD_NO_FIX_PREBINDING

YES

DYLD_ROOT_PATH

 /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.sdk

Double check these values and make sure they are correct for your environment.

Running/Debugging the Unit Test

Now you can run the unit test. Select the LogicTests copy as the active target. Select the otest executable as the active executable. Now you should be able to run, debug, and set breakpoints like any other normal executable.

Don't forget if you add any files to the unit tests, add them to both targets, i.e. LogicTests and LogicTests copy.

Happy unit testing and TDD.

Troubleshooting

If you get errors like, 

SenTestingKit/SenTestingKit.h: No such file or directory
cannot find interface declaration for 'SenTestCase', superclass of 'LogicTests'

this is due to Xcode putting extra \'s before quotes in Framework Search Path build setting. Remove any backslashes in this setting. Select the LogicTest copy item and bring up its info panel (I). Select the build tab and change the Framework Search Path for all configurations.


If you get an error like,

objc[16428]: '/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.sdk/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation' was not compiled with -fobjc-gc or -fobjc-gc-only, but the application requires GC

objc[16428]: '/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation' was not compiled with -fobjc-gc or -fobjc-gc-only, but the application requires GC

objc[16428]: *** GC capability of application and some libraries did not match


the wrong otest was selected. Make sure the path to the correct iPone simulator is selected in the General tab of the otest info panel.




If you get an error like,

dyld: Symbol not found: _SCDynamicStoreCreate
Referenced from: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices
Expected in: /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.1.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
in /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices

Harald reported below in the comments that setting the DYLD_FORCE_FLAT_NAMESPACE environment variable solved this (see man dyld). Add it to the list of the other environment variables set above.



If the source are not compiling using the new target, LogicTests copy, you probably forgot to rename the Product. See the important step above in the Setting up the Target above.

Hello World

What else should a geeky programmer's first post be?