2011-05-31

eXtreme Programming: You ain't gonna need it (YAGNI)

Although iOszkar 1.0 has already been released and the update 1.1 is also on the way the app is far from being complete. The reason why I'm satisfied, though, is that the basic functionalities are already implemented. The backlog is quite large, but I didn't know (and still don't know) how many users will download and use it and therefore I wanted to limit the initial effort.
I came up with the idea that I'll let the upcoming iterations be controlled by the users so that they can vote on and thus prioritize the backlog.
I want to achieve the following with this approach:

  • Only implement features delivering value to the users
  • Avoid implementing unnecessary features confusing the users, putting constraints on and letting grow the code base
  • Save time for improving the existing code base and increasing the amount of tests
  • Minimize the risk of putting enormous effort into an app which eventually won't be used.
As you can see these are simple rational considerations and therefore I'm glad that they not only can be applied to several similar projects, but they have similarly been expressed in eXtreme Programming and also combined as a principle and acronym: YAGNI - you ain't gonna need it.
The basic idea is comparable to what's present in Test-Driven Development, too:
TDD: Create a failing test, but only implement code which is absolutely necessary to make it pass
YAGNI: Create acceptance tests, but only implement features which make them pass.
Both are applications of the KISS (my favorite interpretation is Keep It Simple and Straightforward) principle, so follow them!

2011-05-28

My first iPhone app: iOszkar

Pretty exactly three months after I came up with the idea and started learning Objective-C, Cocoa, the SDK and so on, my first iPhone app namely iOszkar has been released in the App Store.
It's an iPhone front-end to the excellent Hungarian ride sharing marketplace Oszkar.
I'm proud that the app has been accepted on the first try and I'm also satisfied with the performance of Apple: They managed to process the app within one workweek!




2011-05-26

Understanding EXC_BAD_ACCESS, EXC_BAD_INSTRUCTION/SIGABRT, and zombies

Being primarily a Java developer I was spoilt by the garbage collector of the JVM and therefore I had to face memory management issues when I started learning Objective-C for the iPhone, because that runtime doesn't have a garbage collector.

When an app crashes in most cases access to already released objects is the cause and there are two symptoms which refer to this fact, so let's see who they are and when they occur based on an example related to RestKit.

EXC_BAD_ACCESS
// Here creation and initialization of the params dictionary, etc.
...
SearchDelegate *delegate = [[SearchDelegate alloc] init];
[delegate release];
[[RKObjectManager sharedManager]
    loadObjectsAtResourcePath:@"/search/"
    queryParams:params objectClass:[SearchResult class]
    delegate:delegate];
What's happening here is:
  1. Search request parameters will be prepared
  2. A delegate will be allocated and initialized
  3. The delegate will be released
  4. Some network operation will be done
  5. Last the delegate will be called
The last step fails with EXC_BAD_ACCESS, because the desired object has been released, its assigned memory freed, but not allocated for another object.

EXC_BAD_INSTRUCTION/SIGABRT
SearchDelegate *delegate = [[SearchDelegate alloc] init];
[delegate release];
// Here creation and initialization of the params dictionary, etc.
...
[[RKObjectManager sharedManager]
    loadObjectsAtResourcePath:@"/search/"
    queryParams:params objectClass:[SearchResult class]
    delegate:delegate];
What's happening here is:
  1. A delegate will be allocated and initialized
  2. The delegate will be released
  3. Search request parameters will be prepared
  4. Some network operation will be done
  5. Last the delegate will be called

The last step fails with EXC_BAD_INSTRUCTION/SIGABRT, because the desired object has been released, its assigned memory freed, and also allocated for another object - this is why an exception with a message similar to the following will be thrown:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType objectLoader:didLoadObjects:]: unrecognized selector sent to instance 0x5967460'
We definitely didn't have to do anything with objects of type __NSCFType (it could be __NSCFSet, __NSArrayM, etc), so this is a precise indicator of an object stored on an unwanted memory area.

Ways leading to these errors
  • Over-releasing an object
  • Releasing an object not being an owner of
  • Not retaining an autoreleased object which will be released in the meantime via the autorelease pool
  • Prematurely releasing an object
  • Directly assigning an object to an instance variable instead of the accessor

Finding guilty objects
The exceptions and the subsequent crashes can't be tracked back, because the freed i.e. non-existing object has no information available. The easiest method is to enable the NSZombieEnabled environment variable in the Run phase of the product scheme accessible via the Product | Edit Scheme… menu entry in Xcode:


