iOS9 ATS and weakly signed server certificates

Much as been written about iOS9 new network security layer, ATS (Application Transport Security) and how to relax security when the backend servers don’t support TLSv1.2.

If you aren’t in the loop regarding this you might want to check the following:

Apple’s tech note on ATS
useyourloaf on ATS
Ste.vn on configuring ATS
Timekl on ATS

Typically what happens to most users is that their server’s don’t have TLSv1.2 enabled and iOS9 network layer drops the connection under the suspicion that it might not be secured. The console shows something like this:

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824 CFNetwork SSLHandshake failed (-9824)

The case that I am going to described is a little bit different: what if you have TLSv1.2 enabled on your servers but still can’t connect?

Well, time to do some more investigation. The first place to go is obviously Apple’s tech note on ATS. Those of you who read it before might have noticed that there are two more criteria for dropping connections:

Connection ciphers are limited to those that provide forward secrecy (see the list of ciphers below.)

Certificates must be signed using a SHA256 or better signature hash algorithm, with either a 2048 bit or greater RSA key or a 256 bit or greater Elliptic-Curve (ECC) key.

So, you should get to your openssl toolkit to find out the reasons.

One great high level way of checking your server’s security is to use SSLHopper site to check SSL on you server. It’s just a matter of entering you server’s address on the text box, hit a button and let it run it’s suite of tests.

My results were the following:

Screen_Shot_2015-09-21_at_10_38_40

This tells us that the certificates are signed with an old and weaker encryption algorithm and that ATS is not liking that. In fact, Wikipedia’s SHA entry tells us that in terms of security performance SHA1 is under the minimum required SHA256. This is a hint for the problem.

If you are paranoid like me you’ll want to be rock-solid sure about this. You can use openssl tools to dig deeper. Get a hold of your server certificates and use the following command:

openssl x509 -in myServerCertificate.cert -text -noout

On the output look for:

Signature Algorithm: sha1WithRSAEncryption

This confirms that myServerCertificate.cert is signed with a weak encryption, SHA1 in this case, and that it won’t be accepted by iOS9 new ATS layer.

(If you are not so solid on secure certificates check this resource.)

Also, if you need to check if your server is working with the correct TLS version (or shockingly find that it’s still on SSL) you can use the following shell command:

openssl s_client -showcerts -connect your.host.com:443

Dig trough the output and look to:

SSL-Session:
Protocol : TLSv1.2
Cipher : ...

There you can find information on the secure session protocol in use. If you don’t care about the rest you just grep the result with openssl s_client -showcerts -connect your.host.com:443 | grep Protocol.

Now the funny part: how to solve this.πŸ™‚

As you might suspect (especially if you read the links I suggested at the top of the post) it might have something to do with changing something on Info.plist. You’re right!

Edit your .plists for all the relevant targets (i.e. the targets that use servers with weakly signed certificates) and add the following entry:

Screen Shot 2015-09-21 at 11.09.57

If you check Apple’s tech note on ATS you can understand why this works: the NSExceptionRequiresForwardSecrecy key tells ATS to accept weaker ciphers (check that SHA1 is there). I am using the NSThirdPartyExceptionRequiresForwardSecrecy because my servers are managed by an hoster so I have limited control over the certificates there. I am also using NSIncludesSubdomains so that this change takes effect on nodes under my top-level domain.

So, hopefully if you have an hoster that controls your weakly signed certificates you can still be able to work things out.

A bunch of other useful resources:

iOS 9 change set
List of trusted certificates in iOS9
List of trusted certificates in iOS8
HTTPS Server trust evaluation (useful to check the NSError codes for networking)
Browsers compatible with SHA256

Calendar event management tutorial

Disclaimer:throughout this post I will refer to Calendar. That’s the native iOS Calendar App, not NSCalendar.

Catching up the things I had in store.πŸ™‚

Let’s get to business. This will be a long one.

A couple of weeks a go I had to implement a feature on an app to add an event to the iOS calendar. I had never done it before so I had to investigate. I was glad that Apple had once again carefully thought things up so it was almost painless to do it which was indeed a good thing since I was racing against a deadline.

Before diving deep in the details and the code (yes, there’s code – I made a small example project) we need to know the basics.

Fist thing we need to know is that to mess with events and stuff like that we will be using EventKit. As such, we’ll need to

#import <EventKit/EventKit.h>

Second thing to know is that we’ll be performing the event addition and removal operations on a special object, EKEventStore, which is really a kind of proxy to the Calendar.

Third thing to know is that we will be managing EKEvent objects. Easy one.πŸ™‚

Last thing to know: the idiom. Yes, there’s an idiom to use the EKEventStore. And it seems daunting. But it’s not.

Here it is:

 EKEventStore *eventStore = [[EKEventStore alloc] init];
    
    if ([eventStore respondsToSelector:@selector(requestAccessToEntityType:completion:)])
    {
        [eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
            // Do stuff...
        }];
    }

Here’s the idiom broken to pieces:

  1. First you instantiate an EKEventStore object since you need it to communicate with the Calendar.
  2. Then you check if it responds to a particular selector. Don’t worry too much with this. This is simply to ensure backwards compatibility with iOS 5 and below since the method you’ll be calling next was introduced on iOS 6. (Side note: in iOS6 Apple’s changed the way you interact with Calendar and requires the app to ask permission to access the Calendar.)
  3. Then, if you’re running on iOS6+, you call the method that will ask for permission. This is an asynchronous call and your app will not block. Once the user answers the permission request your completion block will be executed.
  4. At last you can preform your add/remove event operation and the usual boring permission and error handling bullshit boilerplate.

So that’s pretty much what you need. An import, an EKEventStore instance and a two-line idiom. We are done here. I will go to sleep now. Have a nice evening.πŸ™‚

ymm_conscience

Ok, I’ve had my funny moment. Back to work.πŸ™‚

To demonstrate this I’ve set a simple demo project. There’s a UIViewController that adds an event to the Calendar when you tap a button. Oh, there’s also a button to remove the event and another one to expedite the Calendar opening.

Here’s what it looks like running on the iPhone simulator:

iOS Simulator Screen shot 29 May 2014 23.57.14

(The demo project will have a white background. I just changed it to black to have some contrast with the blog’s background).

In the StoryBoard we shall set the UIViewController as shown below. Take note that we’ll need two IBOutlets to the add and remove buttons and one IBAction for each of the three buttons.

Screen Shot 2014-05-30 at 00.10.05

On the PPViewController.m we’ll have a private category with the IBOutlets and couple more properties.

@interface PPViewController ()

@property (weak, nonatomic) IBOutlet UIButton *addEventButton;
@property (weak, nonatomic) IBOutlet UIButton *removeEventButton;

// Event related stuff
@property (strong, nonatomic) NSDate *eventStartDate;
@property (strong, nonatomic, readonly) NSDate *eventEndDate;
@property (copy, nonatomic) NSString *eventTitle;

// UI stuff
@property (strong, nonatomic) UIAlertView *alertView;

@end

We have properties for the event related stuff such as start and end dates and the title and also an UIAlertView which we’ll use to give feedback to the user. I chose to keep the event related stuff in properties to make it simple and to better illustrate some concepts below.

On the viewDidLoad we’ll initialize the stuff:


- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    
    self.addEventButton.enabled = YES;
    self.removeEventButton.enabled = NO;
    
    self.eventTitle = @"#pragmaPilot's outstanding event!";
    
    self.alertView = [[UIAlertView alloc] initWithTitle:nil message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
}

We start with the remove button disabled. When we add the event we will enable it and disable the add event button.

(Yep, it’s a pretty dumb app: it’s just able to manage the same event. But we like it that way. After all we’re a simple kind of men).

Our event end date will be a calculated property and will have a value exactly one hour after the start date.

- (NSDate*)eventEndDate
{
    return [NSDate dateWithTimeInterval:60*60 sinceDate:self.eventStartDate]; // ends in one hour
}

Our add button tap-event handler will use “the idiom” and will:

  1. Do all the permission & error handling bullshit boilerplate
  2. Create a new EKEvent starting NOW
  3. Check if such event already exists in the Calendar (I know we wired up our app to disallow such mishap but we like to do it nice)
  4. Add the event to the Calendar if it doesn’t exist yet

Boring Complete feedback is given to the user on all the noteworthy points.

Here’s the add event method gibberish code:

