Accessing the model from the viewcontrollers? - iphone

I am creating an iPhone application, a game, and I am trying to understand and embrace the MVC architecture. I am planning to create a model, in this case called HighScoresModel that is responsible for holding all the information about high scores in my game.
Where should this model be created? In the AppDelegate? In the first view controller?
How should other view controllers access the model in order to pass messages like addScore:withDifficulty:?

I think the best option to to have a class method on HighScoresModel that will access a single, shared instance of the model from any object that needs it.
This is superior to the other options because no controller is responsible for instantiating the model, and the controllers are not unnecessarily coupled to the app delegate either.
As an example:
#interface HighScoresModel : NSObject
+ (HighScoresModel *)sharedHighScoresModel;
...
#end
#implementation HighScoresModel
static HighScoresModel *SharedHighScoresModel;
+ (HighScoresModel *)sharedHighScoresModel
{
if (!SharedHighScoresModel)
{
SharedHighScoresModel = [[HighScoresModel alloc] init];
}
return SharedHighScoresModel;
}
...
#end
Hope this helps!

Create a Singleton and create the HighScoresModel in there. The singleton will be accessible from all ViewController.
As far as other view controller passing messages you'll be able to do something similar from anywhere within the controller.
[MySingleTon myHighScoresModel] addScore:myScore withDifficulty:myDifficulty];
See the following link for more reference to singleton http://www.johnwordsworth.com/2010/04/iphone-code-snippet-the-singleton-pattern/

Related

Separating delegates from ViewController to own classes

I'm pretty new to iOs development and I've become a bit confused how should I separate my code to avoid a huge ViewController.m-file. Currently my main viewcontroller has quite a many delegates:
#interface ViewController : UIViewController <MKMapViewDelegate, HttpClientDelegate, CLLocationManagerDelegate, NSFetchedResultsControllerDelegate>
I would like to reduce the viewController code and I guess I should create separate classes to handle delegate tasks. The problem is that if I for example create singleton class for CLLocationManager and let it implement delegate methods, how do I then communicate with the view controller?
Let's say that my CLLocationManager receives a location update, how do I tell the viewController to make changes to the UI? Should I use NSNotificationCenter to post a notification and then add observer to the view controller? Or is the best way just to let viewController implement all delegate methods as it is now?
Move some of that functionality into your data model.
It's hard to say how you should manage this given the limited information you've provided, but one has to wonder whether a single view controller should really be managing a map view and keeping track of location and managing a HTTP connection and managing a Core Data fetch. Why not move some of that into your model, where it'll be somewhat easier to divide into modules?
Let's say that my CLLocationManager receives a location update, how do
I tell the viewController to make changes to the UI? Should I use
NSNotificationCenter to post a notification and then add observer to
the view controller?
A notification would be a good solution -- it provides a way for the part of your program that manages location (again, this probably belongs in the model) to communicate the change without having to know anything in particular about the parts of the program that care about changes to location. It also allows one-to-many communication -- if you have another view controller that also cares about location, it can observe the same notification.
Or is the best way just to let viewController implement all delegate methods as it is now?
I try to think about dividing responsibilities appropriately more than limiting the size of the code. If you have a class that does one job but needs a lot of code to do it, that's fine. If you have one class that manages many unrelated things, that's not so good. The trouble is that a lot of jobs seem to fall into the traditional "manages a screenful of content" role of a view controller. Try to separate the task of managing the presentation of the data (which is the view controller's rightful job) from managing the data itself (which is the model's job).
Implement a class responsible for delegate methods:
#interface DelegateManager : NSObject <MKMapViewDelegate, HttpClientDelegate, CLLocationManagerDelegate, NSFetchedResultsControllerDelegate>
-(id)initWithViewController:(ViewController*)vc;
#property (weak) ViewController *delegate;
#end
In your ViewController:
#interface ViewController : UIViewController
-(void)doSomething;
#end
In your ViewController, create an instance of DelegateManager with self as parameter. Set all your delegates' target to your DelegateManager. In the delegate methods of your DelegateManager, call [self.delegate doSomething]; to communicate back to your ViewController.

Accessing a view controller's function/variable from other view controllers

