Morten Bøgh

My personal blog.

Swift Makes the Little Things More Simple

On monday 2014-06-02 Apple introduced a new scripting-style programming language called Swift. As an iOS developer I find this extremely interesting and looking forward to learn this new language and hopefully boost my productivity :)

Swift introduces a lot of nitfy new functions which makes your life as a developer much easier and hides away the boring details. Here is a showcase of map, reduce and filter.

Map

Objective-C
1
2
3
4
5
6
NSArray *names = @[@"John", @"Steve", @"Tim"];
NSMutableArray *namesTemporary = [NSMutableArray new];
names enumerateObjectsUsingBlock:^(NSString *name, NSUInteger idx, BOOL *stop) {
    [namesTemporary addObject:name.lowercaseString];
}];
names = [namesTemporary copy];
Swift
1
2
var names: String[] = ["John", "Steve", "Tim"]
names = names.map{ name in name.lowercaseString }
Swift (Alternative)
1
names = names.map{ $0.lowercaseString }

Reduce

Objective-C
1
2
3
4
5
NSArray *numbers = @[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10];
__block NSUInteger number = 0;
[numbers enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
    number += num.integerValue;
}];
Swift
1
2
let numbers: Int[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let number = numbers.reduce(0){ previous, next in previous + next }
Swift (Alternative)
1
let number = numbers.reduce(0){ $0 + $1 }

Filter

Objective-C
1
2
NSArray *moreNumbers = @[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10];
moreNumbers = [moreNumbers filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"modulus:by:(SELF, 2) = 0"]];
Swift
1
2
var moreNumbers: Int[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
moreNumbers = moreNumbers.filter{ number in number % 2 == 0 }
Swift (Alternative)
1
moreNumbers = moreNumbers.filter{ $0 % 2 == 0 }

With map and reduce the Swift code is much more simple than the Objective-C counter part. With filter we can take advantage of the powerful NSPredicate to do the magic. I know that I will be using the map function a lot as much of what we do is transforming values into other values.

Empty Back Button on iOS7

On iOS7 the UINavigationBar features a nice little back arrow when used together with a UINavigationItem and/or UINavigationController.

But what if you wanted the back button text to disappear leaving the back arrow there by itself, turns out it is not as simple as you might expect.

Back Button Title Position Adjustment

A popular answer on StackOverflow is simply to call setBackButtonTitlePositionAdjustment:forBarMetrics: on UIBarButtonItem with a large negative offset e.g. UIOffsetMake(-1000, -1000).

[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-1000, -1000) forBarMetrics:UIBarMetricsDefault];

And at the first glance it actually looked like it works, but as Jelle Vandebeeck points out in Empty Back Button In iOS 7 the solutions fails when the title of the previous view controller is too large.

If we put a breakpoint in viewDidAppear: and execute po [[UIWindow keyWindow] recursiveDescription] in the console we get the following output.

<UIWindow: 0x8d6f970; frame = (0 0; 320 480); autoresize = W+H; gestureRecognizers = <NSArray: 0x8d5dbf0>; layer = <UIWindowLayer: 0x8d717d0>>
   | <UILayoutContainerView: 0x8d7bbf0; frame = (0 0; 320 480); autoresize = W+H; gestureRecognizers = <NSArray: 0x8d78a70>; layer = <CALayer: 0x8d7bcd0>>
   |    | <UINavigationTransitionView: 0x8d813f0; frame = (0 0; 320 480); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x8d814d0>>
   |    |    | <UIViewControllerWrapperView: 0x8d61050; frame = (0 0; 320 480); autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x8d88f40>>
   |    |    |    | <UIView: 0x8ab0dc0; frame = (0 0; 320 480); autoresize = RM+BM; layer = <CALayer: 0x8ab0610>>
   |    |    |    |    | <_UILayoutGuide: 0x8ab0e20; frame = (0 0; 0 64); hidden = YES; layer = <CALayer: 0x8ab0e90>>
   |    |    |    |    | <_UILayoutGuide: 0x8ab1080; frame = (0 480; 0 0); hidden = YES; layer = <CALayer: 0x8ab10f0>>
   |    | <UINavigationBar: 0x8d75c40; frame = (0 20; 320 44); opaque = NO; autoresize = W; userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x8d5e750>; layer = <CALayer: 0x8d70f00>>
   |    |    | <_UINavigationBarBackground: 0x8d59af0; frame = (0 -20; 320 64); opaque = NO; autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x8d549f0>> - (null)
   |    |    |    | <_UIBackdropView: 0x8d7c440; frame = (0 0; 320 64); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <_UIBackdropViewLayer: 0x8d7e7b0>>
   |    |    |    |    | <_UIBackdropEffectView: 0x8d7f1c0; frame = (0 0; 320 64); clipsToBounds = YES; opaque = NO; autoresize = W+H; userInteractionEnabled = NO; animations = { filters.colorMatrix.inputColorMatrix=<CABasicAnimation: 0x8ba4490>; }; layer = <CABackdropLayer: 0x8d7f480>>
   |    |    |    |    | <UIView: 0x8d7fc80; frame = (0 0; 320 64); hidden = YES; opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x8d7fce0>>
   |    |    |    | <UIImageView: 0x8d67cc0; frame = (0 64; 320 0.5); userInteractionEnabled = NO; layer = <CALayer: 0x8d67d50>> - (null)
   |    |    | <UINavigationItemView: 0x8ab6400; frame = (124 8; 163 27); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6480>>
   |    |    |    | <UILabel: 0x8ab64b0; frame = (0 3; 163 22); text = 'A Story About a Fish'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6550>>
   |    |    | <UINavigationItemButtonView: 0x8ab6c80; frame = (8 6; 110 30); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6d60>>
   |    |    |    | <UILabel: 0x8ab6f10; frame = (-981 -995; 91 22); text = 'Passionfruit'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6fb0>>
   |    |    | <_UINavigationBarBackIndicatorView: 0x8d87560; frame = (8 12; 12.5 20.5); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8d87650>> - Back

