iOS | How - To Share Data Between View Controllers using a Singleton

Sharing data between iOS objects like View Controller can be as equally a challenge as any other programming platform. Global variables, #define constants, pointers, protocols are all excellent solutions. Of course there are numerous other solutions, some unique to each environment and which takes advantage of some unique feature of that particular language or SDK. In my previous tutorial, iOS 5 | How-To Use Global Variables with the AppDelegate, I demonstrated how to create and use global variables through the AppDelegate. In this tutorial will focus on the Singleton.

This design has been implemented across many if not all programming platforms for its ease of design and functionality.

A Singleton is a special class that can only be created and instantiated once in a running app. If you try to create another instance of the class, the Singleton will return itself. A Singleton is lazily created and cannot be copied or released until a process is terminated. The Singleton’s globalness is achieved through the factory method. In iOS there are several classes that are singletons like the NSFileManager.

To create and use a singleton follow these steps:

Create the Singleton

  • Create a NSObject class in a project of your choice
  • In the header define a NSString property and a instance method as in listing 1. The singleObj method must return an instance of itself.

Listing 1

@interface singletonObj : NSObject


@property(nonatomic, strong) NSString * gblStr;

+(singletonObj *)singleObj;

@end

The gblStr is a property of the Singleton class that will be used to hold any string data from any of the views. Of course you could replace this with any other data type or even have several properties with different data types depending on the needs of your app.
For the header that covers it. The rest of Singleton class work is in the implementation.
Have a look at listing 2 below. As usually create the getter and setter property using the @synthesize directive.
Next is the body of the singleObj method. This is where the Singleton actually takes shape. Create a static singleObj variable and set it to nil which always be an instance of itself. Next use the @synchronized which will synchronize the different threads of the Singleton object so that our object variable, glbStr will always that an instance of itself and return the desired information no matter where you are.
Next we check to see if the Singleton object already exists, if it doesn’t create it otherwise return the existing one. That is it for the Singleton. It is a very simple design pattern. Next I will demonstrate how to implement it to pass data between two view controllers.

Listing 2

Listing 2
@implementation singletonObj
@synthesize gblStr;

+(singletonObj *)singleObj{
    
    static singletonObj * single=nil;
    
    @synchronized(self)
    {
        if(!single)
        {
            single = [[singletonObj alloc] init];

        }
        
    }    
    return single;
}
@end

Sample Test Project

For this example, you will need two view controllers at least, more is ok too. Lay out the view controllers as in Figure 1 and connect them both with a Tab Bar Navigator.
Next open the header file of the first view controller, klViewController, and create a Singleton variable called, sobj. We will initialize this variable in the implementation viewDidLoad method.


The two other properties are created by adding IBOutlets through the storyboard. One of the IBOutlet is a UILabel to display the value of the sobj, the other is the UITextField.The same goes for the IBAction. See listing 3 below.

Figure 1 - Lay out the UI Scenes
Figure 1 - Lay out the UI Scenes

Listing 3

#import <UIKit/UIKit.h>
#import "singletonObj.h"


@interface klViewController : UIViewController
{
    singletonObj * sobj;
  
}

- (IBAction)addToSingleton:(id)sender;

@property (strong, nonatomic) IBOutlet UILabel *displayValue;

@property (strong, nonatomic) IBOutlet UITextField *enterValue;

@end

In the implementation file, there is no need to synthesize the Singleton variable because it’s not a property with a getter/setter.


Initialize the Singleton variable in the viewDidLoad method as in listing 4. This method gets called when the app is first loaded. Every time you transition from one view to another this method never will get called except on first load. Subsequent processing of the view controller will need to be done in the viewWillAppear or the viewWillDisappear methods. We will these methods to access the sobj variable and its value as we transition from one view controller to the next and back.

Listing 4

- (void)viewDidLoad
{
    [super viewDidLoad];
     sobj = [singletonObj singleObj];
}

In the addToSingleton method, listing 5, I assign the value of the UITextField, enterValue to the sobj variable. Since we are using the @synchronize directive, this value will be carried forward to other objects or until it is replaced by another value. The resignFirstRespsonder is there to dismiss the keyboard.

Listing 5

- (IBAction)addToSingleton:(id)sender {
    sobj.gblStr = [self enterValue].text;
    [enterValue resignFirstResponder];
}

The Second View Controller

To test the code add another view controller to the canvas and connect it to the Tab Bar Navigator controller. Add a UITextField to enter data and an UILabel to display data from the Singleton variable. Once the UI is laid out, make your IBOutlets and connect them. Your Scene should look like Figure 2.

Figure 2 - The Second View Controller
Figure 2 - The Second View Controller

You are also going to need a second custom UIViewController for your view controller in the storyboard. Create a new custom UIViewController class as a subclass of a UIViewController class. Once the class is create, open the storyboard and select the second view controller. Activate the Identity inspector and add the custom UIViewController class to the view controller.

