Category: iOS

How to calculate ‘Tomorrow at 4 pm’ in iOS

NSCalendar *calendar = [NSCalendar gregorianCalendar];
NSDateComponents *oneDayComponents = [[NSDateComponents alloc] init]; = 1;
NSDate *tomorrow = [calendar dateByAddingComponents:oneDayComponents toDate:[NSDate date] options:nil];
NSUInteger unitFlags = NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents *tomorrowAt4PM = [calendar components:unitFlags fromDate:tomorrow];
tomorrowAt4PM.hour = 16;
tomorrowAt4PM.minute = 0;
tomorrowAt4PM.second = 0;
NSDate *alarmDate = [calendar dateFromComponents:tomorrowAt4PM];

Click to access session_304__events_and_reminders_in_event_kit.pdf

Facebook sharing feature in iOS app

Classic/Obsolete(?) way

Install SDK

Drag framework folder into XCode project.

Drag bundle into XCode project.

Drag deprecated header folder into XCode project. check copy in needed.

When picking dependency libraries, set them to optional, to allow app running on iOS5.

Set other link flag to -lsqlite3.0

Those steps are fully documented on facebook developer site:

Note: we are using old fashion Facebook sharing feature in app.  (FBDelegate vs. FBRequest) Because my client doesn’t like posting story on their wall without a preview. This way app will popup a separated window to display link to share, image, title, description, etc. User needs to type something in the textbox with placeholder ‘say something about this…’

Note: again, preset message feature has been disabled by Facebook as of 2012.

In app, we are adding #include “Facebook.h” to implement this.

Note: you can use sharekit library for this, but I want to figure out what’s happening behind the scene.

In the place you want to popup Facebook share options to user, do this to open a Facebook session.