This reveals that the UILabel of the back button indeed has been adjusted by (-1000, -1000) but the superview (UINavigationItemButtonView) still has the ‘original’ frame, which is affecting the frame of the UINavigationItemView containg the title label.

An Empty Back Button

Another approach is to set the backBarButtonItem in viewDidLoad on the ‘parent’ view controller to a UIBarButtonItem with an empty title, nil target and nil action as suggested by Jagie.

- (void)viewDidLoad {
    [super viewDidLoad];
    UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
    [self.navigationItem setBackBarButtonItem:backButtonItem];
}

This approach acutally archives the wanted effect.

This is doable if it is only needed in a single view controller, but if this behaviour is application wide a more sustainable and centralized solution is needed. The option is either subclassing ´UIViewControllerand then use it as a base class or by method swizzlingviewDidLoad`.

Subclassing

Subclassing ´UIViewController´ is straight forward and it is something we do all the time when creating apps. An example of a new base class could be.

//  MOBViewController.h

@interface MOBViewController : UIViewController

@end

//  MOBViewController.m

#import "MOBViewController.h"

@implementation MOBViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
    [self.navigationItem setBackBarButtonItem:backButtonItem];
}

@end

Then every time you create a new view controller just specify MOBViewController as the super class. There is just a small issue with this approach, what if you need to use a UITableViewController or a UICollectionViewController. Then you would need similar base classes with the exact same code in them and duplicate code is bad.

Method swizzling

Since the subclassing approach leads to duplicated code we can use Method Swizzling to archive the same result without writing the same code twice.

//  UIViewController+EmptyBackButton.h

@interface UIViewController (EmptyBackButton)

@end

//  UIViewController+EmptyBackButton.m

#import "UIViewController+EmptyBackButton.h"
#import <objc/runtime.h>

@implementation UIViewController (EmptyBackButton)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(viewDidLoad);
        SEL swizzledSelector = @selector(mob_viewDidLoad);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

#pragma mark - Method Swizzling

- (void)mob_viewDidLoad {
    [self mob_viewDidLoad];
    UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
    [self.navigationItem setBackBarButtonItem:backButtonItem];
}

@end

Now we have specified that we want our back buttons empty in a centralized place with no duplication of code.

Auto Layout and UIRotationGestureRecognizer + UIPinchGestureRecognizer

On a new project I recently joined, I was given the task to implement a feature which involved panning, zooming and rotating a UIImageView. A straightforward task and something I have done a few times before, so I went and wired up everything in the storyboard and found some old code for handling the three required actions. So far so good.

#pragma mark - Gesture recognizer actions

- (IBAction)didPan:(UIPanGestureRecognizer *)panGestureRecognizer {
    CGPoint translation = [panGestureRecognizer translationInView:_bearImageView.superview];
    _bearImageView.center = CGPointMake(_bearImageView.center.x + translation.x, _bearImageView.center.y + translation.y);
    [panGestureRecognizer setTranslation:CGPointZero inView:_bearImageView.superview];
}

- (IBAction)didPinch:(UIPinchGestureRecognizer *)pinchGestureRecognizer {
    _bearImageView.transform = CGAffineTransformScale(_bearImageView.transform, pinchGestureRecognizer.scale, pinchGestureRecognizer.scale);
    pinchGestureRecognizer.scale = 1;
}

- (IBAction)didRotate:(UIRotationGestureRecognizer *)rotationGestureRecognizer {
    _bearImageView.transform = CGAffineTransformRotate(_bearImageView.transform, rotationGestureRecognizer.rotation);
    rotationGestureRecognizer.rotation = 0;
}

The reason that I am using _bearImageView and not panGestureRecognizer.view is that all three recognizers are added to the superView to give a much better user experience as explained by Ole Begemann in Gesture Recognition on iOS with Attention to Detail.

A few seconds later the app was running on my device, but something was not quite right. The whole experience was odd because the image would not overflow the edges of the superView, rotation was done around what seemed an arbitrary point, and any panning action would be reset by subsequent rotate or scale gestures.

I figured that the culpit might be Auto Layout since it was the only new thing in town and the code had previously worked. After some tests and crawling StackOverflow, the conclusion seemed to be adding the UIImageView programatically since Interface Builder apparently is adding hidden constraints. These contraints would then interfer with the transformations of the UIImageView. This looks and feels much better and I have put together a small sample project on GitHub called RotatingAutoLayout. Credit goes to Juliejean Bell for the image of the beautiful bear :)

Success!

ReactiveCocoa: Counting Characters in a UITextView

In a pet project I have started working on, I have decided to use ReactiveCocoa (RAC) together with the pattern Model View ViewModel (MVVM). If you are not familiar with RAC or MVVM for that matter, I can recommend Ash Furrow’s book Functional Reactive Programming on iOS which is a great book on both topics.

In my app, the user can create an entity and add a description text with a maximum length of 256 characters. The number of characters remaining is displayed to the user with a label. This task can fairly easy be done without using RAC like the following.

#pragma mark - UITextViewDelegate

- (void)textViewDidChange:(UITextView *)textView {
    _charactersRemainingLabel.text = [@(EnityDescriptionMaxLength - (NSInteger)textView.text.length) stringValue];
}

But this is not much functional or reactive and since I am trying to learn and think functional and reactive I needed to find another way around this. My first approach was using rac_signalForSelector:fromProtocol: to wrap the UITextViewDelegate method textViewDidChange: like Ash Furrow gave an example of in his book.

[[self rac_signalForSelector:@selector(textViewDidChange:) fromProtocol:@protocol(UITextViewDelegate)] subscribeNext:^(RACTuple *arguments) {
    UITextView *textView = arguments.first;
    _charactersRemainingLabel.text = [@(EnityDescriptionMaxLength - (NSInteger)textView.text.length) stringValue];
}];

So each time the UITextView calls textViewDidChange: on its delegate the signal fires and I get notified in the subscribeNext block. This certainly looks better and the code can be place near the _charactersRemainingLabel.

Then the other day, I stumbled upon RACChannel which made me think I could do better. After a little reserach (#1069) I ended up with the following code.

RACChannelTerminal *characterRemainingTerminal = RACChannelTo(_charactersRemainingLabel, text);
[[_textView.rac_textSignal map:^id(NSString *text) {
    return [@(EnityDescriptionMaxLength - (NSInteger)text.length) stringValue];
}] subscribe:characterRemainingTerminal];

A RACChannelTerminal is created for _charactersRemainingLabel and the property text, which is subscribed to the rac_textSignal signal on UITextView. So each time the text changes the rac_textSignal fires and the value is first mapped and then passed to the channel terminal.

I find the final form kinda beautiful :)

Catch me on Twitter @mbogh if you have any comments or want to discuss.

Octopress More Productive Than WordPress

Welcome to my new blog powered by Octopress.

This is a reboot of my blog and hopefully switching from WordPress to Octopress will make it easier for me to finish all the blog posts I have drafted in the old WordPress install. I find Octopress more suited for me since it is out of the browser and hopefully less distracting, so far I have only productive plugins installed for Sublime Text 2.
Using the tutorial Create a Blog With Octopress and Host It in Github Pages it was fairly easy to setup my blog on GitHub Pages. You can find the repository for this blog on mbogh.github.io.

Currently I am working on finalizing two posts. First one is a tutorial on integrating and using Unity as a component in your app rather than using Unity for every aspect of your app.
The second one is about symbolicating crash reports using LLDB which is useful when symbolicatecrash fails to do so.

I will add an About page when I find the time between changing diapers :)

Catch me on Twitter @mbogh