Advertise here




Advertise here

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Sharing data between view controllers and other objects - link fixed

Duncan CDuncan C Posts: 9,034Tutorial Authors, Registered Users @ @ @ @ @ @ @
edited August 2012 in iPhone SDK Development
At least a couple of times a week somebody posts a question something like this:

My app has 2 view controllers, and I need to get information from the user in one view controller, and pass it to a second view controller. How do I do that?

More generally, the question is, what's the best way to share information between different objects in your app?

I got tired of answering this question repeatedly, and decided to write a sample app that shows how to do it. The idea is to create a single data container object that is a property of the app delegate, and make that data object available to every object in your app that needs it.

Here's what I did:

-I created a generic data container class AppDataObject. This is an ancestor class for a data container object that you would actually use to hold your application's global variables.

-At init time, I added code to the app delegate that create an empty data container object.

-I created a dirt simple protocol, AppDelegateProtocol. This protocol only lists one method, -theAppDataObject. That method lets you ask the app delegate for it's data object.

-I created a subclass of the AppDataObject that actually holds data for a real app. This is where you put the information that you want to share between objects.

Using a protocol means that the only things the objects in your app need to include are the AppDelegateProtocol and the header for your subclass of the app data object. You don't have to #include the header of your app delegate, which makes for good separation between the different objects in your app.

I have posted a complete project, ViewControllerDataSharing, that uses the above method to share information between view controllers. The project uses a navigation controller, and has a root view controller and a second view controller. Each view controller has a UITextView and a slider. The app uses an AppDataObject to pass the value of these fields between view controllers.

I've posted the project to my public iDisk. You should be able to download it simply by clicking the link below:


Download the ViewControllerDataSharing project.

Note that I updated the link above. The old link broke when Apple shut down their dot mac service.
Post edited by Duncan C on
Regards,
Duncan C
WareTo

widehead.gif
Animated GIF created with Face Dancer, available for free in the app store.

I'm available for one-on-one help at CodeMentor
«13