- (IBAction)addEventButtonTapped:(id)sender
{
    EKEventStore *eventStore = [[EKEventStore alloc] init];
    
    if ([eventStore respondsToSelector:@selector(requestAccessToEntityType:completion:)])
    {
        [eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
            // We need to run the main thread to issue alerts
            dispatch_async(dispatch_get_main_queue(), ^{
                if (error)
                {
                    self.alertView.message = @"Could not access the calendar because an error ocurred.";
                    [self.alertView show];
                }
                else if (!granted)
                {
                    self.alertView.message = @"Could not access the calendar because permission was not granted.";
                    [self.alertView show];
                }
                else
                {
                    self.eventStartDate = [NSDate date];
                    
                    EKEvent *event = [EKEvent eventWithEventStore:eventStore];
                    event.title = self.eventTitle;
                    event.startDate = self.eventStartDate;
                    event.endDate = self.eventEndDate;
                    
                    NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:event.startDate endDate:event.endDate calendars:nil];
                    NSArray *eventsOnDate = [eventStore eventsMatchingPredicate:predicate];
                    
                    __block BOOL eventExists = NO;
                    
                    [eventsOnDate enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                        EKEvent *eventToCheck = (EKEvent*)obj;
                        
                        if([event.title isEqualToString:eventToCheck.title])
                        {
                            eventExists = YES;
                            *stop = YES;
                        }
                    }];
                    
                    if(! eventExists)
                    {
                        [event setCalendar:[eventStore defaultCalendarForNewEvents]];
                        
                        NSError *saveEventError;
                        [eventStore saveEvent:event span:EKSpanThisEvent error: &saveEventError];
                        
                        if(saveEventError)
                        {
                            self.alertView.message = @"Could not add event to the calendar because an error ocurred.";
                            [self.alertView show];
                        }
                        else
                        {
                            self.addEventButton.enabled = NO;
                            self.removeEventButton.enabled = YES;
                            
                            self.alertView.message = @"The event was added to the calendar.";
                            [self.alertView show];
                        }
                    }
                    else
                    {
                        self.addEventButton.enabled = NO;
                        self.removeEventButton.enabled = YES;
                        
                        self.alertView.message = @"Could not add event to the calendar because it already existed.";
                        [self.alertView show];
                    }
                }
            });
        }];
    }
    else
    {
        self.alertView.message = @"Could not add event to the calendar because the feature is not supported.";
        [self.alertView show];
    }
}

There are a few interesting points in the above.

Create a new event

The EKEvent objects are created in a given EKEventStore. The EKEventStore is passed as a parameter to the init method. They can carry a lot of interesting properties. Feel free to browse the documentation for more.

EKEvent *event = [EKEvent eventWithEventStore:eventStore];
event.title = self.eventTitle;
event.startDate = self.eventStartDate;
event.endDate = self.eventEndDate;

Searching for events

Search is done through a NSPredicate. You MUST build the predicate with EKEventStore’s predicateForEventsWithStartDate:endDate:calendars:. The actual search is performed by EKEventStore’s eventsMatchingPredicate:. This method returns an array containing all the events between the given dates. You can then iterate through it and search for what ever you want. On this case we will simply query the array for the index of the element with the cool title we defined in viewDidLoad.

Here’s the search code:

NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:event.startDate endDate:event.endDate calendars:nil];
NSArray *eventsOnDate = [eventStore eventsMatchingPredicate:predicate];
                    
NSUInteger eventIndex = [eventsOnDate indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
          EKEvent *eventToCheck = (EKEvent*)obj;
          return [self.eventTitle isEqualToString:eventToCheck.title];
        }];
                    
if(eventIndex != NSNotFound)
{
   // Do something with the event...  
}

Saving the event

Prior to saving the event we have to set the Calendar to which the event will be associated. In this case we’ll have it pinned on the default calendar for new events. We could be using the calendar for reminders if we need a…reminder.

Saving is done through the saveEvent:span:error: method. The span parameter is just a means of saying if we are interested in one (this) occurrence of the event or if we care about all occurrences (recurrent event).

[event setCalendar:[eventStore defaultCalendarForNewEvents]];
                        
NSError *saveEventError;
[eventStore saveEvent:event span:EKSpanThisEvent error: &saveEventError];

Take notice of the button disabling logic according to the different operation outcomes on the method above.

Now that we added the event we’re ready to remove it.πŸ™‚

Once again here’s the bulk:

