Categories
english

NSFileWarper pitfalls

Always use the .fileWarpers property

Altough keeping a child File Wrapper as a property does not look stupid beforehand, you will run into problems when doing so. The reason is because the parent wrapper might replace its instances of child wrappers by other instances if its see fit.
The solution is to always consult the .fileWarpers property of the parent Wrapper and enumerate child wrappers from it.

Updating files

There is no method to update a FileWrapper. If the content changes, remove the wrapper from its parent and add a new one.

Files named 1_#%$#%$_MyFile are created

This mechanism is used to ensure that the file names are unique. Therefore, if a file named “MyFile” already exists in the directory, and a child File Warper is added with its preferedName set as “MyFile” too, the second file will be named “1_#%$#%$_MyFile”. Hence the name of the “preferedName” property.

In general, this happens because you’ve messed up with the child wrappers — for example, when updating (see above), you forgot to remove the old wrapper before adding the new one with the same name.

# “File already exists” error when calling writeToURL

I’ve run into this during unit testing.
Say you have a file package on disk, and you want to update it. Calling writeToURL:options:originalContentsURL:error: will provoke an error “File already exists”. Pass the NSFileWrapperWritingAtomic and it will work.

I think the reason is because, by default, Apple engineers wanted to ensure that the file would remain untouched if the saving failed. It looks like NSDocument will ensure this, so it does not even set this option.

Categories
english

Zbar support on armv7 and upper

For one of my client project, I needed a QR code reader. I do know that Apple introduced such a reader on iOS 7, but I needed it to run on iOS 6 as well.

Two open-source librairies looked like serious candidates at first:

Zbar

zxing

zxing dropped support for the iOS platform recently. This leaves us with Zbar.

Zbar pre-compiled library does not support modern architectures

However, there is a problem with the Zbar library found in the SDK: it’s been compiled for the armv6, armv7 and i386 architectures only. Therefore, if your application supports more modern architectures like armv7s or arm64, it won’t link with the precompiled library.

Compiling from the source code

The solution is to compile Zbar yourself from the source code, which you can clone using Mercurial. You shall find explanations for Xcode 4 here, and Xcode 5 here.

However, there is an easier way. Someone had the great idea to clone the original Mercurial repository to github and also created a Podspec, so Zbar may be included in your project using Cocoapods.

In your Podfile, write:

pod 'ZBarSDK', '~> 1.3.1'

and voilà! You might still need the documentation for the Zbar SDK.

Categories
english

UILocalizedIndexedCollation shows empty sections

A client wanted his app to show a list of contacts. He wanted each contacts whose name began with the same letter to be in the same section of a table view. However, he was not satisfied with the first version I coded, because empty sections would show. For instance, if there was no contact whose name began with a B, there would be an empty B section.

So, I went back to my code, and I came up with this result:

Capture d’écran 2013-11-18 à 11.29.20

My code only handles letters from A to Z, with no diacritics.  I was not aware of the UILocalizedIndexedCollation class at the time I programmed the first version, and when I discovered it, it seemed to present an interesting feature: it can handle other locales, in particular, Arabic or Asian alphabets.

For the second version, I thought it would be better using it, but I really wasted two hours:

Capture d’écran 2013-11-18 à 11.04.49

UILocalizedIndexedCollation uses a fixed list of indexes, so empty sessions are shown. Just like the first version I came up with. I thought Apple would have had produced a much better implementation.

By the way, Apple’s documentation is way too spare. NSHipster provides us with a good starting point to understand how to use this class.

Categories
english

Reading Barcodes on iOS 7

iOS 7 introduced APIs to read bar codes using the camera. I could have easily overlooked it without this excellent post from doubleencore.

NSHipster provided sample code, but it missed some details to work. Here is a sample which does.

Sample code

@import AVFoundation;

@interface CEViewController () <AVCaptureMetadataOutputObjectsDelegate>

@property (strong) AVCaptureSession *captureSession;

@end

@implementation CEViewController

- (void)viewDidLoad
{
	[super viewDidLoad];

	self.captureSession = [[AVCaptureSession alloc] init];
	AVCaptureDevice *videoCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
	NSError *error = nil;
	AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoCaptureDevice error:&error];
	if(videoInput)
		[self.captureSession addInput:videoInput];
	else
		NSLog(@"Error: %@", error);

	AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
	[self.captureSession addOutput:metadataOutput];
	[metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
	[metadataOutput setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]];

	AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
	previewLayer.frame = self.view.layer.bounds;
	[self.view.layer addSublayer:previewLayer];

	[self.captureSession startRunning];
}

#pragma mark AVCaptureMetadataOutputObjectsDelegate

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
	for(AVMetadataObject *metadataObject in metadataObjects)
	{
		AVMetadataMachineReadableCodeObject *readableObject = (AVMetadataMachineReadableCodeObject *)metadataObject;
		if([metadataObject.type isEqualToString:AVMetadataObjectTypeQRCode])
		{
			NSLog(@"QR Code = %@", readableObject.stringValue);
		}
		else if ([metadataObject.type isEqualToString:AVMetadataObjectTypeEAN13Code])
		{
			NSLog(@"EAN 13 = %@", readableObject.stringValue);
		}
	}
}

@end

Pitfalls

The issue I had with NSHipster’s sample code is the delegate method was not called at all. I quickly understood this was because the AVCaptureMetadataOutput must be configured to tell which metadata to recognise.

But, and this was not obvious to me, -[AVCaptureMetadataOuput setMetadataObjectTypes:] must be called after -[AVCaptureSession addOutput:]. Otherwise, the following message shows in the console:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVCaptureMetadataOutput setMetadataObjectTypes:] - unsupported type found.  Use -availableMetadataObjectTypes.'

I did try to look at the output of -availableMetadataObjectTypes, and it returns an empty array.

Therefore, -addOuput: must be called before setMetadataObjectTypes. In retrospect, it makes sense: the output object must know it is linked to a video session to know which metadata it may provide.

Categories
english

Add git tracking to an existing Xcode project

When creating a new project on Xcode 4, you are given the opportunity to activate git versioning  for this project.

You might have not checked this option in the first place and wish to add git tracking to the project later. Here is how to do it:

1) Open a Terminal window and go to the Project’s directory (the one that contains the .xcodeproj).

2) Type

  • git init

This adds git version control to the directory and creates a .git repository. The project is now under version control.

3) Some files which should not be versioned. git uses a .gitignore text file for that purpose which contains a list of files (you may use wildcard characters).

To edit .gitignore, you may create an empty file:

touch .gitignore

Then open it in your favourite text editor.

I think it should at least contain:

.DS_Store

You may also add:

MyProject.xcworkspace/xcuserdata/

Because you do not want information specific to a user (e.g. the list of breakpoints) to be pushed to a remote git repository.

4) To track project files (in other words, add them to the “staging area”), type

git add .

5) Finally, commit these files

git commit -m "Initial commit"