iOS Obj-C. Posing correct solution? - objective-c

I am at a place in my application where essentially every ViewController has a local NSManagedObjectContext:
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
and every segue passes the managedObjectContext via the same setter
[segue.destinationViewController setManagedObjectContext:self.managedObjectContext];
Coming from Java, it would be easy to create an abstract class that each ViewController implementes. In Objective-c it doesnt seem like that is possible. What I am looking to do is have a base class that performs this passing, but basically anything that implements UIViewController will have this (including just a plain UIViewController as well as a UITableViewController). Would it be possible/correct to have create an "abstract" class that poses as UIViewController that does this?
Update:
UIViewController+ManagedObjectContext.h
#interface UIViewController (ManagedObjectContext)
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#end
UIViewController+ManagedObjectContext.m
#import "UIViewController+ManagedObjectContext.h"
#implementation UIViewController (ManagedObjectContext){
NSManagedObjectContext *context; // This is not valid, cant have local variables
}
#synthesize managedObjectContext; // This is not valid, must be #dynamic
-(void)setManagedObjectContext:(NSManagedObjectContext *)context{
//How do you have a local NSManagedObjectContext?
}
#end

You can just make your own subclass of UIViewController, let's say MOCViewController, with the managedObjectContext property. Then make all of your other view controllers be subclasses of MOCViewController instead of directly subclassing UIViewController.
If you really want to do it with a category, your category can use objc_setAssociatedObject to attach the managed object context to the view controller.
If you only really have one managed object context and you're just passing it around everywhere, consider just putting the context in a property of your application delegate, or in a global variable.

You can get the managedObjectContext from a managed object rather than pass it separately.
Generally its more logical to pass the managed object.
For example:
Say you have a managed object called thing, you can get the managedObjectContext by calling
NSManagedObjectContext *moc=[thing managedObjectContext];
Alternatively you can get the managed object context from the application delegate:
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;

Related

IBOutlet in ARC releases and sets to nil. How to avoid this? objective c