// openSessionWithAllowLoginUI will be called by the actual sharing screen where users can click button to post message on their wall.
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI
    NSArray *permission = [[NSArray alloc] initWithObjects:@"user_likes",@"publish_actions", nil];

    return [FBSession openActiveSessionWithPublishPermissions:permission
                                            completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
                                                [self sessionStateChanged:session state:status error:error];

NSString *const FBSessionStateChangedNotification = @"com.bitrixsoft.Login:FBSessionStateChangedNotifiction";

- (void)sessionStateChanged:(FBSession *)session
                    state:(FBSessionState) state
                    error:(NSError *)error
    switch (state) {
        case FBSessionStateOpen:
            if (!error) {
                NSLog(@"User session found");
        case FBSessionStateClosed:
        case FBSessionStateClosedLoginFailed:
            [FBSession.activeSession closeAndClearTokenInformation];

    [[NSNotificationCenter defaultCenter]

    if (error) {
        [[[UIAlertView alloc]
          otherButtonTitles:nil] show];

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
    return [FBSession.activeSession handleOpenURL:url];

I put this in appDelegate, so it can be shared. I don’t want to call this in appDelegate, otherwise the redirect to facebook to ask permission/login will annoy users.

Add those in appDelegate.h file

extern NSString *const FBSessionStateChangedNotification;

- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI;

Before calling opensession, sharing window should subscribe statechange notification.

[[NSNotificationCenter defaultCenter]

- (void)sessionStateChanged:(NSNotification *)notification
    if ((FBSession.activeSession.isOpen)) {

        if (nil == self.facebook) {
            self.facebook = [[Facebook alloc]

            self.facebook.accessToken = FBSession.activeSession.accessToken;
            self.facebook.expirationDate = FBSession.activeSession.expirationDate;

            // resume sharing routine here, because first time attempt without login will stop after app back from background, 
            // but this method will get statechange notifcation, so perfect to resume/restart here.
            [self shareViaFacebook];

        self.facebook = nil;

Finally, the actual post action:

- (IBAction)shareOptions:(id)sender

    BNAppDelegate * appDelegate = (BNAppDelegate*)[[UIApplication sharedApplication] delegate];
    [appDelegate openSessionWithAllowLoginUI:YES];

    UIActionSheet * options = [[UIActionSheet alloc]
                               otherButtonTitles:@"Twitter", @"Facebook", @"email", nil];

    [options showInView:self.view];
- (void)shareViaFacebook
    NSString * message = @"test msg from my test ios app";

    // no nil allow in any parameter
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                   @"Bitrix News iPhone app", @"name",
                                   _post.title, @"caption",
                                   _post.exerpt, @"description",
                                   @"", @"link",
                                   @"", @"picture",
                                   message, @"message", //already obsolete, don't bother, facebook will ignore it anyway

    [self.facebook dialog:@"feed" andParams:params andDelegate:self];


Someother gotcha, app identifier must match between app and setting in, otherwise facebook will complain app is misconfigured when starting the session.


Push notification coding memo

Push messages are all from apple push server, one for each environment: sandbox and production.

You need to build a push client to send message to Apple push server in PHP or ruby. Push client needs ssl certificate from apple. To get it, enable push feature in your AppId, import cert, then create new provision file.

Considering Xtify or UrbanAirship if you or your clients don’t have your/their own VPS. Both Xtify and UrbanAirship offer free tier membership, 1 million push messages per month.

Documentation from Xtify and UrbanAirship are a little bit different, Xtify needs you export private key under APN certificate item as push certificate, while UrbanAirship document stated that you need to export the APN certificate itself (not the private key under), otherwise when exporting certificate, it will report error saying you are importing a development certificate.

(Update at 2012-07-21: I couldn’t get message sent from Xtify server for my new app, re-export the dev certificate from root instead from child, upload this certificate, problem solve. This is consistent with UrbanAireship then, while the Xtify doc did state this very clear, Right click and export this key as .p12 format. should be right click on ‘Apple Development iOS push service’ to export, not on your name.)

Xtify doesn’t need credit card to register, UrbanAirship won’t allow you create App for production until you give away your credit card number.

Xtify has a magic schedule settings, forgetting set this will cause not sending message, ensure to activate it and check measurement report before you revoke your certificate and start over.

Support response from UrbanAirship is mush faster, my first question was answered in 3 hours, amazing.

Call Apple feedback service frequently to eliminate inactive device from your send out list.

Invalid Product Id?

I’ve re-visited this same blog post again and again at least 10 times since I started iOS developing, every time with different answer, to save me search time in the future, I quoted the list here and added my 2 cents for how to solve invalid-product-id problem.

List from Troy:

  • Have you enabled In-App Purchases for your App ID?
  • Have you checked Cleared for Sale for your product?
  • Have you submitted (and optionally rejected) your application binary? (I don’t think this is necessary, in fact, after you submit, you can add new product into the app anymore)
  • Does your project’s .plist Bundle ID match your App ID?  (This is what I got today, App ID and bundle ID were created by another guy)
  • Have you generated and installed a new provisioning profile for the new App ID? (got me 2 times)
  • Have you configured your project to code sign using this new provisioning profile? (got me 1 time)
  • Are you building for iPhone OS 3.0 or above?
  • Are you using the full product ID when when making an SKProductRequest? (got me 1 time)
  • Have you waited several hours since adding your product to iTunes Connect? (got me 1 time)
  • Are your bank details active on iTunes Connect? (via Mark)
  • Have you tried deleting the app from your device and reinstalling? (via Hector, S3B, Alex O, Joe, and Alberto) (This solved my problem 3 times)
  • Is your device jailbroken? If so, you need to revert the jailbreak for IAP to work. (via oh my god, Roman, and xfze)


My suggestions:

  1. Test listing product from simulator first, then move on to device. Ensure product id is OK.
  2. Uninstall can fix the problem happened on device most of time.
  3. Delay on new created product happens, at least to me 2 times, but not always, check your local settings carefully following the list above.
  4. Once old product id being removed, the new product id still can not conflict with the old id, it’s a soft delete in Apple’s system.
  5. Don’t bother auto-renewable subscription product, unless your are building a newsstand/magazine app.
  6. Don’t be too greedy on consumable in-app purchase product, start small, you can add big one in later version. Apple is very strict on those coin-like products.

Build an iOS app access only Drupal CMS site

Drupal is based on mysql, in the new version 7 it gives option to choose sqlite. Either way, connecting from iOS app directly to Drupal db is a problem.

Somebody posted mysql library for mac OS, even for iSO, but it’s not public API. Apple won’t like it.

Solution: RESTful service on Drupal

Installation process is painful, because the missing sypc.php can’t be done through Update management web interface module.

Turn on RESTful service, getting node/1 is not a problem, but I couldn’t make user/1 to work, later I realized it’s permission problem, had to grant ‘pull user profile’ right to anonymous user.

The official doc stated that RESTful comes with basic authentication sub module, but I couldn’t find it, the only one available in RESTful config panel is Session Authentication, which means admin must log in to be able to call user/1

Tried http://username:passwrod@mysite/test/user/1, and using code to pass credential, like this code in ruby,  no good.

uri = URI('')

req =

req.basic_auth 'user', 'psw'

res = Net::HTTP.start(, uri.port) {|http|
p res
puts res.body

It seems Drupal RESTful service is having problem in basic authentication. Or I just too dumb to figure it out.

My project doesn’t have a requirement to protect web content, which means those nodes should be only available to iOS client.

Work Around: Secure site module

One problem to fix is to apply patch for non-Apache-module mode php site, mine is CGI, adding

RewriteRule .* – [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

to .htaccess, tah-dah, it works!

While the database setup in secure site module is still too messy to me, I just end up with setup a highly secured anonymous user for now. Anyway, content has been protected.

About testing Drual REST servcie

GET is fine, but is a POST only URI.

Test method 1: download Poster Firfox add-on, set Content Type to application/x-www-form-urlencoded, parameter to tid=1

To get json result, set headers to application/json.

Test method 2:

curl –form ‘tid=1’ -u user:pwd –header ‘Accept:application/xml’

Watch out, by default form-urlencoded is not checked after installation.

Passing json as request data is much easier.

curl –d {‘tid’:1} -u user:pwd –header ‘Accept:application/json’

doesn’t work for me, don’t know what missing.

Back to Session Authentication

According to this comment, to properly setup a session in RESTfule service is this:

  1. Get session id through an empty body POST to rest/system/connect, parse response to get session id. (Must set req[‘Accept’] = ‘application/json’ or xml if using ruby)
  2. POST to rest/user/login with parms like sessid=2c038ba6326f12f132502e98434857cc&username=<username>&password=<password>
  3. Parse body.

This works, but how to keeping session in the following rest request is not clear, more specific, how to pass sess id in request?

Here is another way based on Michael Cole’s comment, calling login directly, his example is saving session name/id in a cookie file, then passing it as session cookie in the following request. If actually can be passed as Cookie attribute in header.

Here is my explanation in ruby code,

  def login(username, psw)
    uri = URI("")

    req =

    req.content_type = 'application/x-www-form-urlencoded'
    req.set_form_data("username" => username, 'password' => psw)
    #req['Accept'] = 'application/json'  #another style if url not end with .json

    res = Net::HTTP.start(, uri.port) {|http|

    if Net::HTTPOK
       json = JSON(res.body)

      @session_id = json["sessid"]
      @session_name = json["session_name"]

  def get_post(id)
    uri = URI("{id}.json")

    req =
    req["Cookie"] =  "#{@session_name}=#{@session_id}"
    res = Net::HTTP.start(, uri.port) {|http|


    if Net::HTTPOK

       json = JSON(res.body)

      return json["body"]["und"][0]["safe_value"]


Best doc I found on drupal REST service.

Sqlite and CoreData in iOS, repository pattern

I haven’t see too many articles talking about iOS Core-Data development yet, so I documented something important here for future reference.

  1.  No need to create ID property as identity column for your model, in fact, id is a reserved keyword for cocoa, IDE will looks weird for id property in model. core-data will generate a few extra columns in your model table, including ZID, Z…, all  have a Z prefix.
  2. All number like fields will be mapped to NSNumber.
  3. Create app with core-data support, in XCode4, single view app template doesn’t have this core-data support option, try create another project in different project template, then copy the generated core-data code from appDelegate into your project.
  4. If you have your own existing database which will be installed with app bundle, you need to manually copy it from bundle to document folder to make it writable.
  5. New object needs to save into database later should created by insertNewObjectForEntityForName.
  6. Hold the managedObjectContext till the app terminate, that’s why core-data code were in appDeledate, if move into you own class, create retain property.

Personnly, I like to implement repository pattern for all coredata operations, including initialize. My sample repository looks like this:

@interface iCWOccurrenceRepository()
- (NSMutableArray *)retrieveAllOccurrencesForCity:(NSString*)city sinceDate:(NSDate*)startDate;

@implementation iCWOccurrenceRepository

@synthesize managedObjectContext = __managedObjectContext;
@synthesize managedObjectModel = __managedObjectModel;
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;

NSString * DBNAME = @"mydb.sqlite";

- (void)saveANew:(NSArray*) line{
    NSManagedObjectContext * context = self.managedObjectContext;
    NSManagedObject *newOccurrence = [NSEntityDescription

    Occurrence* oToSave = ((Occurrence*)newOccurrence); = [NSNumber numberWithInt:[[line objectAtIndex:0] intValue]]; = [line objectAtIndex:1];
    [self saveContext];

- (void)saveContext
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = __managedObjectContext;
    if (managedObjectContext != nil)
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])
             Replace this implementation with code to handle the error appropriately.

             abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);

#pragma mark - Core Data stack

 Returns the managed object context for the application.
 If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
    if (__managedObjectContext != nil)
        return __managedObjectContext;

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil)
        __managedObjectContext = [[NSManagedObjectContext alloc] init];
        [__managedObjectContext setPersistentStoreCoordinator:coordinator];
    return __managedObjectContext;

 Returns the managed object model for the application.
 If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
    if (__managedObjectModel != nil)
        return __managedObjectModel;
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"iCW" withExtension:@"momd"];
    __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return __managedObjectModel;

 Returns the persistent store coordinator for the application.
 If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
    if (__persistentStoreCoordinator != nil)
        return __persistentStoreCoordinator;

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:DBNAME];

    NSError *error = nil;
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
         [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);

    return __persistentStoreCoordinator;

#pragma mark - Application's Documents directory

 Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];

-(void)createEditableCopyOfDatabaseIfNeeded {
	NSFileManager *fileManager = [NSFileManager defaultManager];
	NSURL *documentDirectory = [self applicationDocumentsDirectory];
	NSString *writeableDBPath = [[documentDirectory
	NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath]

	BOOL defaultDbExists = [fileManager fileExistsAtPath:defaultDBPath];
	BOOL dbExistsInDocFolder = [fileManager fileExistsAtPath:writeableDBPath];

	if (!dbExistsInDocFolder && defaultDbExists) {

		NSError *error;
		BOOL success = [fileManager copyItemAtPath:defaultDBPath toPath:writeableDBPath error:&error];

		if (!success) {
            NSAssert1(0, @"Failed to create writable database file with message '%@'.",
                      [error localizedDescription]);


- (NSMutableArray *)retrieveAllOccurrencesForCity:(NSString*)city sinceDate:(NSDate*)startDate{

	NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
	NSFetchRequest *request = [[NSFetchRequest alloc] init];
	NSEntityDescription *entity = [NSEntityDescription entityForName:@"Occurrence"
	[request setEntity:entity];

	NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
										initWithKey:@"id" ascending:NO];

	NSArray *sortDescriptors = [[NSArray alloc]
								initWithObjects:sortDescriptor, nil];

	[request setSortDescriptors:sortDescriptors];

	NSPredicate *bPredicate =
    [NSPredicate predicateWithFormat:@"city == %@ and date > %@", city, startDate];

    [request setPredicate:bPredicate];

	NSError *error;
	NSMutableArray *fetchResults = [[managedObjectContext executeFetchRequest:request
                                                                        error:&error] mutableCopy] ;

	if (fetchResults == nil) {
		// handle the error
	return fetchResults;


Again, this repository class needs to be a strong retain property in caller.


I need implement the following two features in most of my iOS apps, e.g., iClip:

  • auto-play-next-episode if available
  • auto resume from the stop point last time stopped/pasued

By looking into the developers doc, the first one is easy to do, just observe the MPMoviePlayerPlaybackDidFinishNotification:


	MPMoviePlayerViewController* mpvc =[[MPMoviePlayerViewController alloc] initWithContentURL:theURL];
	[self presentModalViewController:mpvc animated:NO];

	double stoppedPoint = [[NSUserDefaults standardUserDefaults] doubleForKey:[mpvc.moviePlayer.contentURL absoluteString]];
	if (stoppedPoint > 0.0) {
		mpvc.moviePlayer.initialPlaybackTime = stoppedPoint;

	[[NSNotificationCenter defaultCenter] addObserver:self

	if (![self isOS5])
		mpvc.moviePlayer.useApplicationAudioSession = NO;

	self.mpvc = mpvc; //retain it
	[mpvc release];


// When the movie is done,release the controller.

    if ([[aNotification object] isKindOfClass:[MPMoviePlayerController class]])
        MPMoviePlayerController* theMovie=[aNotification object];
        [[NSNotificationCenter defaultCenter] removeObserver:self

        [self  dismissModalViewControllerAnimated:NO];

        theMovie = nil;
        [theMovie release];

    NSDictionary *userInfo = [aNotification userInfo];

    int reason = [[userInfo objectForKey:@"MPMoviePlayerPlaybackDidFinishReasonUserInfoKey"] intValue];
    NSLog(@"clip finished because %d. play next one if available.", reason);
    // ios 4 always return MPMovieFinishReasonPlaybackEnded even it's user exit it, a bug. don't provide play next feature for ios4 users then.
    if ([self isOS5] && reason == MPMovieFinishReasonPlaybackEnded)
        [self playNextEpisodeIfAvailable];

Notice the finished reason in iOS 4 (bug) is always MPMovieFinishReasonPlaybackEnded, I had to implement the same feature in MPMoviePlayerPlaybackStateDidChangeNotification for iOS 4 users:

    if ([[aNotification object] isKindOfClass:[MPMoviePlayerController class]])
        MPMoviePlayerController* theMovie=[aNotification object];
        if (theMovie.playbackState == MPMoviePlaybackStateStopped && theMovie.currentPlaybackTime == theMovie.playableDuration) {
            NSLog(@"clip finished!");

            if ([self isOS5]) {

                [self playNextEpisodeIfAvailable];

For second feature, saving stop point when movie exit, I also use the MPMoviePlayerPlaybackStateDidChangeNotification to catch the currentPlaybackTime, I’ve tried catch it in MPMoviePlayerPlaybackDidFinishNotification, the value is zero, too late.


Use NSPredicate in Searchable UITableView

Searchable? Not, you mean filterable. TableView supposes already have data in it, what you want to do is to provide a text box on top to accept keyword then FILTER the data in table.


Before diving into TableView/SearchBar all those UI magic, get ready to NSArray basic filtering skill.
For fitlering NSString array: //[cd] means case and diacritic insensitive lookups according to doc.

     NSPredicate* p = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", keyword];
     self.filteredList = [self.list filteredArrayUsingPredicate:p];

NSDictionary type Model lookup is also supported in NSPredicate. Given your model has key named ‘title’:

     NSPredicate* p = [NSPredicate predicateWithFormat:@"title contains[cd] %@", keyword];
     self.filteredList = [self.list filteredArrayUsingPredicate:p];

Now, back to UI magic.


From O’Reilly book Programming iOS4 by Matt Beuburg:

The first question is how to make the search filed (textbox) appear along with the table view, same as Apple’s own app such as the Contracts app. (Indeed, this is such a common arrangement that if you drag a UISerachBar onto a UITableView in a nib, the search field becomes the table’s header view and a UISearchDisplayController is created for you automatically.) Another feature of Apple’s standard interface is that the search field isn’t initially showing. To implement this, we scroll to the first actual row of data when the table view appears.

To create all of those by code:

    UISearchBar* b = [[UISearchBar alloc] init];
    [b sizeToFit];
    b.delegate = self;
    [self.tableView setTableHeaderView:b];
    [self.tableView reloadData];
    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
                          atScrollPosition:UITableViewScrollPositionTop  animated:NO];
    UISearchDisplayController* c = [[UISearchDisplayController alloc] initWithSearchBar:b contentsController:self];
    [b release];
    self.sbc = c;  // property type of UISearchDisplayController
    c.delegate = self;
    c.searchResultsDataSource = self;
    c.searchResultsDelegate = self;
    [c release];

In UITableview datasource code, switch to

(tableView == sbc.searchResultsTableView)? [self.filteredList count] : [self.list count];

Implement searchBarSearchButtonClicked method:

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
    [self filterArrayBy:searchBar.text];
    [self.sbc.searchResultsTableView reloadData];


- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
    [self filterArrayBy:searchText];

- (void)filterArrayBy:(NSString*) keyword
    // title is a key in my ArrayListData(NSDictionary), for pure NSString filtering, use SELF instead
    NSPredicate* p = [NSPredicate predicateWithFormat:@"title contains[cd] %@", keyword];
    self.filteredList = [self.list filteredArrayUsingPredicate:p];
    [self.sbc.searchResultsTableView reloadData];


// to solve the problem of list becomes empty whenever user starts typing
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
    self.filteredList = self.list;

// live search
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
    [self filterArrayBy:searchText];


Pictures are from this post, but the technology used here is a little bit different.

My 1st iOS app is on AppStore now

It’s called iGameTimer2. I messed up the bundle name at first time, had to delete it and start over, then apple told me iGameTimer complain that iGameTimer is already in use. They did warn me when I was trying to delete the old bundle, the app name will stay there for ever, even after I delete it.

OK, I had to call it iGameTimer2 when re-applying for bundle. Whatever.

I can’t believe it that there is no time related screen saver, or kind of prent control tool in iOS. So I created this app using local notification to simulate the screen locker. It is gift for my 6-year-old smart son, who is a master to all the games on my iPad, and won’t stop playing them unless I physically took it away from him.

This won’t happy any more, here is the tool for him, for both of us. Here is the link for my first iOS app.

Tweet me @_Frank_Mao to get a promo code for it, you have to use iTunes to redeem it,

CoreData: constraint failed?

I got an error exactly same as this post described.

Followed the hint, sqlite3 open database,

  1. .tables
    command will echo a table named Z_PRIMARYKEY
  2. .schema Z_PRIMARYKEY
    there is a column called Z_MAX with type INTEGER
  3. select * from Z_PRIMARYKEY;
    remember this value.
  4. select max(Z_PK) from ZSTORE;
    I got different max value, because I did some operation outside of iOS.
  5. update Z_PRIMARYKEY set Z_MAX=…;

Problem solved. My concern is, will this kind of error happen in production enviroment?