Aleksandar • Vacić

iOS bits and pieces

Masterclass: Constants and Flags

You have probably seen something like this many times:

1
2
3
4
5
6
7
#define kMySegueIdentifier @"SegueMain"
#define reuseIdentifier @"TABLECELL"
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 320.0f
#define CELL_CONTENT_MARGIN 10.0f

CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

This is usually found in quick&dirty code samples, to define constants. You can also see it in many older APIs, where various constants were defined this way. This is bad. It works, but it’s lazy.

#define is pre-processor directive and should not be used to define numbers and strings that will then be used as part of the actual code. It’s a hard-coded value that lives in its own memory space, outside of your regular scope.

Use proper, modern stuff that Objective-C offers and Xcode supports very well.

Masterclass: naming things

I’m a big believer that code should have consistent style. I got my first contract web gig not by how my code performed, but how it looked. Its vibe was that it’s a code done by person that cares.

There is only one true rule: make up your mind and be utterly and thoroughly consistent.

Style guide

I was lucky to be properly schooled very early on naming and general guidelines how Cocoa code should look. I found two great blog posts by Scott Stevenson (he works at Apple for a number of years now) from back in 2004. Everything he wrote back then still holds true and I urge you to go and read both. Scott explains not only how but also why it should be that way.

Back in 2008, I made a small booklet out of those pages and color-printed it. It was siting on my table for years, right next to the monitor.

Over time, I have developed few rules of my own.

How to invalidate flow collection view layout on rotation

Say you want to have grid layout with cells always half the size of the screen’s width. You want to maintain that layout in both portrait and landscape. Since iOS 8 that can’t be easier, with the right incantations.

First, subclass UICollectionViewFlowLayout.

Then add the following method into it:

1
2
3
4
5
6
7
8
9
- (UICollectionViewLayoutInvalidationContext *)invalidationContextForBoundsChange:(CGRect)newBounds {

  UICollectionViewFlowLayoutInvalidationContext *context = (UICollectionViewFlowLayoutInvalidationContext *)[super invalidationContextForBoundsChange:newBounds];
  context.invalidateFlowLayoutDelegateMetrics = (
                                                 CGRectGetWidth(newBounds) != CGRectGetWidth(self.collectionView.bounds) ||
                                                 CGRectGetHeight(newBounds) != CGRectGetHeight(self.collectionView.bounds)
                                                 );
  return context;
}

The key here is casting returned context from super as UICollectionViewFlowLayoutInvalidationContext which allows you to toggle the invalidateFlowLayoutDelegateMetrics property. That will tell UIKit that it should re-query FlowLayout’s delegate for the sizes.

In the collection view controller, make sure it’s set to adopt UICollectionViewDelegateFlowLayout protocol and then add this delegate call:

1
2
3
4
5
6
- (CGSize)collectionView:(UICollectionView *)collectionView
                layout:(UICollectionViewFlowLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

  return CGSizeMake(floor(self.view.bounds.size.width/2.0), 88);
}

Done.

How to symbolicate watchOS 2 extension crash logs from device

I’m working on the next version of Run 5k app, which includes standalone watchOS 2 app. In current Xcode 7 beta 4, it takes 30-40mins for the watch .app bundle to get copied from iPhone to the watch device. No idea why it takes so long; it’s even called out as issue in Xcode 7 beta 4 release notes (they promise it will be resolved in future seeds).

The main problem with this is that debugging is next to impossible. Due to this you can’t realistically debug with the devices attached to Xcode so the only way is to install the iOS app, let it copy the watch bundle over and then use the app once it’s done.

My last-night test ended in a very repeatable crash. Great. Crashes from the Apple Watch are always copied to its paired iPhone and you can get them by connecting the iPhone, then opening Windows / Devices in Xcode, then tap “Show device logs” button.

Xcode then goes and symbolicate each .crash log file for you. However, in my case it symbolicated everything except my own code.