Make Better Things



I like to make better things.

Check iOS version and write conditional code

I strongly recommend not to use iOS version numbers to write conditional code. There is usually a more reliable method of checking whether a particular feature available or not. But if you are in a deadly situation and hardly needed the version number then you can get iOS version number by UIDevice class. Here is the real code –


NSString *currSysVer = [[UIDevice currentDevice] systemVersion];

Here is how you can use this to write conditional code –


// A system version of 3.1 or greater is required to use CADisplayLink. The NSTimer
// class is used as fallback when it isn't available.
NSString *reqSysVer = @"3.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
    displayLinkSupported = TRUE;

Above code works great but writing this condition is real pain every time. The solution of this is Macros. We can use Macros in objective c and reduce the line of code in above code.

Here is real code using Macros –

/*
 *  System Versioning Preprocessor Macros
 */ 

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)


And how we can use these Macros to write conditional code –


/*
 *  Usage
 */ 

if (SYSTEM_VERSION_LESS_THAN(@"4.0")) {
    ...
}

if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"3.1.1")) {
    ...
}

Hope these Macros will save someone’s time.

Recipe for Programming Success

Here’s my recipe for programming success:

• Get interested in programming, and do some because it is fun.

• Talk to other programmers; read other programs. This is more important than any book or training course.

• Program. The best kind of learning is learning by doing. To put it more technically, “the maximal level of performance for individuals in a given domain is not attained automatically as a function of extended experience, but the level of performance can be increased even by highly experienced individuals as a result of deliberate efforts to improve.” and “the most effective learning requires a well-defined task with an appropriate difficulty level for the particular individual, informative feedback, and opportunities for repetition and corrections of errors.”

• Work on projects with other programmers. Be the best programmer on some projects; be the worst on some others. When you’re the best, you get to test your abilities to lead a project, and to inspire others with your vision. When you’re the worst, you learn what the masters do, and you learn what they don’t like to do (because they make you do it for them).

• Work on projects after other programmers. Be involved in understanding a program written by someone else. See what it takes to understand and fix it when the original programmers are not around. Think about how to design your programs to make it easier for those who will maintain it after you.

• Remember that there is a “computer” in “computer science”. Know how long it takes your computer to execute an instruction, fetch a word from memory (with and without a cache miss), read consecutive words from disk, and seek to a new location on disk.

Detecting user inactivity/idle time since last touch on screen

Recently I required to find inactivity of screen in one of my iPad project.  Here is the steps by which I completed this task.

Step 1 -  Add a class (IdleTimeCheck) in your project which subclass UIApplication. In the implementation file, override the sendEvent: method like so:


- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];

// Only want to reset the timer on a Began touch or an Ended touch, to reduce the number of timer resets.
NSSet *allTouches = [event allTouches];
if ([allTouches count] > 0) {
// allTouches count only ever seems to be 1, so anyObject works here.
UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
if (phase == UITouchPhaseBegan || phase == UITouchPhaseEnded)
[self resetIdleTimer];
}
}

- (void)resetIdleTimer {
if (idleTimer) {
[idleTimer invalidate];
[idleTimer release];
}

idleTimer = [[NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:@selector(idleTimerExceeded) userInfo:nil repeats:NO] retain];
}

- (void)idleTimerExceeded {
NSLog(@"idle time exceeded");
}

where maxIdleTime and idleTimer are instance variables.

Step 2 – Modify your UIApplicationMain function in main.m file to use your UIApplication subclass class as principal class.

int retVal = UIApplicationMain(argc, argv, @"IdleTimeCheck",nil);

And its done.

Hope it help someone who is looking for something like this.

Headphone plug-in plug-out event in iOS

To get the headphone plug-in plug-out event we need to employ a property listener callback function. The system invokes the callback when a user plugs in or unplugs a headset, or docks or undoes the device – thereby adding or removing an audio connection. The system also invokes the property listener callback when a Bluetooth device connects or disconnects.

Defining a Property Listener Callback Function

To respond to a route change, a property listener callback function must:

  • Identify the nature of the route change
  • Branch, depending on the specific route change and the current audio context (for example, recording, playback, or stopped)
  • Take or invoke appropriate action

Here is the example of this type of callback function. To see a similar callback function in working project you should see AddMusic sample code by Apple.

void audioRouteChangeListenerCallback (
void                   *inUserData,                                 // 1
AudioSessionPropertyID inPropertyID,                                // 2
UInt32                 inPropertyValueSize,                         // 3
const void             *inPropertyValue                             // 4
)

{

if (inPropertyID != kAudioSessionProperty_AudioRouteChange) return; // 5
MainViewController *controller = (MainViewController *) inUserData; // 6
if (controller.appSoundPlayer.playing == 0 ) {                      // 7
return;
} else {
CFDictionaryRef routeChangeDictionary = inPropertyValue;        // 8
CFNumberRef routeChangeReasonRef = CFDictionaryGetValue (routeChangeDictionary,CFSTR (kAudioSession_AudioRouteChangeKey_Reason));
SInt32 routeChangeReason;
CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason);
if (routeChangeReason ==  kAudioSessionRouteChangeReason_OldDeviceUnavailable) {  // 9

[controller.appSoundPlayer pause];
UIAlertView *routeChangeAlertView =    [[UIAlertView alloc] initWithTitle: @"Playback Route Changed"  message: @"Audio output was changed." delegate:nil cancelButtonTitle: @"Ok" otherButtonTitles:nil];
[routeChangeAlertView show];

}

}

}

Here’s how this code works:

  1. A pointer to data that you provide when initializing your audio session.In an Objective-C class file, such as a view controller class, you place the property listener callback function outside of the class implementation block. Because of this, the callback needs a reference to the controller object to be able to send messages to it. You provide this reference when initializing your audio session.
  2. The identifier for the property that this callback function gets notified about.
  3. The size, in bytes, of the data in the inPropertyValue parameter.
  4. The current value of the property that this callback function is monitoring. Because the property being monitored is the kAudioSessionProperty_AudioRouteChange property, this value is a CFDictionary object.
  5. Ensures that the callback was invoked for the correct audio session property change.
  6. Initializes a MainViewController object instance to the reference passed in by the inUserData parameter. This allows the callback to send messages to your view controller object—typically defined in the same file that implements the callback.
  7. If application sound is not playing, there’s nothing to do, so return.
  8. This line and the next several lines determine the reason for the route change. The one route change of interest in this playback-only example is that an output device, such as a headset, was removed.
  9. If an output device was indeed removed, then pause playback and display an alert that allows the user to stop or resume playback.

Registering Your Property Listener Callback with the Audio Session

Your application can listen for hardware and route change events by way of the property mechanism in Audio Session Services. For example, to listen for route change events, you register a callback function with your audio session object, as shown here.

AudioSessionPropertyID routeChangeID =  kAudioSessionProperty_AudioRouteChange;    // 1

AudioSessionAddPropertyListener (                                  // 2

routeChangeID,                                                 // 3

audioRouteChangeListenerCallback,                                      // 4

userData                                                       // 5

);

Here’s how this code works:

  1. Declares and initializes a variable to the identifier for the property you want to monitor.
  2. Registers your property listener callback function with your initialized audio session.
  3. The identifier for the property you want to monitor.
  4. A reference to your hardware listener callback function.
  5. Data you want the audio session to pass back to your callback function.