I'm new to ARC and Storyboarding. I've set IBOutlet to UITableView from my UIViewController.
After some time my IBOutlet sets to nil and I can't reload it from other classes.
Here is my dataTable IBOutlet:
#property (weak, nonatomic) IBOutlet UITableView *dataTable;
At the start dataTable is not nil, but not when I try to access it from another class (via appDelegate). How to solve this problem?
UPDATE
I call this method from my UIViewController
[appDelegate.myClass loginWithUserName:loginField.text andPassword:pwdField.text];
When it's done, and I have data to show, I call this code from loginWithUserName method:
MyViewController *controller = [[AppDelegate sharedStoryboard] instantiateViewControllerWithIdentifier:#"MyViewController"];
[controller audioLoaded];
And here is that method in my UIViewController, wich reloads data
-(void) audioLoaded
{
//it is nil here
[self.dataTable reloadData];
}
Set the property to strong retain the object:
#property (strong, nonatomic) IBOutlet UITableView *dataTable;
It's not good practice to access a UITableView from another view controller though..
EDIT:
You shoul reconsider the whole approach, by moving that logic from your appdelegate to a dedicated class that will perform the login. You can create a simple protocol that the UIViewController with the table can implement, then, when calling the login method, pass a reference to the current viewcontroller, something like
loginWithUserName:andPassword:andCaller:(id<LoginDelegate>)sender
Where LoginDelegate is something on this line:
#protocol LoginDelegate
- (void)audioLoaded;
#end
In this way you can just call
[sender audioLoaded];

How do I assign a delegate in a view for a control contained in a child view?

I have a view controller which contains a UISearchBar
#interface TradeFindHeaderViewController_iPhone : UIViewController {
UISearchBar *searchBar;
}
#pragma mark - Properties
#property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
#pragma mark - Methods
-(void) configureSearchBar;
#end
This controller is then initialized and stored in a property of another controller.
#interface TradeFindViewController_iPhone : TradeFindViewController<UISearchBarDelegate> {
TradeFindHeaderViewController_iPhone *_headerController;
}
#pragma mark - Properties
#property (nonatomic, retain) TradeFindHeaderViewController_iPhone *headerController;
#end
I want this TradeFindViewController_iPhone to receive the UISearchBar delegate events so I assign it's delegate
-(void)configureTableHeader{
self.headerController=[[TradeFindHeaderViewController_iPhone alloc]initWithNibName:#"TradeFindHeaderView_iPhone" bundle: nil];
self.headerController.searchBar.delegate=self;
self.tableView.tableHeaderView=self.headerController.view;
}
However, the UISearchBar delegate events are not being called. Have I assigned the delegate properly given the UISearchBar is in the contained view?
I would probably implement a multi-level delegate system. Your TradeFindHeaderViewController_iPhone class would register as the delegate for the UISearchBar, and would then call a delegate method in your TradeFindViewController_iPhone class.
This solution helps to keep the whole design very modular, and also prevents things breaking (changing the name of objects) across classes.
This should solve your issue with the delegate methods not being called.
Hope this was of some help.
Josh

Mac Development Building Basic Interface

I am going through several example code banks and tutorials and just completely stumbling, a lot of it is due to the fact that these tutorials are written for Xcode < 4.2 and ARC changes a few things.
I am attempting to build an interface with an MVC design pattern. I am using the basic template provided for the Application Delegate. I have added a class called MainWindowController which inherits from NSWindowController. In the Interface Builder, firstly I removed the Window object in the MainMenu nib file (because I want it in a separate file). I create a new Interface called MainWindow(.xib) I change the file owner to MainWindowController I add the Delegate to the Object List. Now at this point something is not clicking.
I do not fully grasp how or what I need to implement in order for the Delegate to essentially load and launch the Window Controller. First I tried linking the outlet for "delegate" in the Window to the actual application delegate (called AppDelegate) and then linking the Window Outlet in the Delegate class to the Window in Interface Builder.
I would like an answer to this but I would be far more happy with the correct documentation describing this process. I'm sure there is something on MacDev but I'm having trouble finding it.
Here's what I'm working with:
#class MainWindow;
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (strong) MainWindow *mainWindowController;
#property (assign) IBOutlet NSWindow *window;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (IBAction)saveAction:(id)sender;
#end
...
#implementation AppDelegate
#synthesize window;
#synthesize mainWindowController;
#synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
#synthesize managedObjectModel = __managedObjectModel;
#synthesize managedObjectContext = __managedObjectContext;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
MainWindow *controller = [[MainWindow alloc] initWithWindowNibName:#"MainWindow"];
mainWindowController = controller;
// ... the rest handles the ManagedObject Models...
Solution to date:
#synthesize mainWindowController = _mainWindowController; // IBOutlet is linked in IB
//...
- (void) applicationDidFinishLaunching: (NSNotification *) aNotification
{
//... initialization of event handling etc...
if ( !_mainWindowController ) _mainWindowController = [[ MainWindowController alloc] initWithWindowNibName:#"MainWindow"];
[_mainWindowController showWindow: self];
// ...
The application delegate can manage an NSWindow in the main nib file or it can delegate that task to a controller (NSWindowController) which is typical of document based applications or MVC design patterns. The default nib file specified by the Basic-info.plist defines which nib file is loaded. Typically the default nib should be the main menu, which loads the delegate as well. The delegate should then by means of applicationDidFinishLoading: or awakeFromNib: perform initialization of the controllers and continue appropriate delegation in accordance with the delegation and mvc design patterns.
The main problem I was having was launching the window, which is done by showWindow: . The exacerbation of this problem stemmed from source code from a very old Mac OS X project that used deprecated functions and methods to accomplish delegation and led me down the wrong path. Thanks for the answer, it ultimately had me look in the right place for the right questions and I found the right answer.
Your problem is that in that second xib, "MainWindow", a new AppDelegate object is being created that has absolutely nothing to do with the AppDelegate object in the "MainMenu" xib. The objects in each xib are real objects that get serialized and then loaded at runtime.
This specific issue will be addressed in the Resource Management Guide, "Nib Loading". I can also suggest "Core Application Design".

How to Call a Controller Class (delegate) method from View Class Event in Objective C/Cocoa

lets say I have an NSWindow Class, that has several events for mouse and trackpad movements
for example this code
IMHO, I think this should be good programming, it is similar to pointing an action of a button to its method in controller.
in MyWindow.m Class I have (which in IB I have set the window class to it)
#implementation MyWindow
- (void)swipeWithEvent:(NSEvent *)event {
NSLog(#"Track Pad Swipe Triggered");
/* I want to add something like this
Note that, Appdelegate should be existing delegate,
not a new instance, since I have variables that should be preserved*/
[AppDelegate setLabel];
}
#end
and in My AppDelegate.h I have
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet NSTextField *label;
}
-(void)setLabel;
#end
and in my AppDelegate.m I have
-(void)setLabel
{
[label setStringValue:#"swipe is triggered"];
}
I have tried #import #class, [[... alloc] init], delegate referencing in IB (I made an object class of MyWindow - thanks to the answer of my previous question )
that latter seems the closest one, it works if both of the classes are delegates, so I could successfully call the "setLabel" action from a button in a second controller (delegate class)'s IBAction method,
but this View Events seem not communicating with the delegate's action although their code is executing.
You are sending a message to the AppDelegate class, not the instance of your AppDelegate class which is accessible from the NSApplication singleton ([NSApplication sharedApplication])
[AppDelegate setLabel];
This is wrong, to get the delegate do this:
AppDelegate* appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
then send the message to that instance:
[appDelegate setLabel];

Error when ViewController is implementing UITextFieldDelegate

When implementing the UITextFieldDelegate in my ViewController class, the following error is thrown when entering the first character in the text field:
-[MyViewController respondsToSelector:]: message sent to deallocated instance...
So, I tried creating a separate class (inheriting only NSObject) and implementing UITextFieldDelegate. Guess what, it worked perfectly. However, that introduces some other problems as I have to do a lot of ugly cross-class-communication that I'd like to avoid. Here's the relevant parts of my app delegate code:
#interface RMSAppDelegate : NSObject <UIApplicationDelegate,
UITabBarControllerDelegate>
#property (nonatomic, retain) UIViewController* myViewController;
#end
#implementation MyAppDelegate
#synthesize myViewController;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
myViewController = [[MyViewController alloc]
initWithNibName:#"MyView" bundle:nil];
[self.window setRootViewController:myViewController];
[self.window makeKeyAndVisible];
return YES;
}
#end
.. and here's what is being displayed:
#interface MyViewController : UIViewController <UITextFieldDelegate>
#property (nonatomic, retain) IBOutlet UITextField* pinTextField;
- (void)viewDidLoad;
#end
#implementation MyViewController
#synthesize pinTextField;
- (void)viewDidLoad {
// DOES NOT WORK (WHY?)
//[pinTextField setDelegate:self];
// WORKS, BUT I'D LIKE TO AVOID
[pinTextField setDelegate:[[[MyTextFieldDelegate alloc] init] autorelease];
[pinTextField becomeFirstResponder];
[super viewDidLoad];
}
#end
And please, if you see any code (even off topic) that I could be doing better, leave a comment.
Since you asked for off-topic code comments: You forget to call [super viewDidLoad]. You also don't need to redeclare the prototype in order to override it. And the #synthesize textFieldDelegate is not valid, as you have no property in the class named textFieldDelegate. And your dealloc method is releasing an ivar named tfd which doesn't seem to actually exist in the class.
Your real problem is that you are not properly retaining the view controller at whatever point you allocate it. It may be that the view controller is being instantiated in a nib and associated with an ivar rather than a property declared retain, or is not being associated with anything. Or it could be that you are allocating it in code, adding its view as a subview of something, and then releasing it without ever retaining the view controller itself. Or it could just be that you are just releasing it when you shouldn't.
Your other class works specifically because you are leaking the object, so it never gets deallocated. The better solution, were you to go with this method, would be to store the object in an ivar when you allocate it and then release it (and set the ivar to nil) in both dealloc and viewDidUnload.
Okay, I finally solved this on my own. I have not changed the code. My NIB (.xib) was the culprit!
I thought that nested UIViewControllers was OK, and I still think they are in some cases (and maybe using another programmatic method). Anyway, I was initializing my class MyViewController with a NIB that in the Objects panel had a UIViewController as the first object.
I solved this by having the UIView as the first object in the Objects panel, and setting the File's Owner to be the UIViewController instead.
Correct code, incorrect NIB. Thank you for your help.