I'm making a little app based on the "pickers" sample project from the "Beginning iPhone Development" book.
The book's example is basically how to use the UITabBarController and, in this case, the app has five unrelated views, one linked to each Tab and each view's data is processed and output to it's view through that view's controller.
So, the book's example structure looks like this:
AppDelegate
--View1Controller
--View2Controller
--View3Controller
--View4Controller
In my app, however, most of the views are related to one another and will thus share data. For example, one tab will give you a Search Screen and your selection from the search results will take you to another tab that will display data based on that result.
My questions are:
Where do I code-up the processing/data-population of the search result so it can be accessed by the other views? The only place I can think to put that code (so it will be global) is in the AppDelegate, but, is that bad form?
I can certainly figure out how to get the job done, I guess my question is more of a design problem or a best practices question...
The only place I can think to put that code (so it will be global) is in the AppDelegate, but, is that bad form?
Well, Chris, I came here to ask almost the exact same question!
Many controllers need to access the same object instances. From what I've seen online, it seems everyone just puts these instances in their AppDelegate. This doesn't really sit well with me, but the only other solution I can think of is to make my classes instance-aware, so that I can grab the appropriate instance via static methods on my classes. But THAT also seems like an ugly solution. (Though I like it a little bit better than relying on AppDelegate.)
So if someone out there would enlighten us as to best practices, I think it would be valuable for everyone who happens across this thread.
Thanks.
Last edited by p.witty; 04-20-2009 at 07:24 PM.
Reason: Fixing the quote
you should put the code which you are talking about in what is called a model class, which is a class which contains and processes data for your applications. This is one of the three classes in the Model View Controller paradigm. Just google Model View Controller, check it in the apple online documentation, search this forum for it and check YouTube (there is a small series of humorous videos on the subject). It is one of the main programming paradigms on the iphone.
you should put the code which you are talking about in what is called a model class, which is a class which contains and processes data for your applications. This is one of the three classes in the Model View Controller paradigm. Just google Model View Controller, check it in the apple online documentation, search this forum for it and check YouTube (there is a small series of humorous videos on the subject). It is one of the main programming paradigms on the iphone.
Hope this helps
M.J.
Thanks for the reply, M.J.
I actually have a pretty decent grasp of the MVC paradigm. My question is more about implementation of the paradigm in a more specific context.
It makes sense that I would create an instance of some model class, initialize the class' ivars and do specific operations on that data from within the class via it's instance methods. What I'm hazy about is where to create the instance and initialize it's data if multiple views rely on that object at any given time during the app's lifecycle.
What I'm hazy about is where to create the instance and initialize it's data if multiple views rely on that object at any given time during the app's lifecycle.
So applicationDidFinishLaunching in your app delegate is a good place to set up any controllers that you want to use. And then have those controllers set up whatever model objects are needed.
And then if you keep references to the controllers in the app delegate, then it's very easy to get those references from any view controller, since a pointer to the app delegate is always easily available.
So applicationDidFinishLaunching in your app delegate is a good place to set up any controllers that you want to use. And then have those controllers set up whatever model objects are needed.
And then if you keep references to the controllers in the app delegate, then it's very easy to get those references from any view controller, since a pointer to the app delegate is always easily available.
Thank you for the advice.
Question: Why then wouldn't you simply have an instance of your model in the app delegate and have each controller access it via the delegate pointer?
Question: Why then wouldn't you simply have an instance of your model in the app delegate and have each controller access it via the delegate pointer?
Chris.
If the app is simple, you could do that. But once the app grows a bit, I think you'll find the app delegate has taken on too much at that point. Especially when model data is really being loaded from disk or the net. Then loading that model data (and checking for errors, timeouts, etc.) is a fair amount of code that sits better in its own controller.
If the app is simple, you could do that. But once the app grows a bit, I think you'll find the app delegate has taken on too much at that point. Especially when model data is really being loaded from disk or the net. Then loading that model data (and checking for errors, timeouts, etc.) is a fair amount of code that sits better in its own controller.
I think I see what you mean...
Would you mind giving me an example? - I don't need any code. Perhaps, just a model/controller flow for an app you've done. If it's not too much to ask.
Look, suppose I have a "currentUser" instance of my User Model. I want to be able to access that specific "currentUser" instance anywhere in my app -- in every single controller.
As some people have pointed out, using the App Delegate seems to be a good way to do this -- and people use it this way in various examples and whatnot on the net -- but to some of us, this doesn't seem like an ideal solution.
An idea I've had -- but have not yet tried -- is to put these instances into appropriate static class vars. That is, to make instance-aware classes, so I can do something like this:
User currentUser = [User currentUser];
Or even more generically,
NSArray myUsers = [User findByName:@"Joe Bob"];
And it would return an array of users with the name Joe Bob (who exist in memory -- not persisted to the DB).
But we could take this approach a step further and bring it to persisted context. At this point the classes would be behaving more like we're in Ruby on Rails than anything else. I'm not sure whether it would be better to have something which would differentiate between in-memory models and in-db models:
And then the class would do memory management -- pushing dirty objects back to the DB and clearing unchanged objects from memory when they are no longer used and when we get low memory warnings.
I think I see what you mean...
Would you mind giving me an example? - I don't need any code. Perhaps, just a model/controller flow for an app you've done. If it's not too much to ask.
Chris.
Yeah, so I can describe something I did in general terms.
So we had a bunch of items on a server that users could view and edit and add to and then we would sync those items back to the server.
So the items are model objects. But those models don't need to know about all the issues with transport over the net (codes as JSON in our case) and nor did they need to know how to deal with failures (phone network disconnects, server is slow, server is down, etc.)
And the models themselves didn't need to deal with the issues of merging updates and changes.
So on both the phone side and the server side, we had these model objects. Which were managed by a controller that exposed what looked like a simple array of those objects. That controller hides all the complexity behind that. That controller also takes care of dumping objects from memory when we're tight for memory, and reconstituting those objects when they are needed again and so on. It hands out proxies to objects when they aren't immediately available so the user interface is always smooth.
Internally the controller knows what to do with inserts and updates and merges and that sort of thing. And that controller uses another controller that deals with the specifics of managing the network and queuing network operations that can't happen at the moment for various reasons, etc.
And then our view controllers just need access to an array of stuff (and be notified when that stuff changes). They don't care about any of those details. They don't care if the objects they are getting are real or proxies. They don't care if those objects are coming from local cache or the network itself.
So we have a single instance of an "array" controller that behaves like a simple mutable array. A pointer to that instance is stored in the app delegate. In turn, that controller uses another controller to send and retrieve actual objects from the server.
There's a little more to it than that, because the controllers have helper objects for certain things. But I hope that at least gives you a high level real world case.
An idea I've had -- but have not yet tried -- is to put these instances into appropriate static class vars. That is, to make instance-aware classes, so I can do something like this:
User currentUser = [User currentUser];
Yeah, so that's the classic singleton pattern, with access through a class method. Only issue is you can't have an actual class var in ObjC, so you have use global variables instead. Not a huge deal.
Quote:
Or even more generically,
NSArray myUsers = [User findByName:@"Joe Bob"];
So that's just a static method that would find instances as needed. With the "class variable" in this case being an array of model instances. So it's a little different, but still would work nicely.
Quote:
I'm not sure whether it would be better to have something which would differentiate between in-memory models and in-db models:
Only issue is you can't have an actual class var in ObjC, so you have use global variables instead. Not a huge deal.
You mind sharing proper syntax for this? Wouldn't I just put it above the implementation?
static User currentUser;
@implementation MyViewController
So how is this different from a class var?
Quote:
Originally Posted by eddietr
So the latter ("generic", as you called it) approach is how I would generally do this. But it all depends on your project of course.
So have you implemented something like this on an iphone project? It sounds like a lot of work, and something that would be worthwhile to make *very* generic (as in, a super class I could inherit from to gain this functionality for any model) and sharing with the open source community.
Of course, with the release of 3.0, we'll have CoreData, so that might make this sort of thing redundant.
You mind sharing proper syntax for this? Wouldn't I just put it above the implementation?
static User currentUser;
@implementation MyViewController
Well, the static var would have to be a pointer to User. And you should create a class method (an accessor really) that creates the actual user object as needed. Remember, you can't actually create/load the user object in static scope.
Something like this:
Code:
+ (User*) currentUser
{
@synchronized(self) {
if (currentUser==nil)
// set up the user, find her, whatever
}
}
return currentUser;
}
+ (void) setCurrentUser: (User*) currentUser{
// if this is new user, release the old one and retain the new one
// assign it to the static var
}
Quote:
So how is this different from a class var?
Well, true class variables in other languages allow you to control the scope of that class and give you setters and accessors at the class level. But, again, it's not a huge deal. You can easily write this yourself.
Quote:
So have you implemented something like this on an iphone project? It sounds like a lot of work, and something that would be worthwhile to make *very* generic (as in, a super class I could inherit from to gain this functionality for any model) and sharing with the open source community.
Yep, can't do that legally now. But that would be a nice project. It's not a huge amount of work, but it is work to make it generic enough to actually be useful to other developers. Would you help with it?
Quote:
Of course, with the release of 3.0, we'll have CoreData, so that might make this sort of thing redundant.
In my testing, Core Data doesn't exactly address the issues we faced. But I can't get more into that without violating two different NDAs. Maybe something to talk about later on, or it could be part of an open source solution to the general problem.
In my testing, Core Data doesn't exactly address the issues we faced. But I can't get more into that without violating two different NDAs.
Well, certainly you can talk about Core Data's shortcomings in generic terms, couldn't you?
Quote:
But that would be a nice project. It's not a huge amount of work, but it is work to make it generic enough to actually be useful to other developers. Would you help with it?
Sure. I'm not sure how much help I'd really be, as I'm brand new to Objective-C work. But I did just finish making this Facebook-connected app work (it was half done when I got it). It's just a prototype at the moment, but I've spent the past week and a half or so learning iPhone SDK and making this thing work.
And in so far as I might be helpful, my helpfulness will be very much constrained by my limited free time. When work isn't too demanding, I could definitely put in a few hours a week (maybe more) on a project like this.
Also, in the interest of making sure we don't reinvent the wheel, we may want to check out EntropyDB. It might at least be a good starting point.
Yeah, so I can describe something I did in general terms.
So we had a bunch of items on a server that users could view and edit and add to and then we would sync those items back to the server.
So the items are model objects. But those models don't need to know about all the issues with transport over the net (codes as JSON in our case) and nor did they need to know how to deal with failures (phone network disconnects, server is slow, server is down, etc.)
And the models themselves didn't need to deal with the issues of merging updates and changes.
So on both the phone side and the server side, we had these model objects. Which were managed by a controller that exposed what looked like a simple array of those objects. That controller hides all the complexity behind that. That controller also takes care of dumping objects from memory when we're tight for memory, and reconstituting those objects when they are needed again and so on. It hands out proxies to objects when they aren't immediately available so the user interface is always smooth.
Internally the controller knows what to do with inserts and updates and merges and that sort of thing. And that controller uses another controller that deals with the specifics of managing the network and queuing network operations that can't happen at the moment for various reasons, etc.
And then our view controllers just need access to an array of stuff (and be notified when that stuff changes). They don't care about any of those details. They don't care if the objects they are getting are real or proxies. They don't care if those objects are coming from local cache or the network itself.
So we have a single instance of an "array" controller that behaves like a simple mutable array. A pointer to that instance is stored in the app delegate. In turn, that controller uses another controller to send and retrieve actual objects from the server.
There's a little more to it than that, because the controllers have helper objects for certain things. But I hope that at least gives you a high level real world case.
Yeah, so I can describe something I did in general terms.
So we had a bunch of items on a server that users could view and edit and add to and then we would sync those items back to the server.
So the items are model objects. But those models don't need to know about all the issues with transport over the net (codes as JSON in our case) and nor did they need to know how to deal with failures (phone network disconnects, server is slow, server is down, etc.)
And the models themselves didn't need to deal with the issues of merging updates and changes.
So on both the phone side and the server side, we had these model objects. Which were managed by a controller that exposed what looked like a simple array of those objects. That controller hides all the complexity behind that. That controller also takes care of dumping objects from memory when we're tight for memory, and reconstituting those objects when they are needed again and so on. It hands out proxies to objects when they aren't immediately available so the user interface is always smooth.
Internally the controller knows what to do with inserts and updates and merges and that sort of thing. And that controller uses another controller that deals with the specifics of managing the network and queuing network operations that can't happen at the moment for various reasons, etc.
And then our view controllers just need access to an array of stuff (and be notified when that stuff changes). They don't care about any of those details. They don't care if the objects they are getting are real or proxies. They don't care if those objects are coming from local cache or the network itself.
So we have a single instance of an "array" controller that behaves like a simple mutable array. A pointer to that instance is stored in the app delegate. In turn, that controller uses another controller to send and retrieve actual objects from the server.
There's a little more to it than that, because the controllers have helper objects for certain things. But I hope that at least gives you a high level real world case.
Very good explanation!
So, on the iPhone (or the server, for that matter), a View or VC that wants to access the "array" does so by referencing the appDelegate. In effect, isn't the appDelegate acting as the "model" even though it has subordinated the creation, maintenance and manipulation of the "array" to other classes?
What happens if a VC wants to search or sort the array for presentation does it do that itself, or request it from the "model" controller through the appDelegate?
Quote:
But that would be a nice project. It's not a huge amount of work, but it is work to make it generic enough to actually be useful to other developers. Would you help with it?
.
I would gladly help in any way I can. I am relatively new to Cocoa, but I understand the concept and see the need to define "where to put the data and the methods to manipulate it".
I have 2 apps (of sufficient scope) in development which would benefit from this.
TIA Dick
Last edited by dicklacara; 04-27-2009 at 07:58 PM.
So, on the iPhone (or the server, for that matter), a View or VC that wants to access the "array" does so by referencing the appDelegate. In effect, isn't the appDelegate acting as the "model" even though it has subordinated the creation, maintenance and manipulation of the "array" to other classes?
No, view controllers get a *reference* to an array controller from the app delegate. Once the reference is obtained, they use the array controller directly. The array controller is a proxy to the actual array.
Quote:
What happens if a VC wants to search or sort the array for presentation does it do that itself, or request it from the "model" controller through the appDelegate?
Well, sorting is tricky when not all of the objects have yet been downloaded or if they've been removed from RAM temporarily due to memory constraints. So I leave that up to the controller. (not the vc)
Quote:
I would gladly help in any way I can. I am relatively new to Cocoa, but I understand the concept and see the need to define "where to put the data and the methods to manipulate it".
That's cool. I wrote down some ideas on how to generalize this. It might start as a tutorial and then grow into something that could be reused.
I'm not sure I have a solution that I'm 100% happy with yet, but I had gotten tired of having to keep adding things to track to the app delegate. I also try to not overuse singletons, and since there is already one handy ( thanks to the AppDelegate ), I simply made a DataManager class that would handle the application data.
In many other languages you'll see a class like that being a singleton so it can be used to access / manage data from anywhere. But since we already have our singleton, I simply made an instance of that as a property of the AppDelegate - then referenced data properties and methods from within there.
Coupling that with FMDB for database access simplified my code and consolodated it to one handy place without making my delegate all cloudy.
Would love to hear any better approaches though. =)
No, view controllers get a *reference* to an array controller from the app delegate. Once the reference is obtained, they use the array controller directly. The array controller is a proxy to the actual array.
Mmmm.... I understand.
Well, sorting is tricky when not all of the objects have yet been downloaded or if they've been removed from RAM temporarily due to memory constraints. So I leave that up to the controller. (not the vc)
OK!
That's cool. I wrote down some ideas on how to generalize this. It might start as a tutorial and then grow into something that could be reused.
I'm not sure I have a solution that I'm 100% happy with yet, but I had gotten tired of having to keep adding things to track to the app delegate. I also try to not overuse singletons, and since there is already one handy ( thanks to the AppDelegate ), I simply made a DataManager class that would handle the application data.
In many other languages you'll see a class like that being a singleton so it can be used to access / manage data from anywhere. But since we already have our singleton, I simply made an instance of that as a property of the AppDelegate - then referenced data properties and methods from within there.
Coupling that with FMDB for database access simplified my code and consolodated it to one handy place without making my delegate all cloudy.
Would love to hear any better approaches though. =)
Anything you can share? We all seem to be looking for a packageable/reuseable prototype.
Well it's nothing terribly magical, and I'll assume that anyone reading this can parse out the header vs module code and that stubs of course have code in them, but maybe this will help the idea:
In app delegate, I have a property to access the data manager class instance:
Like I said, nothing super magical here, but it at least consolidates my data access code and the data points that need to be shared in the application without cluttering the delegate. To access data from somewhere I can get at one of the data points using something like the following: