Wednesday, August 15, 2012

How to Pass Data Between UIViewControllers: The Global Variable & The Singleton Class

Say what now?

Global? Singleton? Isn't that the one who married Prince William? No. That would be Ms Middleton.


Noobies like us tend to get intimidated by these names because they sound so complicated.  But the truth is, it is quite simple. When you create an app, often they will have more than 1 ViewControllers - for example a ViewController, where all the main thingy happens. Then there is a Settings button that will bring you to another view, say SecondViewController that changes some of the state of the component in ViewController.

So how do you pass that state back to the ViewController?



GLOBAL VARIABLES

The simple, but crude way is to use Global Variables. Global Variables can get messy because once you use a certain name as global variable, you can not use it again at ANY OTHER PLACES. And this can get confusing when you have many lines of codes and more than one class.

A quick example of Global Variable Declaration is as follows:

In your header (.h) file, input the "allocation" of the variable.




#import <UIKit/UIKit.h>;

extern BOOL MYGlobalVariable;

@interface BattleGameViewController : UIViewController 

@end;



"extern" does not declare it. What it does is just "allocate" a memory for that particular variable. Therefore to declare it, one need to declare in the implementation file (*.m) as follows:



#import "MyAppHeader.h"

@implementation BattleGameViewController

BOOL MYGlobalVariable = NO;


So to use this variable in another class, all you need to do is import "MyAppHeader.h" and MYGlobalVariable should be ready to be used.

It works well, in fact, I also used it in some of my early apps. That is until I learn about Singleton.

SINGLETON

Believe it or not, MOST of us NOOBIES already used Singleton. The most common Singleton we use is.... *drumroll*... NSUserDefaults!

Do you like NSUserDefaults? I do. It is so easy and simple. And that is why you also should use Singleton in variable aspects of your app. It will make your coding very nice and easy to maintain.

How to implement a Singleton? I am no ObjC Guru, that much I can tell you, but hey, why reinvent the wheel, when some gurus have created the Singleton class for you? There are loadsssss of ways to write a Singleton Class, and you can find them here:

StackOverflow: Samples of Singletons

Anyway, here is MY singleton class. I used this Singleton class in one of my latest app - Clock Stand for iPad. I stripped off all the other variables and leave just 1 for example so that it is clear for you to see how to implement it.


//
//  MySingletonCenter.h
//  ClockForiPad
//
//  Created by Emir Fithri Samsuddin on 6/20/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface MySingletonCenter : NSObject {
    
// Your variables go here
// here's one example:
  BOOL is24Hour;
}

// Your property settings for your variables go here
// here's one example:
@property (nonatomic, assign) BOOL is24Hour;

// This is the method to access this Singleton class
+ (MySingletonCenter *)sharedSingleton;

@end

And the implementation file (*.m)


//
//  MySingletonCenter.m
//  ClockForiPad
//
//  Created by Emir Fithri Samsuddin on 6/20/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "MySingletonCenter.h"


@implementation MySingletonCenter

static MySingletonCenter *shared = NULL;

@synthesize is24Hour;

- (id)init
{
    if ( self = [super init] )
    {
       // init values 
       // here you assign initial values to your variable.
       // in my case, I save all these values into NSUserDefaults as users preference.
       // so i do the necessary things to ensure that happens.
       
        NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
        if ([def objectForKey:@"is24Hour"]==nil) {
        
            is24Hour = NO;
            [def setBool:self.is24Hour forKey:@"is24Hour"];
            [def synchronize];
            
        } else {
  
            // read from NSUserDefaults
            self.is24Hour = [def boolForKey:@"is24Hour"];
        }
    }
    return self;
    
}

+ (MySingletonCenter *)sharedSingleton
{
    @synchronized(shared)
    {
        if ( !shared || shared == NULL )
        {
            // allocate the shared instance, because it hasn't been done yet
            shared = [[MySingletonCenter alloc] init];
        }
        
        return shared;
    }
}

- (void)dealloc
{
    NSLog(@"Deallocating singleton...");

    
    [super dealloc];
}



@end

Now, to create the Singleton Class, all you need to do is Right Click on the left panel of XCode and Choose Add File... and Choose Objective-C Class. Give a proper name (for eg, MySingletonCenter.h and MySingletonCenter.m) to it, select NSObject as its class and add to project. After that just copy and paste the above Singleton code into the corresponding .h and .m. And you're set to go.

To use Singleton in another class is easy.. in fact super easy just like NSUserDefaults.

1. #import "MySingletonCenter.h"

2. Everytime you want to read or write to it:

MySingletonCenter *tmp = [MySingletonCenter sharedSingleton];

// write
tmp.is24Hour = YES;

// read
BOOL new = tmp.is24Hour;


SUPER EASY! WHAT UP! Note: There is no sample project for this tutorial because it is just too easy. Do shout in the comments box if you need a sample project though. Note: Due to requests, I made a downloadable sample project for this tutorial! Thank you so much for your interest and comments!

8 comments:

  1. Post more dummy tutorials!
    I'm your fan. no seriously.

    ReplyDelete
  2. Hi Dro,

    Thanks for your comment.
    A new tutorial is coming soon. :)

    ReplyDelete
  3. I can't get your tutorial to work. I guess it is best to never underestimate the confusion of a noobie. I have a multi-view app that worked very well before I started this tutorial, and I did my best to triple-check all the steps you described. I'm pretty sure I made no mistakes in the MySingletonCenter .h and .m files. All I changed was my variable names - (both are BOOLs).

    But there was a little ambiguity over where to add the statement

    #import "MySingleton.h"

    which I think should actually be

    #import "MySingletonCenter.h"

    I added it to my views .h files.

    And also I had to guess where to insert the read and write code - I tried putting it into IBAction routines in my views .m files. I got a fatal error on every line that referred to the variables I thought were created in MySingletonCenter.h and MySingletonCenter.m saying "Use of undeclared identifier 'variableName'". I'll try initializing these variables the usual way in each view, but now I may end up with multiple variable definition errors. So I hope you'll create a sample project with multiple views and post it. Thanks.

    ReplyDelete
  4. Very clear explanation, and the tutorial worked great. Thanks!

    ReplyDelete
  5. Thanks all for your nice words! :D

    Ricardocanada, you are correct on the #import "MySingletonCenter.h".
    Anyway, not sure you are still interested or not, but I have made a downloadable sample project and it is available for download in the blog post.

    Thank you all!

    ReplyDelete
  6. how would you refresh the data if it's changed in the NSUserData file?

    ReplyDelete
  7. Your sir are now my personal jesus christ! Cheers! :)

    ReplyDelete
  8. Noobies like us tend to get intimidated by these names because they sound so complicated. But the truth is, it is quite simple. Now its time to avail power only dispatch servicesfor more details.

    ReplyDelete