Friday, September 3, 2010

How to calculate Easter Day in iPhone OS

Recently, I have to implement the calculation of Easter Day.

Here is what I got

Easter Day is always the Sunday after the full moon that occurs after the spring equinox on March 21. This full moon may happen on any date between March 21 and April 18 inclusive. If the full moon falls on a Sunday, Easter Day if the Sunday following. But Easter Day cannot be earlier than March 22 or later than April 25.

To find the the full moon day, I have used the NSChineseCalendar in iOS 4.

and my implementation is here
eastercal.m Select all

// eastercal.m
#import <Foundation/Foundation.h>
#include <stdio.h>

int main( int argc, char *argv[] )
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSDate *today = [NSDate date]; // today
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger unitFlags = NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit;
NSDateComponents *gregorianComps = [gregorian components:unitFlags fromDate:today];
if (argc==2) {
[gregorianComps setYear:atoi(argv[1])];
[gregorianComps setDay:21];
[gregorianComps setMonth:3];
NSDate *easterStartDate = [gregorian dateFromComponents:gregorianComps]; //March 21 for the year

NSCalendar *chinese = [[NSCalendar alloc] initWithCalendarIdentifier:NSChineseCalendar];
// convert from gregorian calendar to chinese calendar
NSDateComponents *chineseComps = [chinese components:unitFlags fromDate:easterStartDate];
NSDate *easterStartDateChineseDate = [chinese dateFromComponents:chineseComps];

NSDate *easterStartDateChineseDateTemp = easterStartDateChineseDate;
if ([chineseComps day] >=15) { // 15 is the full month day in Chinese Calendar
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setMonth:1]; // set the next month
easterStartDateChineseDateTemp = [chinese dateByAddingComponents:offsetComponents toDate:easterStartDateChineseDate options:0];
NSDateComponents *dayComponents = [chinese components:NSDayCalendarUnit fromDate:easterStartDateChineseDateTemp];
NSDateComponents *componentsToAdd = [[NSDateComponents alloc] init];
[componentsToAdd setDay: (15 - [dayComponents day])];
// find the next full month date
NSDate *springEquinoChineseDate = [chinese dateByAddingComponents:componentsToAdd toDate:easterStartDateChineseDateTemp options:0];

NSDateComponents *springEquinoChineseComps = [chinese components:unitFlags fromDate:springEquinoChineseDate];

NSDateComponents *diffComps = [chinese components:NSDayCalendarUnit fromDate:easterStartDateChineseDate toDate:springEquinoChineseDate options:0];
NSInteger diffDays = [diffComps day];

// calculate the days difference from the March 21 to the next full month day
NSLog(@"diffDays is %ld",diffDays);

NSDateComponents *daysToAdd = [[NSDateComponents alloc] init];
[daysToAdd setDay:diffDays];
NSDate *springEquinoGregorianDate = [gregorian dateByAddingComponents:daysToAdd toDate:easterStartDate options:0];

// convert the next full month date from ChineseDate to GregorianComps
NSDateComponents *springEquinoGregorianComps = [gregorian components:unitFlags fromDate:springEquinoGregorianDate];

NSLog(@"springEquinoGregorian is %ld %ld %ld",[springEquinoGregorianComps year], [springEquinoGregorianComps month], [springEquinoGregorianComps day]);

int weekday = [springEquinoGregorianComps weekday];
NSDate *easterSundayGregorianDateTemp = springEquinoGregorianDate;
NSDateComponents *offsetGregorianComponents = [[NSDateComponents alloc] init];
if (weekday == 7) {
[offsetGregorianComponents setWeek:2]; // If the full moon falls on a Sunday, Easter Day if the Sunday following
else {
[offsetGregorianComponents setWeek:1];
easterSundayGregorianDateTemp = [gregorian dateByAddingComponents:offsetGregorianComponents toDate:springEquinoGregorianDate options:0];
NSDateComponents *weekdayComponents = [gregorian components:NSWeekdayCalendarUnit fromDate:easterSundayGregorianDateTemp];
NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init];
[componentsToSubtract setDay: 0 - ([weekdayComponents weekday] - 1)];
NSDate *easterDate = [gregorian dateByAddingComponents:componentsToSubtract toDate:easterSundayGregorianDateTemp options:0];

NSDateComponents *easterComps = [gregorian components:unitFlags fromDate:easterDate];
NSLog(@"Easter Date is %d %d %d",[easterComps year], [easterComps month], [easterComps day]);
[pool drain];
return 0;

This code does not run on Mac OS 10.5 or iOS 3 as the ChineseCalendar was not implemented.
It will only run on Mac OS 10.6 or iOS 4

iPhone-4:root# gcc -I/var/toolchain/sys30/usr/include -F/var/toolchain/sys30/System/Library/Frameworks -L/var/toolchain/sys30/usr/lib -framework Foundation eastercal.m -o eastercal
iPhone-4:root# ldid -S eastercal
iPhone-4:root# ./eastercal 2011
eastercal[3005:107] diffDays is 27
eastercal[3005:107] springEquinoGregorian is 2011 4 17
eastercal[3005:107] Easter Date is 2011 4 24


Wednesday, June 16, 2010 is down

Please update the cydia source url


Tuesday, June 8, 2010

Sign up for iAd

Here in iTunes Connect -> Contracts

Monday, June 7, 2010

iPhone gcc for SDK 3.2 & iPad

iPhone gcc for SDK 3.2
(1) Install iphone gcc installed in your jailbroken iPad with firmware 3.2

iPhone gcc is available in Cydia. To install it in you need to do these

# assume you have installed APT 0.6 Transitional and Aptitude and wget in Cydia, so that you can use the command apt-get
# if libgcc is broken in Cydia, you have to install it manually before iphone-gcc
dpkg -i libgcc_4.2-20080410-1-6_iphoneos-arm.deb
# install iphone-gcc
apt-get install iphone-gcc

Moreover, install these utilities via apt-get as well

apt-get install make ldid zip unzip wget

for editor in iPad you could use vim or nano

(2) Header files for toolchain and SDK 3.2

the header files for SDK 3.2 is available in one zipped tar file (sys32.tgz). You can download it (about 147M) here

(3) copy and untar the required headers and libraries (first copy to /var/mobile/sys32.tgz) and install it in iPad say

mkdir -p /var/toolchain/
cd /var/toolchain/
tar -xzvf /var/mobile/sys32.tgz

(4) Use this sample and unzip it to test.

This TabBar sample source code is compatible with iPad and iPhone gcc, with auto-rotation and auto-resizing support.

cd TabBarSample
make install

The package includes a new command line utility called appinstall, it is used to install compiled app to /var/mobile/Applications/* directory for iPad, it simulates the app install process via XCode.

Sunday, April 18, 2010

MFMessageComposeViewController Sample Code (OS 4.0 only)

Start a new View-based Application Project called SMS2
and have to add the MessageUI framework to the project

Modify SMS2ViewController.h

  SMS2ViewController.h    Select all

// SMS2ViewController.h
// SMS2

#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMessageComposeViewController.h>

@interface SMS2ViewController : UIViewController <MFMessageComposeViewControllerDelegate> {
UILabel *message;

@property (nonatomic, retain) UILabel *message;



and in SMS2ViewController.m

  SMS2ViewController.m    Select all

// SMS2ViewController.m
// SMS2

#import "SMS2ViewController.h"

@implementation SMS2ViewController
@synthesize message;

// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
return self;

// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *smsButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
smsButton.frame = CGRectMake(97.0, 301.0, 125.0, 37.0);
smsButton.adjustsImageWhenDisabled = YES;

[smsButton setTitle:@" Send SMS" forState:UIControlStateNormal];
[smsButton setTitleColor:[UIColor colorWithWhite:0.000 alpha:1.000] forState:UIControlStateNormal];
[smsButton setTitleShadowColor:[UIColor colorWithWhite:0.000 alpha:1.000] forState:UIControlStateNormal];
[smsButton addTarget:self action:@selector(displayComposerSheet) forControlEvents:UIControlEventTouchUpInside];

message = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 360.0, 280.0, 29.0)];
message.frame = CGRectMake(20.0, 360.0, 280.0, 29.0);
message.adjustsFontSizeToFitWidth = YES;
message.hidden = YES;
message.text = @"";
message.userInteractionEnabled = NO;

[self.view addSubview:smsButton];
[self.view addSubview:message];

MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init];
picker.messageComposeDelegate = self;

picker.recipients = [NSArray arrayWithObject:@"123456789"]; // your recipient number or self for testing
picker.body = @"test from OS4";

[self presentModalViewController:picker animated:YES];
[picker release];
NSLog(@"SMS fired");

- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result {
message.hidden = NO;
switch (result)
case MessageComposeResultCancelled:
message.text = @"Result: canceled";
NSLog(@"Result: canceled");
case MessageComposeResultSent:
message.text = @"Result: sent";
NSLog(@"Result: sent");
case MessageComposeResultFailed:
message.text = @"Result: failed";
NSLog(@"Result: failed");
message.text = @"Result: not sent";
NSLog(@"Result: not sent");

[self dismissModalViewControllerAnimated:YES];


// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;

- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.

- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;

- (void)dealloc {
[super dealloc];



Saturday, April 17, 2010

Enable Homescreen Wallpaper in iPhone Simulator OS 4.0 beta 1

Edit this file (you need root access


and add the key as below
"homescreen-wallpaper" = 1;

The default HomeBackground.jpg and LockBackground.jpg are here
/Users/yourusername/Library/Application Support/iPhone Simulator/4.0/Library/SpringBoard/

As a bonus, if you add this homescreen-wallpaper key-pair, the problem of "iPhone simulator goes black when trying to taking app to background" problem will be solved.


Sunday, April 11, 2010

Enable Emoji Keyboard in iPhone Simulator OS 4.0 beta

iPhone Simulator can enable Emoji in the new iPhone SDK 4.0 beta (similar to previous releases) in here

/Users/yourusername/Library/Application\ Support/iPhone\ Simulator/4.0/Library/Preferences/


Saturday, April 10, 2010

Local Notification Sample Code (OS 4.0 only)

Start a new Window-based Application Project called LocalPush

Add an instance variable bgTask in LocalPushAppDelegate

@interface LocalPushAppDelegate : NSObject {
    UIWindow *window;
    UIBackgroundTaskIdentifier bgTask;

It will fire up a Local Notification to remind you one min before the event which is 2 minutes due from now

LocalPushAppDelegate.m Select all

// LocalPushAppDelegate.m
// LocalPush

@interface ToDoItem : NSObject {
NSInteger year;
NSInteger month;
NSInteger day;
NSInteger hour;
NSInteger minute;
NSInteger second;
NSString *eventName;

@property (nonatomic, readwrite) NSInteger year;
@property (nonatomic, readwrite) NSInteger month;
@property (nonatomic, readwrite) NSInteger day;
@property (nonatomic, readwrite) NSInteger hour;
@property (nonatomic, readwrite) NSInteger minute;
@property (nonatomic, readwrite) NSInteger second;
@property (nonatomic, copy) NSString *eventName;


@implementation ToDoItem

@synthesize year, month, day, hour, minute, second, eventName;


#import "LocalPushAppDelegate.h"

@implementation LocalPushAppDelegate

@synthesize window;

#define ToDoItemKey @"EVENTKEY1"
#define MessageTitleKey @"MSGKEY1"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(@"application: didFinishLaunchingWithOptions:");
// Override point for customization after application launch

UILocalNotification *localNotif = [launchOptions

if (localNotif) {
NSString *itemName = [localNotif.userInfo objectForKey:ToDoItemKey];
// [viewController displayItem:itemName]; // custom method
application.applicationIconBadgeNumber = localNotif.applicationIconBadgeNumber-1;
NSLog(@"has localNotif %@",itemName);
else {
[[UIApplication sharedApplication] cancelAllLocalNotifications];
NSDate *now = [NSDate date];
NSLog(@"now is %@",now);
NSDate *scheduled = [now dateByAddingTimeInterval:120] ; //get x minute after
NSCalendar *calendar = [NSCalendar currentCalendar];

unsigned int unitFlags = NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit;
NSDateComponents *comp = [calendar components:unitFlags fromDate:scheduled];

NSLog(@"scheduled is %@",scheduled);

ToDoItem *todoitem = [[ToDoItem alloc] init]; = [comp day];
todoitem.month = [comp month];
todoitem.year = [comp year];
todoitem.hour = [comp hour];
todoitem.minute = [comp minute];
todoitem.eventName = @"Testing Event";

[self scheduleNotificationWithItem:todoitem interval:1];
[window makeKeyAndVisible];
return YES;

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notif {
NSLog(@"application: didReceiveLocalNotification:");
NSString *itemName = [notif.userInfo objectForKey:ToDoItemKey];
NSString *messageTitle = [notif.userInfo objectForKey:MessageTitleKey];
// [viewController displayItem:itemName]; // custom method
[self _showAlert:itemName withTitle:messageTitle];
NSLog(@"Receive Local Notification while the app is still running...");
NSLog(@"current notification is %@",notif);
application.applicationIconBadgeNumber = notif.applicationIconBadgeNumber-1;


- (void) _showAlert:(NSString*)pushmessage withTitle:(NSString*)title

UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:title message:pushmessage delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertView show];
if (alertView) {
[alertView release];

- (void)scheduleNotificationWithItem:(ToDoItem *)item interval:(int)minutesBefore {
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSDateComponents *dateComps = [[NSDateComponents alloc] init];
[dateComps setMonth:item.month];
[dateComps setYear:item.year];
[dateComps setHour:item.hour];
[dateComps setMinute:item.minute];
NSDate *itemDate = [calendar dateFromComponents:dateComps];
[dateComps release];

UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
localNotif.fireDate = [itemDate dateByAddingTimeInterval:-(minutesBefore*60)];
NSLog(@"fireDate is %@",localNotif.fireDate);
localNotif.timeZone = [NSTimeZone defaultTimeZone];

localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ in %i minutes.", nil),
item.eventName, minutesBefore];
localNotif.alertAction = NSLocalizedString(@"View Details", nil);

localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
// NSDictionary *infoDict = [NSDictionary dictionaryWithObject:item.eventName forKey:ToDoItemKey];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:item.eventName,ToDoItemKey, @"Local Push received while running", MessageTitleKey, nil];
localNotif.userInfo = infoDict;

[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
NSLog(@"scheduledLocalNotifications are %@", [[UIApplication sharedApplication] scheduledLocalNotifications]);
[localNotif release];

- (NSString *) checkForIncomingChat {
return @"javacom";

- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"Application entered background state.");
// UIBackgroundTaskIdentifier bgTask is instance variable
// UIInvalidBackgroundTask has been renamed to UIBackgroundTaskInvalid
NSAssert(self->bgTask == UIBackgroundTaskInvalid, nil);

bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;

dispatch_async(dispatch_get_main_queue(), ^{
while ([application backgroundTimeRemaining] > 1.0) {
NSString *friend = [self checkForIncomingChat];
if (friend) {
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif) {
localNotif.alertBody = [NSString stringWithFormat:
NSLocalizedString(@"%@ has a message for you.", nil), friend];
localNotif.alertAction = NSLocalizedString(@"Read Msg", nil);
localNotif.soundName = @"alarmsound.caf";
localNotif.applicationIconBadgeNumber = 1;
NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:@"Your Background Task works",ToDoItemKey, @"Message from javacom", MessageTitleKey, nil];
localNotif.userInfo = infoDict;
[application presentLocalNotificationNow:localNotif];
[localNotif release];
friend = nil;
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;

- (void)dealloc {
[window release];
[super dealloc];



Tuesday, April 6, 2010

Old versions of iPhone SDK

You need Apple developer account to login
But Apple has disabled some of the links recently

iPhone SDK 2.2.1 Leopard (10.5.4)
or here

iPhone SDK 3.0 (Xcode 3.1.3) Leopard (10.5.7)

iPhone SDK 3.0 (Xcode 3.2) Snow Leopard (10.6.0)

iPhone SDK 3.1 with Xcode 3.1.4 Leopard (10.5.7)
or here

iPhone SDK 3.1 with XCode 3.2.1 for Snow Leopard (10.6.0)

iPhone SDK 3.1.2 with XCode 3.1.4 for Leopard (10.5.7)

iPhone SDK 3.1.2 with XCode 3.2.1 for Snow Leopard (10.6.0)

Update : You are too late, Apple has removed the links above.

iPhone SDK 3.1.3 with XCode 3.1.4 for Leopard (10.5.7)

iPhone SDK 3.1.3 with XCode 3.2.1 for Snow Leopard (10.6.0)

iPhone SDK 3.2 Final with Xcode 3.2.2 for Snow Leopard (10.6.0)

Xcode 3.2.3 and iPhone SDK 4 GM seed for Snow Leopard (10.6.2)

Xcode 3.2.3 and iPhone SDK 4 Final for Snow Leopard (10.6.2)

Xcode 3.2.3 and iOS SDK 4.0.1 for Snow Leopard (10.6.4)

Xcode 3.2.3 and iOS SDK 4.0.2 for Snow Leopard (10.6.4)

Xcode 3.2.4 and iOS SDK 4.1 for Snow Leopard (10.6.4)

Xcode 3.2.5 and iOS SDK 4.2 GM for Snow Leopard (10.6.4)

Xcode 3.2.5 and iOS SDK 4.2 for Snow Leopard (10.6.4)

Xcode 3.2.6 and iOS SDK 4.3 GM Seed for Snow Leopard (10.6.6)

Xcode 3.2.6 and iOS SDK 4.3 for Snow Leopard (10.6.6)

Xcode 3.2.6 and iOS SDK 4.3.1 for Snow Leopard (10.6.6)

Xcode 4 and iOS SDK 4.3 for Snow Leopard (10.6.6)

Xcode 4.0.1 and iOS SDK 4.3.1 for Snow Leopard (10.6.6)

Xcode 4.0.2 and iOS SDK 4.3.2 for Snow Leopard (10.6.6)

Xcode 4.1 for Snow Leopard (10.6.6)

Xcode 4.1 for Lion (10.7)

Xcode 4.2 and iOS SDK 5.0 for Snow Leopard (10.6.6)

Xcode 4.2 and iOS SDK 5.0 for Lion (10.7)

Xcode 4.2.1 and iOS SDK 5.0 for Lion (10.7)

Xcode 4.3 and iOS SDK 5.0 for Lion (10.7.2)
Note: For Xcode 4.3, you need also to install the optional components such as command line tools, and previous iOS Simulators from Xcode’s Download’s preferences

Xcode 4.3.1 and iOS SDK 5.1 for Lion (10.7.3)

Xcode 4.3.2 and iOS SDK 5.1 for Lion (10.7.3)

Xcode 4.3.3 and iOS SDK 5.1 for Lion (10.7.3)

Xcode 4.4 and iOS SDK 5.1 for Lion (10.7.3) or Mountain Lion (10.8)

Xcode 4.4.1 and iOS SDK 5.1 for Lion (10.7.3) or Mountain Lion (10.8)

Xcode 4.5 and iOS SDK 6 GM Seed for Lion (10.7.4) and Mountain Lion (10.8)

Xcode 4.5 and iOS SDK 6 for Lion (10.7.4) and Mountain Lion (10.8)

Command Line Tool for Xcode 4.5 for Mountain Lion (10.8)

Command Line Tool for Xcode 4.5 for Lion (10.7.4)