- (IBAction)removeEventButtonTapped:(id)sender
{
    EKEventStore *eventStore = [[EKEventStore alloc] init];
    if ([eventStore respondsToSelector:@selector(requestAccessToEntityType:completion:)])
    {
        [eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
            dispatch_async(dispatch_get_main_queue(), ^{
                if (error)
                {
                    self.alertView.message = @"Could not access the calendar because an error ocurred.";
                    [self.alertView show];
                }
                else if (!granted)
                {
                    self.alertView.message = @"Could not access the calendar because permission was not granted.";
                    [self.alertView show];
                }
                else
                {
                    NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:self.eventStartDate endDate:self.eventEndDate calendars:nil];
                    NSArray *eventsOnDate = [eventStore eventsMatchingPredicate:predicate];
                    
                    NSUInteger eventIndex = [eventsOnDate indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
                        EKEvent *eventToCheck = (EKEvent*)obj;
                        return [self.eventTitle isEqualToString:eventToCheck.title];
                    }];
                    
                    if(eventIndex != NSNotFound)
                    {
                        NSError *removeEventError;
                        EKEvent *eventToRemove = eventsOnDate[eventIndex];
                        [eventStore removeEvent:eventToRemove span:EKSpanFutureEvents error:&removeEventError];
                        
                        if(removeEventError)
                        {
                            self.alertView.message = @"Could not remove event from the calendar because an error ocurred.";
                            [self.alertView show];
                        }
                        else
                        {
                            self.removeEventButton.enabled = NO;
                            self.addEventButton.enabled = YES;
                            
                            self.alertView.message = @"The event was removed from the calendar";
                            [self.alertView show];
                        }
                    }
                    else
                    {
                        self.addEventButton.enabled = NO;
                        self.removeEventButton.enabled = YES;
                        
                        self.alertView.message = @"Could not remove event from the calendar because it was not found.";
                        [self.alertView show];
                    }
                }
            });
        }];
    }
    else
    {
        self.alertView.message = @"Could not remove event from the calendar because the feature is not supported.";
        [self.alertView show];
    }
}

The code is pretty much the same as we did on the add method. The difference is only on what we do when we find the event:

if(eventIndex != NSNotFound)
{
   NSError *removeEventError;
   EKEvent *eventToRemove = eventsOnDate[eventIndex];
   [eventStore removeEvent:eventToRemove span:EKSpanFutureEvents error:&removeEventError];

Basically we fetch the event to remove from the search array and we call the removeEvent:span:error: on the EKEventStore object.

Note that on this particular case we are removing every single occurrence of the event since we are specifying the EKSpanFutureEvents argument.

There’s a last method on the UIViewController that is used to open the Calendar app from our demo app. It’s very simple as you can see below:

- (IBAction)openCalendarButtonTapped:(id)sender
{
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"calshow://"]];
}

(The URL is the trick!)

If you don’t need to create the programmatically or if you want to give the user the ability to configure the event himself you can use EKEventEditViewController. In the near future I might do another small demo with it just for the sake of completeness (but don’t take my word for it).

If you want you can download or clone the project from git.

u gotta luv git

