Monday, June 24, 2013

Custom Framework for iOS

This entry is for my future reference.

In iOS & MacOSX development, a framework is basically a folder contains headers, nib files, images, object files and other files. Apple provides a list of frameworks along with iOS SDK. Unfortunately, Apple does not provide (or intentionally hided) the ability for Xcode to create custom iOS framework. Without custom framework, it will be troublesome to provide pre-built libraries with UI to external parties. Fortunately with some tricks/hacks one can create a static link custom framework. Please refer to following references.

References:

Monday, April 22, 2013

Releasing AVPlayerLayer and AVPlayer

While using AVPlayerLayer and AVPlayer together, I found that the retainCount of the AVPlayer object is not decremented immediately upon destroying the associated AVPlayerLayer. It seems to be scheduled some time later. If I release them in applicationDidEnterBackground: the free-up progress will just stopped (and the app entered into background) before the freeing is completed. This caused some problems in my project. After some searching, I found that beginBackgroundTaskWithExpirationHandler: allows running background task and so it gives time for the non-finished free-up task. I simply adding it to applicationDidEnterBackground: and the free-up is done completely.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    UIBackgroundTaskIdentifier identifier = UIBackgroundTaskInvalid;
    identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:identifier];
    }];
}

Update: After some searching and trying, I found that a better way is to listen to UIApplicationDidEnterBackgroundNotification event in my class, and I do not need to use the background task hack anymore then.
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(playerDidEnterBackground:)
                                             name:UIApplicationDidEnterBackgroundNotification
                                           object:nil];

Thursday, June 14, 2012

Using NSFileHandle.writeabilityHandler

I am using [NSFileHandle writeData:] to send data over network in one of my projects, and I found an interesting problem when I run the app with the testing device, which is using iOS 5.1. After 100 bytes of data are sent, the writeData method halts and throws an exception. Results from Google search told me that it is a problem since iOS 5.0, and maybe I should use NSFileHandle.writeabilityHandler, which is introduced since iOS 5.0 / MacOSX 10.7, instead. Unfortunately, I could not find a single example on web about how to use NSFileHandle.writeabilityHandler should be used. After a day of trial-and-error I finally figured out a workable solution, and I will put it here as a reference. However, I must say this solution is only workable, but not elegant, not even close. I believe there must be a better solution than mine out there.

Update:
  • It seems that fileDescriptor can be used directly in send() or sendto()
  • It is better to use send() which will not block this thread
  • Updated the code to end the sending if there is sending error

// Some definitions
BOOL isAtOrAboveIOS5 = [yourfileHandle respondsToSelector:@selector(setWriteabilityHandler:)];
NSMutableData* pendingData = [[NSMutableData alloc] initWithCapacity:1024];

// Try to send data here
// iOS 5 / OSX 10.7 or above
if (isAtOrAboveIOS5 == YES) {
    [pendingData appendData:dataToSend];
    remoteFileHandle.writeabilityHandler = ^(NSFileHandle* thisFileHandle)
    {
        int amountSent = send([thisFileHandle fileDescriptor], [pendingData bytes], [pendingData length], MSG_DONTWAIT);
        if (amountSent < 0) {
            // errno is provided by system
            NSLog(@"Error while sending response: %d", errno);
            amountSent = [pendingData length];
        }
        [pendingData replaceBytesInRange:NSMakeRange(0, amountSent) withBytes:NULL length:0];

        // Finishing
        if ([pendingData length] == 0) {
            thisFileHandle.writeabilityHandler = nil;
        }
    };
} else { 
    [yourfileHandle writeData:dataToSend];
}

My guess of the actual problem when using writeData: after iOS 5.0 is that writeData: just tries to send as much data as possible over the socket in one shot. If there is much data than the socket can accept (this time), exception is thrown. With NSFileHandle.writeabilityHandler set, the code block is executed everytime its fileDescriptor (this time it is a socket) can accept more data. Since there is no method in NSFileHandle which tells you how many data is actually written, I used function sendto() here, but I believe there should be a better implementation exists.