To receive and display the value from the sobj Singleton variable, you will need to add a variable for the Singleton but this will only return the same object that was created in the first view controller as in Listing 6.

Listing 6

@interface SecondViewController : UIViewController
{
    singletonObj * anotherSingle;
}
@property (strong, nonatomic) IBOutlet UITextField *enterAnotherValue;
@property (strong, nonatomic) IBOutlet UILabel *displayValues;

@end

In the implementation file, the three main methods are viewdidLoad, viewWillAppear and viewWillDisappear. The viewDidLoad method will set the Singleton variable to itself so we will have access to the Singleton from the first view controller, listing 7. This method is only called the first time the view is loaded. Immediately after the viewDidLoad method is called, the viewWillAppear method is called just before the view controller is displayed on screen, listing 8. This method will assign the value of the gblStr to the UILabel: displayValues. The variable will contain the value from the first view controller.

Listing 7

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    anotherSingle = [singletonObj singleObj];
    
}

Listing 8

-(void)viewWillAppear:(BOOL)animated
{
    [self displayValues].text = anotherSingle.gblStr;
}

If you enter a value in the UITextField on the second view controller and click on the first tab to display to first view controller, the value of the enterAnotherValue field will be assigned to the Singleton variable gblStr as the view will be replaced by the first view controller when the viewWillDisappear method is called. See listing 9 below.

Listing 9

-(void)viewWillDisappear:(BOOL)animated
{
    anotherSingle.gblStr = [self enterAnotherValue].text;
}

When the first view controller appears, the UILabel will contain the variable from the second view controller. Passing data between multiple view controllers is very easy using a Singleton and the @synchronize directive. Using a Singleton is safer than using a variable in the AppDelegate and it is easier to manage the values.

Singleton header

//
//  singletonObj.h
//  SingletonApp
//
//  Created by Kevin Languedoc on 4/2/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface singletonObj : NSObject


@property(nonatomic, strong) NSString * gblStr;

+(singletonObj *)singleObj;

@end

Singleton Implementation

//
//  singletonObj.m
//  SingletonApp
//
//  Created by Kevin Languedoc on 4/2/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import "singletonObj.h"

@implementation singletonObj
@synthesize gblStr;

+(singletonObj *)singleObj{
    
    static singletonObj * single=nil;
    
    @synchronized(self)
    {
        if(!single)
        {
            single = [[singletonObj alloc] init];

        }
        
    }    
    return single;
}
@end

klViewController Header

//
//  klViewController.h
//  SingletonApp
//
//  Created by Kevin Languedoc on 4/2/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "singletonObj.h"


@interface klViewController : UIViewController
{
    singletonObj * sobj;
  
}

- (IBAction)addToSingleton:(id)sender;

@property (strong, nonatomic) IBOutlet UILabel *displayValue;

@property (strong, nonatomic) IBOutlet UITextField *enterValue;

@end

klViewController.m Implementation

//
//  klViewController.m
//  SingletonApp
//
//  Created by Kevin Languedoc on 4/2/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import "klViewController.h"


@implementation klViewController
@synthesize displayValue;
@synthesize enterValue;





- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
     sobj = [singletonObj singleObj];
          
  
}

- (void)viewDidUnload
{
   
 
    [self setDisplayValue:nil];
    [self setEnterValue:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}
-(BOOL)textFieldShouldReturn:(UITextField *)textfield{
    
    [textfield resignFirstResponder];
    
    return YES;
}
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];    
    [self displayValue].text = sobj.gblStr;
}