Today I needed to to change the author names of all commits in a git repo (no, I’m not stealing on anyone’s work, I just happened to have some code I wanted to publish in #pragmapilot that had <paranoid-mode>my real name on it.<paranoid-mode/>).

I googled and was able to find this amazing script that did it perfectly:

#!/bin/sh

git filter-branch --env-filter '

an="$GIT_AUTHOR_NAME"
am="$GIT_AUTHOR_EMAIL"
cn="$GIT_COMMITTER_NAME"
cm="$GIT_COMMITTER_EMAIL"

if [ "$GIT_COMMITTER_EMAIL" = "your@email.to.match" ]
then
cn="Your New Committer Name"
cm="Your New Committer Email"
fi
if [ "$GIT_AUTHOR_EMAIL" = "your@email.to.match" ]
then
an="Your New Author Name"
am="Your New Author Email"
fi

export GIT_AUTHOR_NAME="$an"
export GIT_AUTHOR_EMAIL="$am"
export GIT_COMMITTER_NAME="$cn"
export GIT_COMMITTER_EMAIL="$cm"

Read all the possible caveats here and use it at your own risk.

(I like nukes!)

Tracing Autolayout

A while a go we discussed how to inspect object properties at runtime and we talked about a little UIKit secret: UIView‘s recursiveDescription.

Today I presente you something similar but for Autolayout.

There is an obscure command you can use to debug autolayout, very useful for situations when you have ambiguous layouts (that is, you didn’t supply the AL system enough constraints so it can calculate the apropriate bounds and centres of the view).

So if you find yourself with a broken layout you can simply pause the app’s execution and type in the debugger’s window the following command:

po [[UIWindow keyWindow] _autolayoutTrace]

The debugger will print the view hierarchy in the console and will mark the bogus views with na “AMBIGUOUS LAYOUT” tag in front of them.

Now you will know where to look!

Side note: you can learn very cool stuff by watching Apple’s WWDC vΓ­deos (Check WWDC 2012 session 202).

Being aware of device rotations in hidden UIViewControllers

Long days, long nights, lot’s of stuff to write about, so little time to do it…

Here’s a quick one.

Situation: suppose you have a UIViewController on the screen and put something over it. Say, your app plays videos and you are using a third-party component. Or you have put some modal UIViewController over your original UIViewController. So, to make it clear: you have an UIViewController, let’s call it A. And put another UIViewController over it, let’s call it B.

Pretty simple.

Now, you have some UIViews on A you need to react to rotation (say custom ads views or menus that need to change position on rotation). The first thing that comes to mind is to use:

willRotateToInterfaceOrientation:duration: 

willAnimateRotationToInterfaceOrientation:duration:

didRotateFromInterfaceOrientation: 

But as per doc:

If a view controller is not visible when an orientation change occurs, then the rotation methods are never called. However, the viewWillLayoutSubviews method is called when the view becomes visible.

So, this might come a bit limiting if say, you want to reload your add view, for example. And viewWillLayoutSubviews might not be the best place for it.

There’s little API candy that you can use to help you get through: your (A) UIViewController tells UIDevice to start generating interface change notifications and subscribes them.

Every time your device rotate your UIViewController gets notified (whether it’s visible or not).

You should start notifications and subscribing in viewDidLoad and should stop them and unsubscribe in dealloc.

Here’s the example:


- (void)viewDidLoad
{
    [super viewDidLoad];

    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserverForName:UIDeviceOrientationDidChangeNotification
                                                      object:nil
                                                       queue:nil
                                                  usingBlock:^(NSNotification *note) {
                                                     // do rotation related stuff!!                                        }];

//...

}

- (void)dealloc
{
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Don’t forget to nil delegates!

On the project I am working on the UIViewControllers can contain a grid or a table whose data source is an object apart. This data source gets it’s data from a manager object that fetches JSON data from a Web Service (you can think of it as a feed if you like).

Since there’s “through-the-wire” communication involved the UIViewController is notified by the manager when the data is fetched and ready to be present. This notification involves, as you might expect, delegation.

No big deal.

Except that I was getting these nasty EXC_BAD_ACCESS (SIGSEGV) crashes.

Totally randomly.

At least, so it appeared.

I carefully looking into the problem and zeroed it to the fact that my data manager was trying to deliver data to a UIViewController that had been dealloced. It happened when I was a on a UIViewController and hit the back button all too quickly.

So, now it was an easy fix.

// On the data source
- (void)dealloc
{
self.manager.delegate = nil
}

This is so simple it is almost obvious. But easy to forget in this ARC-times we live…

So, if you want to stay out of trouble use this simple rule-of-thumb:

“if you set a delegate somewhere in your class, don’t forget to set it to nil elsewhere”

(The same goes out to KVOs, and NSNotifications…hey…don’t say I didn’t warn ya…)

Odd-shaped UIButton highlight

On the project I’m working on I got a spec to implement an odd-shaped (meaning: not rectangular) UIButton that must contain a different image for the button’s normal and highlighted states, as well as text.

I didn’t wanted to stray a lot from the UIButton’s functionality, specially I didn’t want to have to implement complex logics to manage the highlighting. So the best thing to do was to work with what I had: the UIButton itself.πŸ™‚

Since the UIButton receives images for different states and does the managing it self my approach was to generate images for each state, assign them to the button and let it do it’s magic.

My button had to have an odd shape, with arrows, a contained image that should change it’s look according to the state and text. So I figured out that I could draw my button shape with a UIBezier, have it filled with the appropriate color for the state and imprint on that shape the contained image. After that I would generate an UIImage and set it to the button. This process was repeated for the other state as well.

Before I go into the code I will leave you a video that shows the whole thing working.

The code is really simple. It’s shown below and it’s commented. So, if you have a grasp on the idea I presented above you won’t have any troubles with it. This is the method that builds the button:

- (UIButton*)buildComplexButton
{
    // Some constants
    static CGFloat fakeButtonHeight  = 100;
    static CGFloat fakeButtonPeakHeight = 150;
    
    // Create button with desired frame
    UIButton *oddButton = [UIButton buttonWithType:UIButtonTypeCustom];
    oddButton.frame = CGRectMake(0.0, 50.0, self.view.bounds.size.width, fakeButtonPeakHeight);
    
    // Create button shape
    UIBezierPath *buttonShape = [UIBezierPath bezierPath];
    [buttonShape moveToPoint: CGPointMake(0.0, 0.0)];
    [buttonShape addLineToPoint:CGPointMake(self.view.bounds.size.width, 0.0)];
    [buttonShape addLineToPoint:CGPointMake(self.view.bounds.size.width, fakeButtonHeight)];
    [buttonShape addLineToPoint:CGPointMake((self.view.bounds.size.width / 2), fakeButtonPeakHeight)];
    [buttonShape addLineToPoint:CGPointMake(0.0, fakeButtonHeight)];
    [buttonShape closePath];
    
    // Declare some vars to hold images and fetch the images that will be contained inside the button
    UIImage *normalStateImage;
    UIImage *highlightedStateImage;
    
    UIImage *normalSkullImage = [UIImage imageNamed:@"skull.png"];
    CGSize normalSkullSize = normalSkullImage.size;
    
    UIImage *highlightedSkullImage = [UIImage imageNamed:@"skull_oops.png"];
    CGSize highlightedSkullSize = normalSkullImage.size;
    
    // Get a graphics context
    UIGraphicsBeginImageContextWithOptions(buttonShape.bounds.size,NO,[UIScreen mainScreen].scale);
    
    /* ### Normal state image ### */
    
    // Set color
    [[UIColor whiteColor] setFill];
    // Fill shape with white color
    [buttonShape fill];
    // Draw the normal skull image inside the context
    [normalSkullImage drawInRect:CGRectMake(CGRectGetMidX(oddButton.frame) - normalSkullSize.width/2, CGRectGetMidY(oddButton.frame) - normalSkullSize.height, normalSkullSize.width, normalSkullSize.height)];
    // Generate an UIImage from the context
    normalStateImage = UIGraphicsGetImageFromCurrentImageContext();
    
    /* ### Highlighted state image ### */
    
    // Set color
    [[UIColor lightGrayColor] setFill];

    // Fill shape with gray color
    [buttonShape fill];
    
    // Draw the "alive" skull image inside the context
    [highlightedSkullImage drawInRect:CGRectMake(CGRectGetMidX(oddButton.frame) - highlightedSkullSize.width/2, CGRectGetMidY(oddButton.frame) - highlightedSkullSize.height, highlightedSkullSize.width, highlightedSkullSize.height)];
    // Generate an UIImage from the context
    highlightedStateImage = UIGraphicsGetImageFromCurrentImageContext();
    
    // End context
    UIGraphicsEndImageContext();

    // Set previously composed images as button background images for the appropriate states
    [oddButton setBackgroundImage:normalStateImage forState:UIControlStateNormal];
    [oddButton setBackgroundImage:highlightedStateImage forState:UIControlStateHighlighted];

    // Do some text related settings on the button
    
    [oddButton setTitleEdgeInsets:UIEdgeInsetsMake(20.0, 0.0, 100.0, 0.0)];
    
    [oddButton.titleLabel setFont:[UIFont fontWithName:@"Chalkduster" size:20.0]];
    
    [oddButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [oddButton setTitle:@"Click me..." forState:UIControlStateNormal];
    
    [oddButton setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
    [oddButton setTitle:@"I'M ALIVE!" forState:UIControlStateHighlighted];
    
    // Return the customized odd-shaped button
    return oddButton;
}

Then it’s all a matter of assign it to a property and/or to your UIViewController’s subview:

- (void)viewDidLoad
{
    [super viewDidLoad];
	
    // Build button and keep a reference to it (just in case...)
    self.oddButton = [self buildComplexButton];
    
    // Add button to view
    [self.view addSubview:self.oddButton];
}

You can grab a sample project with the code at PragmaPilot‘s git page.

Feel free to share alternatives to this solution.πŸ™‚

I take no credit for the skull icons. Please visit the original author site.