My Links

iOS Developer | CV | linked in | stack overflow | twitter | rsaunders100 at gmail dot com

9 Mar 2014

Alternatives to -Werror

On large projects the warning count can pile up meaning that important warnings are missed in all the noise.  Many developers set the -Werror flag to treat all warning as errors forcing them to be fixed.  This is good pratice, however sometimes this is not practical, for example you might have a 3rd party library that contains hundreds of warnings.  This leaves 2 options (other than explicitly silencing warnings one at a time with #pragma clang diagnostic ignored), selectively downgrading errors to warnings or selectively upgrading warning to errors.

The clang docs state how to accomplish this.


  • Assuiming -Werror is set, -Wno-error=X downgrades the error back to a warning.  For example -Wno-error=#warnings would allow you to use the #waning pre-processor directive.
  • -Werror=X, upgrades an error into a warning.  For example -Werror=protocol will break the build if you forget to implement a required protocol method.

An easy way to find the name of an error is to check the build log (cmd-8) and expand on the warning.



If you want to selectively upgrade warnings, here are some flags to get you started.

-Werror=implicit-atomic-properties - properties are atomic by default, most of the time you want nonatimic.  This forces you to explicitly say you want atomic.
-Werror=protocol - unimplemented required methods.
-Werror=incomplete-implementation - A method in the interface, but no implementation in the .m
-Werror=undeclared-selector - Using @selector on something XCode dons't know about, perhaps you renamed the method.
-Werror=arc-retain-cycles - Usually capturing self strongly in a block.
-Werror=mismatched-parameter-types - The method declaration doesn't match the implementation.

-Werror=int-conversion- Pointer to integer conversions.
-Werror=shadow - Creating a local variable in a nested scope that overrides an existing variable with the same name.
-Werror=unknown-warning-option - Protects you from typos in these waring flags.


Plus you can to enable the two XCode assisted upgrades:


Here are some examples of the errors that would be enabled.




Product owners

Really great summary of the job of a product owner.  Well worth watching even from a development perspective. 


5 Mar 2014

How to choose the correct iOS communication pattern

Great diagram from the objc.io post.  The best bit of advice is when to use delegations over blocks.



17 Feb 2014

Date parsing

Parsing dates is simple using Apple's NSDateFormatter, but the class is often misused leading to bugs. Use these 2 rules to avoid errors.

  • When parsing a date from a server be as explicit as possible.
  • When parsing a date displaying to the user, rely on the user's settings.

Parsing Server Dates

Dates that come from the server usually take a constant predefined format.  The most widely accepted standard is ISO 8601 combined date and time E.g. 2014-02-11T10:22:46+00:00.  However, there are a variety of formats you might come across.  Many will not have the timezone encoded in them (i.e. '+00:00'). Instead they assuming that the client knows that the time is quoted in in UTC / GMT or perhaps even PST.

For example the HTTP spec specifies that dates in headers can be one of 3 formats, RFC 822, RFC 850 or ANSI C's asctime() format.  The spec states that all dates should be in GMT.

Last-Modified: Tue, 11 Feb 2014 10:22:48 GMT

Once you know the exact format for a data, create a date formatter using the unicode standard format string.  Be carful about 12/24 hour times, 'hh' is for 12 hour time, 'HH' is for 24 hour time.

This will parse a date conforming to the long form of ISO 8601 standard.   

[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"];

Just setting a carefully crafted date format isn't good enough, you can still get bugs depending on the user's settings.  You be as explicit with the calendar, locale and timezone properties too.  This is important as the values are set to the user's settings by default.  So the formatter might work for you but a user in another timezone may have different settings which will cause their app to be unable to parse server dates.  

This will use the western (gregorian) calendar, with GMT / UTC, using the US english locale:

[dateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];


Parsing dates to display to the user

On the other hand, when you are converting to a string to display to the user, you will probably want to display the date in the user's current locale, timezone and calendar.  This is done by default but its nice to be explicit for readability and incase the default values ever change.

[dateFormatter setCalendar:[NSCalendar currentCalendar]];
[dateFormatter setLocale:[NSLocale currentLocale]];
[dateFormatter setTimeZone:[NSTimeZone defaultTimeZone]];
Next, you need to define how the date is displayed.  You should avoid using setDateFormat.  Instead set the date and time style.  This will guarantee that the date will be displayed correctly in all locales.  Date formats are difficult to get right in all locales Japanese dates read " 平成20年12月31日", or more subtle variations like British and American dates have the month and day order reversed.  Apple engineers have gone to the trouble of defining short medium and long and very long date formats in every locale, all you need to do is decide how much space in the UI you want to take up.

[dateFormatter setDateStyle:NSDateFormatterShortStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];

NSDateFormatterNoStyle can be used if you only want to display the time not the date or vice versa.

Re-using formatters

You should always reuse formatters as much as possible to avoid performance issues.  This is especially important when parsing a long JSON / XML response containing many dates.  Not reusing formatters will cause an overhead each time a date is parsed adding up to a noticeable delay for the user.  

Re-using dates can be done in a few different ways. An example class vending a server and a user date formatter using a singleton style re-use can be seen in this gist



14 Feb 2014

How to test notifications using Kiwi

Kiwi is a testing framework for iOS. Here is how you would test a notification.

// Stub out postNotification so that it dosn't actually do anything.  
// This help prevent failures in unexpected areas of the app.
[NSNotification stub:@selector(postNotificationName:object:userInfo:)];

// Setup a spy to capture the notification name argument.
// You could also do the same with the object and userInfo arguments.
KWCaptureSpy *notificaitonNameSpy = [NSNotification captureArgument:@selector(postNotificationName:object:userInfo:) atIndex:0];

// Trigger the notificaiton
// ...

// Ensure your code has triggered the correct notification
[[notificaitonNameSpy.argument should] equal:@"expectedNotifcaitonName"];

27 Jan 2014

Does your app behave unexpectedly about 1/8 times?

Whats wrong with this bit of code?

- (BOOL) doesThingExist
{
    MyClass * thing;

    thing = // .... compute thing - could be nil

    return thing;
}

Assuming the thing is never nil then this method will actually return NO 1 in 8 times.  This is because the method declares its return type as a BOOL, (a.k.a a unsigned char - one byte - 0x00 to 0xFF). MyClass is a pointer which is either 4 or 8 bytes depending on the architecture.  The method truncates thing into a BOOL which drops everything but the last 1 byte.  Pointers will always have the last 4 bits zero, e.g. 0xa27e4420 however, one in 8 times all of the last 8 bits will all be zero - e.g. 0xa27e4400. When this happens 0 (a.k.a NO) will be returned.

The fix is simple:
return thing != nil;
To catch these errors, enable pointer to integer warnings or simply modernise your project and it will be done under the hood.  For new projects the, the warning will be enabled by default.



I would also suggest turning on -Werror (treat warnings as errors) or see my post on alternatives to -Werror.  Also the 64 bit transition guide is well worth reading.

15 Jan 2014

Preventing unit tests from crashing unexpectedly



If you have strange crashes (e.g. bad access) in your unit tests in seemingly unrelated to the area under test you might have run into an issue where your normal application notifications are interfering with your tests.  This usual manifests due to a combination of NSNoticfications and asynchronous tests.

To fix use this gist.

static BOOL isRunningTests(void) __attribute__((const));

 - (BOOL)application:(UIApplication *)application   
         didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 {
     if (isRunningTests()) {
         return YES;
     }

     //
     // Normal logic goes here
     //

     return YES;
 }

 static BOOL isRunningTests(void)
 {
     NSDictionary* environment = [[NSProcessInfo processInfo] environment];
     NSString* injectBundle = environment[@"XCInjectBundle"];
     NSString* pathExtension = [injectBundle pathExtension];

     return ([pathExtension isEqualToString:@"octest"] ||
             [pathExtension isEqualToString:@"xctest"]);
 }

If its running the tests, it will exit early from the app delegate therefore it will not continue with the normal application logic.  This assumes that you are creating your main window from the application:didFinishLaunchingWithOptions: callback.  If using storyboards the initial view controller is not created in this way so you will have to find another solution.

The code came from an excellent post on objc.io.  I modified it slightly to support XCode5's newer XCTest framework.




4 Dec 2013

Testing objective c code with asynchronous callbacks


Lets say you have some code that has an asynchronous block based callback which may or may not be on the main thread, e.g.

+ (void) asyncCallback:(dispatch_block_t)block
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        block();
    });
}

Writing a unit test for this is tricky since the tests may well exit before the block is invoked.   There are a number of approaches as detailed in an objc.io article.  My solution is a few extra lines of code but doesn't require any external libraries or special naming conventions.

- (void) testAsyncExample
{
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block BOOL hasRunBlock = NO;
    
    [MyClass asyncCallback:^{
        
        hasRunBlock = YES;
        dispatch_semaphore_signal(semaphore);
    }];
    
    double timeoutDelayInSeconds = 1.0;
    dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW,
                                            (int64_t)(timeoutDelayInSeconds * NSEC_PER_SEC));
    long result = dispatch_semaphore_wait(semaphore, timeout);
    XCTAssertTrue(result == 0, @"Timed out waiting for semaphore");
    
    XCTAssertTrue(hasRunBlock, @"");
}

It waits for a semaphore that is signalled from the callback.  It has a 1 second timeout incase the block is never invoked.  Its available as a gist on github.


9 Nov 2013

Why is git called git?

git was created by Linus Torvalds.  Linus was the principal force behind the development of the Linux and later developed the git version control system.

When asked about the name he was reported to have said:

"I'm an egotistical bastard, so I name all my projects after myself. First Linux, now git."

Brilliant :)