Replies

  • iSDKiSDK Posts: 1,353Tutorial Authors, Registered Users
    edited July 2010
    i wasnt goanna ask but this is exactly what i needed thanks Dunc
  • JedJed Posts: 26Registered Users
    edited July 2010
    Wow! This is awesome. My next project won't be a patched together mess. Thank you x100!
  • HodjiHodji Posts: 20Registered Users @
    edited August 2010
    error: synthesized property 'float1' must either be named the same as a compatible ivar or must explicitly name an ivar
  • ataranlenataranlen Plano, TexasPosts: 67Registered Users @
    edited August 2010
    Wouldn't it be easier to use NSUserDefaults to simply save and retrieve data across viewControllers? That way you would also already have a basic data saving and retrieval method for loading saved games.
    Nathan Stoltenberg
    Lead Admin
    Minecraft Server http://www.MineTexas.com
  • BrianSlickBrianSlick Treadmill Desk Ninja Posts: 10,390Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited August 2010
    ataranlen wrote: »
    Wouldn't it be easier to use NSUserDefaults to simply save and retrieve data across viewControllers? That way you would also already have a basic data saving and retrieval method for loading saved games.

    If your data includes custom classes, this will not work. You'd have to convert those objects to something that could go into NSUserDefaults - like a dictionary - and then convert back on the other end. Not worth the effort, and will be significantly slower than simply passing a reference.
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • Duncan CDuncan C Posts: 9,034Tutorial Authors, Registered Users @ @ @ @ @ @ @
    edited August 2010
    ataranlen wrote: »
    Wouldn't it be easier to use NSUserDefaults to simply save and retrieve data across viewControllers? That way you would also already have a basic data saving and retrieval method for loading saved games.

    Sorry, but using a disk based persistent store to exchange in-memory values between objects in a running program seems really, really dumb.

    As a friend of mine says, it "seems like a reaching around your elbow to scratch your butt way to go."

    All you need to do is have one view controller have a pointer to the other, or better yet, to a shared data container object.

    As Brian explains later, userDefaults also doesn't work for anything other than "plist" data structures (NSDictionaries, NSArrays, NSNumbers (and other NSValue objects) and NSData.)
    Regards,
    Duncan C
    WareTo

    widehead.gif
    Animated GIF created with Face Dancer, available for free in the app store.

    I'm available for one-on-one help at CodeMentor
  • ataranlenataranlen Plano, TexasPosts: 67Registered Users @
    edited August 2010
    At this point I'm not sharing that sort of data (CGPoints for example), so It didn't make sense to take the extra work to create a data class for things easily stored such as strings, urls, integers, and arrays. This is an amazing solution for more complicated sharing between objects. Thanks for opening my eyes.
    Nathan Stoltenberg
    Lead Admin
    Minecraft Server http://www.MineTexas.com
  • mickmmickm Posts: 289Registered Users
    edited August 2010
    why is the object released immediately after it is instantiated?
    - (id) init;
    {
    	self.theAppDataObject = [[ExampleAppDataObject alloc] init];
    	[theAppDataObject release];
    	return [super init];
    }
    
  • Duncan CDuncan C Posts: 9,034Tutorial Authors, Registered Users @ @ @ @ @ @ @
    edited August 2010
    mickm wrote: »
    why is the object released immediately after it is instantiated?
    - (id) init;
    {
    	self.theAppDataObject = [[ExampleAppDataObject alloc] init];
    	[theAppDataObject release];
    	return [super init];
    }
    


    The property self.theAppDataObject is set up as a retained property.

    So, assigning something to that property causes it to be retained.

    Thus, I release it so ONLY the property retains it. If I set the property to nil, the object gets released.
    Regards,
    Duncan C
    WareTo

    widehead.gif
    Animated GIF created with Face Dancer, available for free in the app store.

    I'm available for one-on-one help at CodeMentor
  • mickmmickm Posts: 289Registered Users
    edited August 2010
    Duncan C wrote: »
    The property self.theAppDataObject is set up as a retained property.

    So, assigning something to that property causes it to be retained.

    Thus, I release it so ONLY the property retains it. If I set the property to nil, the object gets released.

    retain + retain - release = 1 retain

    just wasn't thinking about the property.

    thanks.
  • Duncan CDuncan C Posts: 9,034Tutorial Authors, Registered Users @ @ @ @ @ @ @
    edited August 2010
    Hodji wrote: »
    error: synthesized property 'float1' must either be named the same as a compatible ivar or must explicitly name an ivar


    Oops. My mistake.

    The .h file for ExampleDataObject is missing an instance variable. It should look like this:


    @interface ExampleAppDataObject : AppDataObject 
    {
    	NSString*	string1;
    	NSData*		data1;
    	NSInteger	int1;
    [B]	CGFloat		float1;
    [/B]}
    
    @property (nonatomic, copy) NSString* string1;
    @property (nonatomic, retain) NSData* data1;
    @property (nonatomic) CGFloat	float1;
    
    @end
    

    (The missing line is marked in bold)
    Regards,
    Duncan C
    WareTo

    widehead.gif
    Animated GIF created with Face Dancer, available for free in the app store.

    I'm available for one-on-one help at CodeMentor
  • DaveFDaveF Posts: 35Registered Users
    edited August 2010
    Nice and clean. However my singleton pattern works like a dream. I know it's not the cleanest way to go and I'll try to get into your way of coding as it seems nice but it'll be painful to drop my singleton! So simple...
  • BrianSlickBrianSlick Treadmill Desk Ninja Posts: 10,390Tutorial Authors, Registered Users @ @ @ @ @ @ @ @
    edited August 2010
    DaveF wrote: »
    Nice and clean. However my singleton pattern works like a dream. I know it's not the cleanest way to go and I'll try to get into your way of coding as it seems nice but it'll be painful to drop my singleton! So simple...

    Nobody says you need to. I use singletons all the time. Every major app I've done has a DataController class.
    BriTer Ideas LLC - WWW | Facebook | Twitter | LinkedIn - Professional iOS App Development. Available for hire.
    I can provide personalized help at codementor.io

    SlickShopper 2 | Leave a PayPal donation

    Free Xcode Tools: NSLog Utility | Getter Utility
    GitHub Projects: BTIKit | BTICoreDataKit

    Are you a newbie? Things you should read:
    Definitive Guide To Properties | UITableView Series | A Model (Object) Is A Beautiful Thing
  • DaveFDaveF Posts: 35Registered Users
    edited August 2010
    BrianSlick wrote: »
    Nobody says you need to. I use singletons all the time. Every major app I've done has a DataController class.
    Yeah I've never understood why people hate the singleton pattern so much. Possibly because you have an instance of an object that never gets released and sits in memory all the time but to be honest, I NEED that data all the time anyway so I don't want to release it! A singleton populated with value objects and a few methods to grab value objects by id etc works like a charm. I'd recommend that approach to anyone.
  • jtproctorjtproctor Posts: 1New Users
    edited November 2010
    Duncan,

    This code is well written and does nearly exactly what I was looking to do (i.e. share data between two viewcontrollers). One question though... Is there a way to push the data from one controller to the other when it changes?

    I have two view controllers that are displayed simultaneously, therefore, the -(void)viewWillAppear:(BOOL)animated method doesn't really help me out.

    My gut tells me that I may need to set up a notification center. Just wondering if there is a better/cleaner way to do this.

    Thanks,
    Jameson
  • tripp13tripp13 Posts: 148Registered Users
    edited November 2010
    Duncan,

    Thank you so much for doing this. This is exactly what I have been looking for and I appreciate you taking the time to explain it and show it. Thanks again for all you do and all your help.
  • Duncan CDuncan C Posts: 9,034Tutorial Authors, Registered Users @ @ @ @ @ @ @
    edited November 2010
    jtproctor wrote: »
    Duncan,

    This code is well written and does nearly exactly what I was looking to do (i.e. share data between two viewcontrollers). One question though... Is there a way to push the data from one controller to the other when it changes?

    I have two view controllers that are displayed simultaneously, therefore, the -(void)viewWillAppear:(BOOL)animated method doesn't really help me out.

    My gut tells me that I may need to set up a notification center. Just wondering if there is a better/cleaner way to do this.

    Thanks,
    Jameson


    Jameson,

    Actually, there is an Apple technology that's tailor-made for what you want: KVO (Key Value Observing)

    Do a search on "Key-Value Observing Quick Start" in the XCode help system for more info.

    You would want to make objects that need to be notified of changes call observeValueForKeyPath:ofObject:change:context: on the data container object. Then, they will get notified automatically when the object changes.

    That's a good idea for an extension for my sample project. I'll think about adding KVO to it if I get some time.
    Regards,
    Duncan C
    WareTo

    widehead.gif
    Animated GIF created with Face Dancer, available for free in the app store.

    I'm available for one-on-one help at CodeMentor
  • humblepiehumblepie Posts: 1New Users
    edited January 2011
    Duncan:

    I downloaded the file and ran it -- it leaks memory -- is there a way to fix this?

    Thanks
    H
  • UnretroGamerUnretroGamer Posts: 112
    edited January 2011
    Duncan C wrote: »
    At least a couple of times a week somebody posts a question something like this:

    My app has 2 view controllers, and I need to get information from the user in one view controller, and pass it to a second view controller. How do I do that?

    More generally, the question is, what's the best way to share information between different objects in your app?

    I got tired of answering this question repeatedly, and decided to write a sample app that shows how to do it. The idea is to create a single data container object that is a property of the app delegate, and make that data object available to every object in your app that needs it.

    Here's what I did:

    -I created a generic data container class AppDataObject. This is an ancestor class for a data container object that you would actually use to hold your application's global variables.

    -At init time, I added code to the app delegate that create an empty data container object.

    -I created a dirt simple protocol, AppDelegateProtocol. This protocol only lists one method, -theAppDataObject. That method lets you ask the app delegate for it's data object.

    -I created a subclass of the AppDataObject that actually holds data for a real app. This is where you put the information that you want to share between objects.

    Using a protocol means that the only things the objects in your app need to include are the AppDelegateProtocol and the header for your subclass of the app data object. You don't have to #include the header of your app delegate, which makes for good separation between the different objects in your app.

    I have posted a complete project, ViewControllerDataSharing, that uses the above method to share information between view controllers. The project uses a navigation controller, and has a root view controller and a second view controller. Each view controller has a UITextView and a slider. The app uses an AppDataObject to pass the value of these fields between view controllers.

    I've posted the project to my public iDisk. You should be able to download it simply by clicking the link below:

    Download the ViewControllerDataSharing project.

    Gosh you know what'd be gold? If you made a video of you making this it.
  • NatxoNatxo Posts: 2New Users
    edited January 2011
    Hi Duncan and all of the members, I´ve used your code in an iPad app. Works perfectly, but now I´ve added a navigationcontroller that loads viewcontrollers for each screen. It crashes when I access the theDataObject.string1 on the viewcontrollers pushed by the navigationcontroller.

    It´s quite strange, because I´m using it without problems using tabBarController in other screens of the app, but when I change to this navigationcontroller crashes.

    Any idea? Anyway, thanks for your help and your great code.
  • DanniMichiganDanniMichigan Posts: 7New Users
    edited February 2011
    As an unabashed cut-and-paste programmer (with just enough experience in Java to be dangerous), thank you! I was going to go the "quick and dirty" route, but I try to learn one or two new skills with each new app, and this worked beautifully!

    Of course, I understand maybe 50% of what it all does, but, hey, it works and I'm that much closer to completing my second app. I want you to know how much I appreciate the code that you posted and how helpful it has been to me.
  • Duncan CDuncan C Posts: 9,034Tutorial Authors, Registered Users @ @ @ @ @ @ @
    edited February 2011
    Natxo wrote: »
    Hi Duncan and all of the members, I´ve used your code in an iPad app. Works perfectly, but now I´ve added a navigationcontroller that loads viewcontrollers for each screen. It crashes when I access the theDataObject.string1 on the viewcontrollers pushed by the navigationcontroller.

    It´s quite strange, because I´m using it without problems using tabBarController in other screens of the app, but when I change to this navigationcontroller crashes.

    Any idea? Anyway, thanks for your help and your great code.

    Sorry, I missed this post until now.

    You probably have an over-release problem. You'd have to post the offending code in order for us to be any help finding the problem.
    Regards,
    Duncan C
    WareTo

    widehead.gif
    Animated GIF created with Face Dancer, available for free in the app store.

    I'm available for one-on-one help at CodeMentor
  • DoraldDorald Posts: 45Registered Users
    edited March 2011
    Hi Duncan C .
    Can you give an example for sharing UISwitch value between view controller ?
    I want to pass uiswitch value from settings view to gameview (EAGLView) to turn background music on/off.
    Thank you.



    Duncan C wrote: »
    At least a couple of times a week somebody posts a question something like this:

    My app has 2 view controllers, and I need to get information from the user in one view controller, and pass it to a second view controller. How do I do that?

    More generally, the question is, what's the best way to share information between different objects in your app?

    I got tired of answering this question repeatedly, and decided to write a sample app that shows how to do it. The idea is to create a single data container object that is a property of the app delegate, and make that data object available to every object in your app that needs it.

    Here's what I did:

    -I created a generic data container class AppDataObject. This is an ancestor class for a data container object that you would actually use to hold your application's global variables.

    -At init time, I added code to the app delegate that create an empty data container object.

    -I created a dirt simple protocol, AppDelegateProtocol. This protocol only lists one method, -theAppDataObject. That method lets you ask the app delegate for it's data object.

    -I created a subclass of the AppDataObject that actually holds data for a real app. This is where you put the information that you want to share between objects.

    Using a protocol means that the only things the objects in your app need to include are the AppDelegateProtocol and the header for your subclass of the app data object. You don't have to #include the header of your app delegate, which makes for good separation between the different objects in your app.

    I have posted a complete project, ViewControllerDataSharing, that uses the above method to share information between view controllers. The project uses a navigation controller, and has a root view controller and a second view controller. Each view controller has a UITextView and a slider. The app uses an AppDataObject to pass the value of these fields between view controllers.

    I've posted the project to my public iDisk. You should be able to download it simply by clicking the link below:

    Download the ViewControllerDataSharing project.
  • MrBrMrBr Posts: 36Registered Users
    edited March 2011
    Hi Duncan C.

    first of all a big thanks for the helpful work you putted in here.

    I have a short question regarding the object data sharing in an TabBar Application.

    When I try to implement the code in the root view controller:
    - (ExampleAppDataObject*) theAppDataObject;
    {
    	id<AppDelegateProtocol> theDelegate = (id<AppDelegateProtocol>) [UIApplication sharedApplication].delegate;
    	ExampleAppDataObject* theDataObject;
    	theDataObject = (ExampleAppDataObject*) theDelegate.theAppDataObject;
    	return theDataObject;
    }
    
    

    I get the following error in the first line of the method:
    error: cannot find protocol declaration for 'AppDelegateProtocol'

    Any ideas what could be the reason for this error?

    Again thank you very much and have a nice day,
    - MrBr.
  • chinthakachinthaka Posts: 3New Users
    edited March 2011
    Hi Duncan,

    I was looking for such a sample for a long time. Your sample helps me lot without any problem. I have used your delegate to pass device location between background process and mapview.

    Thanks again.
«13
Sign In or Register to comment.