As a result deallocated memory areas will be allocated by special objects which will be observed by the runtime - these are the so called zombies. Sending a message to a zombie object results in a message similar to the following in the Xcode Console:
-[SearchDelegate respondsToSelector:]: message sent to deallocated instance 0x59be5a0
Furthermore, we can see the following in the Variables View in Xcode:
_delegate = (_NSZombie_SearchDelegate *)0x59be5a0 
Now, this is enough information to solve the issue.

As an alternative the Zombies instrument of Instruments can be used to track down the failure:

SyntaxHighlighter Objective-C brush

I just wanted to write my first post on Objective-C/Cocoa topic when I noticed that SyntaxHighlighter doesn't support this language out of the box. Despite thorough crawling I didn't find anything useful, thus I decided to write my own brush.
It works with SyntaxHighlighter 3.0.83, is available on GitHub, and its brush name is oc.

2011-05-24

UI Automation performance and the Page Object pattern

In one of my previous posts I provided an example which turned out to be of disadvantage:

function LoginView() {
    this.loginCells = window.tableViews()[0].cells();
    this.loginField = this.loginCells["loginFieldCell"].textFields()["loginField"];
    this.passwordField= this.loginCells["passwordFieldCell"].secureTextFields()["passwordField"];
    this.loginButton = this.loginCells["loginButtonCell"].buttons()["loginButton"];
}

Caching element paths in the constructor takes too much time, if:
  • Element variables refer to already cached subelements
  • The view currently isn't displayed.
This issue can be resolved as follows:

function LoginView() {
}

LoginView.prototype.login = function(user, password) {
    UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].cells()["loginFieldCell"].textFields()["loginField"].setValue(user);
    UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].cells()["passwordFieldCell"].secureTextFields()["passwordField"].setValue(password);
    UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].cells()["loginButtonCell"].buttons()["loginButton"].tap();
}

Via this approach I don't put a runtime dependency on the creation of the LoginView object, so I can reuse anytime and anywhere in my test.
Although I use direct access in the UI Automation element tree due to the application of the Page Object pattern it doesn't disturb me, nor did I have to change anything in the test cases.

2011-05-23

UIAKeyboard keys and buttons

I've just discovered that the UI Automation keyboard doesn't only consist of keys, but also of buttons: All elements with light gray background are keys and all those with dark gray background are buttons.

I could write for example:

app.keyboard().keys()["e"].tap();
app.keyboard().buttons()["return"].tap();















But the distinction is only an option, because the elements are also accessible via the elements() method:

app.keyboard().elements()["e"].tap();
app.keyboard().elements()["return"].tap();

2011-05-22

iPhone UI Automation and the Page Object pattern

At the time when I was actively contributing to Selenium I used it only in conjunction with FitNesse and could therefore not directly apply the Page Object pattern. However, one of the key principles was for me the abstraction of the UI elements by creating UI maps which contain simple mappings of logical names to XPath path expressions.

The Page Object pattern defines the same abstraction: Define and use UI elements only in one module, and provide only service functions to the test cases.
Instead of diving deeper into theoretical details here is an example:

#import "Common.js"

function LoginView() {
    this.loginCells = window.tableViews()[0].cells();
    this.loginField = this.loginCells["loginFieldCell"].textFields()["loginField"];
    this.passwordField = this.loginCells["passwordFieldCell"].secureTextFields()["passwordField"];
    this.loginButton = this.loginCells["loginButtonCell"].buttons()["loginButton"];

}

LoginView.prototype.login = function(user, password) {
    this.loginField.setValue(user);
    this.passwordField.setValue(password);
    this.loginButton.tap();
}

LoginView.prototype.waitForDisappear = function() {
    this.loginField.waitForInvalid();
}
So a test looks like as follows:

#import "../TestControllers/LoginView.js"
#import "../TestControllers/UserDetailView.js"

var loginView = new LoginView();
var userDetailView;

test("Valid login leads to user detail page", function(t, a) {
    loginView.login("andras_hatvani","password");
    loginView.waitForDisappear();
    userDetailView = new UserDetailView();
    userDetailView.assertUserData("andras_hatvani", "Andras Hatvani");
},options)};

Although I use tuneup_js as test framework in this example, it could be Jasmine or a custom framework.
Important is that I could formulate the tests in a business-like language i.e. in an own DSL, thus it is not only straightforward to write, but also to read. Furthermore, if the UI changes, then I only need to adapt one single unit, namely LoginView.js to make my tests run again.

2011-05-18

Becoming a blogger

Like most human beings I also have an opinion, questions or answers. But so far communicating these entities was limited to forums, mailing lists, and comments to blog posts.

Now I think the time has come to be proactive and write down my thoughts to make other people react. I have no plans where this way will lead to, but I will definitely share my ideas and concepts on the infinite topic of software engineering.