MyCodetips Featured passing data objc

Passing data between view controllers in Objective-C

Passing data between view controllers in Objective-C

Passing data between view controllers,When designing an app that makes use of multiple view controllers it may become necessary to pass data back and forth between the view controllers. Passing data to the next view controller as well as passing data back to the previous view controller.

There are multiple options for passing data between view controllers.

  • Using Navigation Controller Push
  • Using Segue
  • Using Delegate
  • Using Notification Observer
  • Using Block
  • Saving in NSUserDefaults – for accessing it later
  • Singleton classes
  • Databases and other storage mechanisms, like p-list files, etc.

Passing Data Forward

Passing data forward to a view controller from another view controller. If you wanted to pass an object/value from one view controller to another view controller that you may be pushing on to a navigation stack.

We will have ViewControllerA and ViewControllerB

To pass a BOOL value from ViewControllerA to ViewControllerB we would do the following.

in ViewControllerB.h create a property for the BOOL

@property (nonatomic, assign) BOOL isSomethingEnabled;

in ViewControllerA you need to tell it about ViewControllerB so use below

#import "ViewControllerB.h"

Then where you want to load the view, for example, didSelectRowAtIndex or some IBAction, you need to set the property in ViewControllerB before you push it onto the navigation stack.

ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.isSomethingEnabled = YES;
[self pushViewController:viewControllerB animated:YES];

This will set isSomethingEnabled in ViewControllerB to BOOL value YES.

Passing Data Forward using Segues

If you are using Storyboards you are most likely using segues and will need this procedure to pass data forward. This is similar to the above but instead of passing the data before you push the view controller, you use a method called

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

So to pass a BOOL from ViewControllerA to ViewControllerB we would do the following:

in ViewControllerB.h create a property for the BOOL

@property (nonatomic, assign) BOOL isSomethingEnabled;

in ViewControllerA you need to tell it about ViewControllerB, so use an

#import "ViewControllerB.h"

Create the segue from ViewControllerA to ViewControllerB on the storyboard and give it an identifier. In this example we’ll call it “showDetailSegue”

Next, we need to add the method to ViewControllerA that is called when any segue is performed. Because of this we need to detect which segue was called and then do something. In our example, we will check for “showDetailSegue” and if that’s performed, we will pass our BOOL value to ViewControllerB

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"showDetailSegue"]){
ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
controller.isSomethingEnabled = YES;
}
}

This will set isSomethingEnabled in ViewControllerB to BOOL value YES.

Passing Data Back

To pass data back from ViewControllerB to ViewControllerA you need to use Protocols and Delegates or Blocks, the latter can be used as a loosely coupled mechanism for callbacks.

To do this we will make ViewControllerA a delegate of ViewControllerB. This allows ViewControllerB to send a message back to ViewControllerA enabling us to send data back.

For ViewControllerA to be a delegate of ViewControllerB it must conform to ViewControllerB’s protocol which we have to specify. This tells ViewControllerA which methods it must implement.

In ViewControllerB.h, below the #import, but above @interface you specify the protocol.


@class ViewControllerB;

@protocol ViewControllerBDelegate <NSObject>
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
@end

Next still in the ViewControllerB.h, you need to set up a delegate property and synthesize in ViewControllerB.m

@property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
In ViewControllerB we call a message on the delegate when we pop the view controller.

NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];

That's it for ViewControllerB. Now in ViewControllerA.h, tell ViewControllerA to import ViewControllerB and conform to its protocol.

#import "ViewControllerB.h"

@interface ViewControllerA : UIViewController <ViewControllerBDelegate>
In ViewControllerA.m implement the following method from our protocol

- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(@"This was returned from ViewControllerB %@", item);
}

Before pushing viewControllerB to navigation stack we need to tell ViewControllerB that ViewControllerA is its delegate, otherwise we will get an error.

ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.delegate = self
[[self navigationController] pushViewController:viewControllerB animated:YES];

NSNotification center

It’s another way to pass data.



// Add an observer in controller(s) where you want to receive data
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];

-(void) handleDeepLinking:(NSNotification *) notification {
id someObject = notification.object // Some custom object that was passed with notification fire.
}

// Post notification
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];

Passing Data by Blocks

Define a block

@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h

Add block handler (listener)

Where you need a value (for example, you need your API response in ControllerA or you need ContorllerB data on A)

// In Contoller A

- (void)viewDidLoad {
[super viewDidLoad];
__unsafe_unretained typeof(self) weakSelf = self;
self.selectedVoucherBlock = ^(NSString *voucher) {
weakSelf->someLabel.text = voucher;
};
}

In Controller B

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
[self.navigationController pushViewController:vc animated:NO];


Call block

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath {
NSString *voucher = vouchersArray[indexPath.row];
if (sourceVC.selectVoucherBlock) {
sourceVC.selectVoucherBlock(voucher);
}
[self.navigationController popToViewController:sourceVC animated:YES];
}

using Singleton Class

A singleton is an instance of a class, that instance being the only instance in existence during its lifetime. A singleton gets its name from the fact that it is the single instance. Normally developers who use singletons have special class methods for accessing them.

+ (SingletonClassManager*) sharedManager; {
    static dispatch_once_t onceQueue;
    static SingletonClassManager* _sharedInstance;

    // dispatch_once is guaranteed to only be executed
    // once in the lifetime of the application
    dispatch_once(&onceQueue, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

using NSUserDefaults

You can always share data using NSUserDefaults. Set the value you want to share with respect to a key of your choice and get the value from NSUserDefault associated to that key in the next view controller.

[[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
[[NSUserDefaults standardUserDefaults] objectForKey:key]

You can just create a property in viewcontrollerA. Create an object of viewcontrollerA in viewcontrollerB and assign the desired value to that property.

You can also create custom delegates for this.

using plist

user can get data from storage mechanism like plist

Reading the property list

The property list is read in as Data and then converted to an NSArray:

NSString *path = [[NSBundle mainBundle] pathForResource:@"Factors" ofType:@"plist"];
NSData *plistData = [[NSData alloc] initWithContentsOfFile:path];
NSPropertyListFormat format;
NSString *error = nil;
NSArray *factorData = (NSArray *)[NSPropertyListSerialization
                                  propertyListFromData:plistData
                                  mutabilityOption:NSPropertyListImmutable
                                  format:&format
                                  errorDescription:&error];



  for (int i = 0; i < 10; i++) {
  NSArray *factorList = (NSArray *)[factorData objectAtIndex:i];
  NSLog(@"Factors of %d\n", i + 1);
  for (int j = 0; j < [factorList count]; j++) {
    NSLog(@"  %d\n", (NSNumber *)[factorList objectAtIndex:j]);
  }
}

Happy Coding 🙂

Scroll to Top

Discover more from CODE t!ps

Subscribe now to keep reading and get access to the full archive.

Continue reading