I'm currently making the initial menu view controller, which sets up the setting for main game view controller..
so in my menu game controller, I have
#import "MainGameViewController.h"
#implementation menuViewcontroller
......
-(void)setting{
NSMutableDictionary *regions =
[(MainGaimViewController *)self.delegate regions];
NSNumber *yesBool = [NSNumber numberWithBool:YES];
NSNumber *noBool = [NSNumber numberWithBool:NO];
[regions setValue:yesBool forKey:#"KM"];
[regions setValue:noBool forKey:#"KF"];
}
but this gives me the "Request for member 'delegate' in something not a structure or union
regions is a NSMutableDictionary in main game view controller.
so I think menuViewController is not being able to access the function/variable in main game view controller despite the import.
currently, I haven't declared "class MainGameViewController" in my menu implementation file. Could that be why? should I make an object of maingameviewcontroller and use it?
What could be wrong?
Please help me out..
I find it is much cleaner and clearer to put application-global data in separate singleton classes rather than wedge them in to the application delegate object and to try passing around pointers to root-level view controllers. View Controllers should focus just on the job of managing its view and responding to actions, and interacting with data models through the view. The data models themselves and app global data generally should sit outside of ViewControllers. The singleton pattern works nicely for data management, as the data is easily available to any piece of code in the app that needs it without having to worry about setting up delegate protocols, or whether a view controller owning data is valid any more.
You can see my answer to this question for how to set up a singleton data manager class:
Objective C: store variables accessible in all views

Delegates Vs. Notifications in iPhoneOS

I am trying to call a method in my root view controller from a child view controller such that when I change my options they will automatically update the root view, which will in turn update several other view controllers. For the second part I have used notifications, but for this first I am trying to use a delegate because it (so I have been lead to believe) is a good programming practice. I am having trouble making it work and know that I can set up another notification easily to do the job. Should I continue trying to implement the delegate or just use a notification?
Delegating is a good programming practice for many situations but that doesn't mean you have to use it if you're not comfortable with it. Both delegating and notifications help decouple the view controllers from each other, which is a good thing. Notifications might be a little easier to code and offer the advantage that multiple objects can observe one notification. With delegates, such a thing cannot be done without modifying the delegating object (and is unusual).
Some advantages of delegating:
The connection between delegating object and delegate is made clearer, especially if implementing the delegate is mandatory.
If more than one type of message has to be passed from delegatee to delegate, delegating can make this clearer by specifying one delegate method per message. For notifications, you can use multiple notification names but all notifications end up in the same method on the side of the observer (possibly requiring a nasty switch statement).
Only you can decide what pattern is more appropriate for you. In any case, you should consider not having your view controller send the notification or the delegate message. In many cases, the view controller should change the model and then the model should inform its observers or its delegate that it has been changed.
Implementing a delegate pattern is simple:
In your ChildViewController.h, declare the delegate protocol that the delegate must implement later:
#protocol ChildViewControllerDelegate <NSObject>
#optional
- (void)viewControllerDidChange:(ChildViewController *)controller;
#end
At the top of the file, create an instance variable to hold the pointer to the delegate in your ChildViewController:
#protocol ChildViewControllerDelegate;
#interface ChildViewController : UIViewController {
id <ChildViewControllerDelegate> delegate;
...
}
#property (assign) id <ChildViewControllerDelegate> delegate;
...
#end
In RootViewController.h, make your class conform to the delegate protocol:
#interface RootViewController : UIViewController <ChildViewControllerDelegate> {
...
In the RootViewController implementation, implement the delegate method. Also, when you create the ChildViewController instance, you have to assign the delegate.
#implement RootViewController
...
// in some method:
ChildViewController *controller = [[ChildViewController alloc] initWithNibName:...
controller.delegate = self;
...
- (void)viewControllerDidChange:(ChildViewController *)controller {
NSLog(#"Delegate method was called.");
}
...
In the ChildViewController implementation, call the delegate method at the appropriate time:
#implementation ChildViewController
...
// in some method:
if ([self.delegate respondsToSelector:#selector(viewControllerDidChange:)]) {
[self.delegate viewControllerDidChange:self];
}
...
That's it. (Note: I have written this from memory so there are probably some typos/bugs in it.)
I would like to add:
objects receiving notifications can
react only after the event has
occurred. This is a significant
difference from delegation. The
delegate is given a chance to reject
or modify the operation proposed by
the delegating object. Observing
objects, on the other hand, cannot
directly affect an impending
operation.
Typically, if you need to update the UI based on a change to data in a model, you would have the view controllers observe the relevant model data and update their views when notified of changes.
I see delegation as a bit more formal and like the distinction that Peter Hosey shared recently:
The difference is that delegation is
for to-one (and bidirectional)
communication, whereas notifications
are for to-many, unidirectional
communication.
Also, I have found that (completely) updating the view in viewWillAppear: works fine (but this is not the best solution where performance is a concern).
Notifications can make the runtime behavior of your program significantly more complex. Think of it like a goto with multiple destinations. The order of those destinations is not defined. If you ever crash there is little stack trace information.
There are cases when it makes sense to use notifications--the typical one being to communicate a model change or a global state change to your views. Example, the network is down, the application will resign, etc!
It is worthwhile to learn the delegate pattern in iOS. Delegates give you complete stack traces when you debug. They result in significantly simpler runtime behavior while still achieving the goal of decoupling your objects.
Delegates are a little hard to get used to, but I think it's the best practice and, like Apple, they just work.
I always use the formal protocol declaration. It's a bit more logical in my mind, and it's very clear in the code. I suggest using a UIView to change your options instead of a controller. I always use one main controller and have a lot of subclassed UIViews that the one controller can control. (However, you can modify the following code for a controller, if you really need a controller instead of a normal view.) In the header file of the child view, make it look like this:
// ChildView.h
#import <UIKit/UIKit.h>
#protocol ChildViewDelegate; // tells the compiler that there will be a protocol definition later
#interface ChildViewController : UIView {
id <ChildViewDelegate> delegate;
// more stuff
}
// properties and class/instance methods
#end
#protocol ChildViewDelegate // this is the formal definition
- (void)childView:(ChildView *)c willDismissWithButtonIndex:(NSInteger)i; // change the part after (ChildView *)c to reflect the chosen options
#end
The method between #protocol and the second #end can be called somewhere in the implementation of the ChildView, and then your root view controller can be the delegate that receives the 'notification.'
The .m file should be like this:
// ChildView.m
#import "ChildView.h"
#implementation ChildView
- (id)initWithDelegate:(id<ChildViewDelegate>)del { // make this whatever you want
if (self = [super initWithFrame:CGRect(0, 0, 50, 50)]) { // if frame is a parameter for the init method, you can make that here, your choice
delegate = del; // this defines what class listens to the 'notification'
}
return self;
}
// other methods
// example: a method that will remove the subview
- (void)dismiss {
// tell the delegate (listener) that you're about to dismiss this view
[delegate childView:self willDismissWithButtonIndex:3];
[self removeFromSuperView];
}
#end
Then the root view controller's .h file would include the following code:
// RootViewController.h
#import "ChildView.h"
#interface RootViewController : UIViewController <ChildViewDelegate> {
// stuff
}
// stuff
#end
And the implementation file will implement the method defined in the protocol in ChildView.h, because it will run when the ChildView calls for it to be run. In that method, put the stuff that happens when you'd get the notification.
In this case, you don't need to use either delegation or notification because you don't really need to communicate directly between your views. As gerry3 said, you need to change the data model itself and then let all other views respond to that change.
Your data model should be an independent object that all your view controllers have access to . (The lazy way is to park it as an attribute of the app delegate.) When the user makes a change in View A, View A's controller writes that change to the data model. Then whenever Views B through Z open up, their controllers read the data model and configure the views appropriately.
This way, the neither the views, nor their controllers need to be aware of each other and all changes occur in one central object so they are easily tracked.
Is it really necessary for your root view controller to know about the changes, or just the subviews?
If the root controller does not have to know, having the settings send out the notifications the other views are looking for seems like a better answer to me, as it simplifies code. There is no need to introduce more complexity than you have to.

Calling one class file from another

I am new to the iPhone development environment so be gentle:
Currently writing an iPhone game app that will also have a high score view. Was wanting to make this view so that it was on its own and I could call it from another class (.m file)
Question is, how do you call another class file? I know I need to include the header from that file, but how do I call a "function/message" from that class...something like updating the high score?
I hope this makes send. Thanks in advance for any and all help.
Geo...
You really should work your way through the introductory documentation on Apple's developer website:
Learning Objective-C: A Primer
and
Your First iPhone Application
If your function is static, call it like this:
[ClassName theFunction:parameter];
If your function is a member of the class, then create an instance of that class and call the function like this:
ClassName obj = [[ClassName alloc] init];
[obj theFunction:parameter];
Don't think of it as calling functions/methods/procedures/whatever. Think of it as one object talking to another. What do they need to do this? You need a reference to the object you want to talk to.
Often, you'll want to create an instance variable that gets assigned to the object you're interested in. If both objects are in a nib together, you can draw connections between them in Interface Builder. Otherwise, you'll need to structure your code so that they can find each other (e.g., give the nib's owner a reference to whatever other object needs to talk to the view).
You might want to try working through one of the many tutorials out there (for instance, on Apple's developer site) to get a feeling for how this works.
The preferred technique for this would be delegation. So that your main view delegates the task of scoring to your HighScore view.
#protocol myDelegate;
#interface myClass : UIView {
id <myDelegate> delegate;
}
#property (nonatomic, assign) id <myDelegate> delegate;
#end
#protocol myDelegate
- (void)myMethod //Method to be implemented by your High Score View
To implement this protocol in your High Score View do:
#interface HighScore : UIview <myDelegate>
The in your HighScore.m implement the method mymethod:
- (void)myMethod {
//update Score etc...
}
To call the method from your other view do:
myHighScoreView.delegate = self // self is your other view
[delegate myMethod] // calls the method in the other view.
I hope that's clear.
-Oscar

Architecting a multiview application on the iPhone

I have an app with a root view controller, a primary view controller, and a secondary view controller. I would like to be able to send a message to the primary view controller from the secondary view controller. How can I get a reference to the primary so that I can send messages to it? Is there a better way to architect this?
The short answer: you can get back to your application delegate like this:
YourAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
You likely already have a pointer to your root view controller in your application delegate class. And you probably have pointers to your primary and secondary view controllers in the root controller object. So, you could write code like this:
SecondaryViewController *primary = delegate.rootController.primaryController;
You can then send messages to it to your heart's content. No outlets required; just properties to each view controller.
There are many longer answers and also a discussion about why this practice might be questionable since it introduces potentially unwanted linkages between objects. In a "pure" object oriented design, you'll follow a clean design pattern with clear linkages between objects in various directions allowing you to better reuse the code.
Another option is to pass in pointers to the objects the class will need at initialization time. Implement a new initWithSomethingOrOther for your view controller classes and pass objects in as parameters. Cache these pointers you need (don't forget to retain them) for later use.
The clean way to do it is to define a protocol for a delegate for the secondary controller which lists the methods it needs the primary controller to provide:
#protocol SecondaryControllerDelegate <NSObject>
- (void)secondaryController:(SecondaryController*)secondaryController
frobFooWithBar:(Bar*)myBar;
- (BOOL)secondaryController:(SecondaryController*)secondaryController
shouldTwiddleBaz:(Baz*)currentBaz;
#end
Now add a delegate property to the SecondaryController:
#interface SecondaryController : UIViewController {
id <SecondaryControllerDelegate> delegate;
...
}
// delegates are one of the few places you don't retain an object
#property (assign) id <SecondaryControllerDelegate> delegate;
...
In SecondaryController's implementation section, synthesize the delegate property. (Do not release it in the destructor.) When SecondaryController needs to communicate with the PrimaryController, it should call the appropriate method on the delegate.
Now make your PrimaryController implement the SecondaryControllerDelegate protocol:
#interface PrimaryController : UIViewController <SecondaryControllerDelegate> {
...
Implement the delegate methods in PrimaryController.
Finally, have your PrimaryController set itself as the SecondaryController's delegate. Exactly how you do this will depend on whether you create SecondaryController in a nib or not. If you do, make the connection there; if not, make it just after you allocate and init the SecondaryController.
Why do you do this song and dance? Well, when you have to introduce another controller between the Primary and Secondary, or use the Secondary elsewhere in the app, or even use the Secondary in another app (I have one controller that gets used in three of my four apps), you don't have to change SecondaryController at all; you just change whatever class should now be its delegate. This is an incredible time saver in the long run.
If the controllers are loaded from a NIB, you could define an outlet on the secondary controller and connect it to the primary controller in interface builder.
Use NSNotificationCenter for decoupled communication between objects.