- (void)viewWillDisappear:(BOOL)animated
{
	[super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
	[super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (IBAction)addToSingleton:(id)sender {
    
        
    sobj.gblStr = [self enterValue].text;
    
    [enterValue resignFirstResponder];

}
@end

SecondViewController.h Header

//
//  SecondViewController.h
//  SingletonApp
//
//  Created by Kevin Languedoc on 4/2/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "singletonObj.h"

@interface SecondViewController : UIViewController
{
    singletonObj * anotherSingle;
}
@property (strong, nonatomic) IBOutlet UITextField *enterAnotherValue;

@property (strong, nonatomic) IBOutlet UILabel *displayValues;

@end

SecondViewController.m Implementation

//
//  SecondViewController.m
//  SingletonApp
//
//  Created by Kevin Languedoc on 4/2/12.
//  Copyright (c) 2012 Kevin Languedoc. All rights reserved.
//

#import "SecondViewController.h"
#import "singletonObj.h"

@implementation SecondViewController

@synthesize enterAnotherValue;
@synthesize displayValues;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (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.
}






// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    anotherSingle = [singletonObj singleObj];
    
}
-(void)viewWillAppear:(BOOL)animated
{
    [self displayValues].text = anotherSingle.gblStr;
}

-(void)viewWillDisappear:(BOOL)animated
{
    anotherSingle.gblStr = [self enterAnotherValue].text;
}

- (void)viewDidUnload
{
    //viewDidUnload not called when navigating from one view to another
     //anotherSingle.gblStr = [self enterAnotherValue].text;
    [self setDisplayValues:nil];
    [self setEnterAnotherValue:nil];
    [super viewDidUnload];

}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(BOOL)textFieldShouldReturn:(UITextField *)textfield{
    
    [textfield resignFirstResponder];
    
    return YES;
}
@end

More by this Author


Comments 20 comments

korutech profile image

korutech 4 years ago from Canberra, Australia

This is a nice tutorial Kevin. What do you believe are the advantages of using a singleton vs. a pointer the app maintains the data for?


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Thanks korutech. I don't think there is much of a difference between the two. There is however the problem of access to the data by a certain object. How do we make sure that the object that is accessing the pointer or singleton is using the right data. In this tutorial, like the one on global variables, the example is simple and controlled, but isn't always the case.

I guess the point I am trying to make is that there is time and place for each and not necessarily to use one over the other. Apple recommends using protocols for transferring data between view controllers, however this is much good if the transfer or sharing of data doesn't involve VCs.


korutech profile image

korutech 4 years ago from Canberra, Australia

The other problem with protocols, depending on how you implement them, is that the delegate is not necessarily "the droid you are looking for". I had a subclassed VC that implemented an optional method. Although not ideal, I needed the data object to check the delegate it had was the one that implemented the optional protocol. There are numerous ways to skin that cat, like adding an empty method to the superclass, but circumstances and implementation don't always make that approach suitable.

If I'm not mistaken, I've always understood you need to be very certain about using singletons, as they can severely limit your options down the track when you suddenly realise you might need two instances of the object. I'd be interested in your views on that aspect.


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

You are right about Singletons and for that matter Global Variables in general. for a small app, they are easily manageable but once you get into more complex apps with complicated workflows, you will need better designs. I guess you got to trust your experience and use the right tool to get the job done...just a carpenter. Not everything will work every time and shouldn't be used as a catch all.


korutech profile image

korutech 4 years ago from Canberra, Australia

Good call Kevin. I think one of the traps that are easy to fall into when you're first starting out, and struggling to understand how it all works, is that you see examples or tutorials, and don't necessarily realise the approach is one of many, vs. "the" one and only way to do something. The series of articles you have on sharing data between views is good in the sense that they illustrate a number of ways of achieving the same thing using different approaches.

In the book you're writing, I think it will be very constructive to include these various methods so readers understand there is no single "right or wrong" approach.


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Thanks, I hope I can live up to those expectations. The book is a very different beast but is my intention to give as much options as possible when discussing solutions.


Madhu 4 years ago

Thanx Man such a great explanation,....


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Thank you Madhu


lfortes 4 years ago

Not working in xcode 4.3.2

@interface klViewController : UIViewController

{

singletonObj * sobj;

}

Gives error: Cannot declare variable inside @interface or @protocol.

any clues?

Thanks.


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

try adding the @class singletonObj. Also, and may sound like a stupid question, did you add your import statement?


MANUEL 4 years ago

Really nice tutorial.. everything well explain.

I want to pass the data from a view controller to a view... do I use the same method? is because in the view (graphview) is a subview on top of another view showing the differences and I just want to link the bars to the values of the view controller... do I use the same method?


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

Yes the singleton will work in this case. Thanks for the nice feedback. Always appreciated.


MANUEL 4 years ago

but since is a view... this part shows me errors ...

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

if (self) {

// Custom initialization

}

return self;

}

what can I put instead of this?


klanguedoc profile image

klanguedoc 4 years ago from Canada Author

what is the error msg you are getting


Deniz 3 years ago

hi, it helps so much. I have small question. I used your way in my project. I have 2 view controller. lets say viewControllerA and viewControllerB.

When I try to click on button opening viewControllerB. Also when i click button(in viewControllerA) I add some data to sobj.glbalstr. opening view ControllerB but not show the data of sobj.globalstr. but when i try second time it show. Why is that? thanks again


klanguedoc profile image

klanguedoc 3 years ago from Canada Author

Hi Deniz,

I believe that when you enter the data the first time, the variable is not properly set globally when the viewcontroller B is move unto the stack. When you try a second, the variable is already set so its value gets transferred.

K


Upendra 2 years ago

That is Cool I was searching For A week For This Method And Thak you Very much


klanguedoc profile image

klanguedoc 2 years ago from Canada Author

Glad I was able to help


Outdated 8 months ago

This doens't work anymore, there are many things to consider, it doesn't scale up. What happen if you have a list of item and that list is used in different controllers, you need to refresh each row when change or delete. I am testing with 10.000 rows and I am having performance issues, of course I am using a much better solution but still performance issues. I am querying directly 40.000 rows to sqlite and it is much faster than creating a singleton.


klanguedoc profile image

klanguedoc 8 months ago from Canada Author

The idea behind the tutorial is to teach the concept. I have several tutorials on SQLite as well as a book

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article
    working