I am well into building a Core Data tab-based iPad application. I am passing in my NSManagedObjectContext to my root view using the following in my app delegate class.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.rootViewController.managedObjectContext = self.managedObjectContext;
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;
}
My question is: how do I set the same managedObjectContext on all my tabs? It would also be good if I could set up some of my service classes in the app delegate and use the same instance across all my tabs. How can this be done?
Thanks in advance!
A "tab" is simply another view controller. As you initiate the VCs for each tab, you can hand them a managed object context in exactly the same way you set rootViewController.managedObjectContext, assuming that they have managedObjectContext properties.
Some people use singleton objects to provide Core Data objects to their classes; what I did in the app I'm currently working on was to declare a protocol, CoreDataSource, with getters for my NSManagedObjectContext, NSManagedObjectModel, and NSPersistentStoreCoordinator, and implemented that protocol on my appDelegate. My view controllers that need to use core data have member variables of type NSObject <CoreDataSource>, and as they create each other they set the property. They're all actually pointing to my appDelegate, but they don't know it, so they're not tightly coupled to an upstream object.
The simplest solution is to add super class for you tab view controllers with a managedObjectContext attribute and a custom getter method like:
- (NSManagedObjectContext *) managedObjectContext{
if (managedObjectContext !=nil) {
return managedObjectContext;
}
MyAppDelegateClass *appDelegate=(MyAppDelegateClass *)[[UIApplication sharedApplication] delegate];
self.managedObjectContext=appDelegate.managedObjectContext;
return managedObjectContext;
}
If all your tab view controllers inherit this method they will all automatically find the app delegate's managed object context. And you're done.
Related
In AppDelegate.h
#property(strong,nonatomic)NSString *str2;
In ViewController.m
AppDelegate *c3=[[AppDelegate alloc]init];
c3.str2= #"Hello";
NSLog(#"Output:-%#",c3.str2);
Output:-Hello
Navigation code in Tableview didselect method(view Controller):-
Class2 *c2=[self.storyboard instantiateViewControllerWithIdentifier:#"cl2"];
[self.navigationController pushViewController:c2 animated:YES];
In Class2.m:-
AppDelegate *c3=[[AppDelegate alloc]init];
NSLog(#"%#",c3.str2);
Output:-Null
First, let's fix your current approach: the reason some recommend using app delegate to store shared values is that there is one easily accessible instance of it. You never create a new app delegate, but access the shared one instead:
AppDelegate *c3 = (AppDelegate*)[[UIApplication sharedApplication] delegate];
Once you replace [[AppDelegate alloc]init] with the above, your code will start working the way you expect.
However, this solution is not ideal. App delegate is not supposed to be a place for storing shared values; they should be stored in your model object (as "M" in MVC, Model-View-Controller). Create a singleton model object, place shared variable in it, and use that shared variable by accessing the singleton from different view controllers.
In AppDelegate.h
#property(strong,nonatomic) NSString *str2;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
_str2 = "Hello"
}
In ViewController.m and any other view controller that wants to access str2:
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSLog(#"Output: %#", delegate.str2);
Never create AppDelegate object on your own. Instead, access its instance via sharedApplication.
I got a customized UILabel which register itself to a communication instance during creation. This part works perfect, the UILabel will . But when I remove the UILabel will update itself after called by the communication instance. I have of course remove the listener delegate too. But as ViewDidUnload is not call anymore, I do not know where.
Here the code sample:
#implementation MyValueLabel
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self)
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate addBalanceListener:self];
}
return self;
}
-(void)communicationUpdate
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
}
// This is the missing Method
-(void) DestructionOfTheLabelWhichIDidNotFound
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate removeBalanceListener:self];
}
#end
Technically you seem to be looking for the -dealloc method which is called on an object when it is to be deallocated. However I do not believe that is an adequate solution.
The example provided here seems to be doing a number of unexpected things which I believe will result in code which is surprising to other developers, requires fighting against common patterns in the UIKit framework, and be difficult to maintain.
Normally iOS view controllers are responsible for mediating or coordinating how data reaches view objects. A controller supplies a view with either the data it should currently display or provides it with a source of that data. This allows a single view class to be used in many different locations and to display data of the same type regardless of its source. Instead here we have a view which reaches out to obtain it's own data. There's no way for a controller to determine when an instance of this view class should update or what data it should display. For example the controller cannot decide to pause updates, or cause other view elements to update at the same time.
In addition this view makes several assumptions about the source of it's data and how that source may be obtained. The view assumes that the current application's app delegate is specifically an AppDelegate class so it will not work if you want to use this view in another application. Since the view also obtains this AppDelegate instance via a call to + sharedApplication there's no hint to users of this view that it has this dependency.
Consider instead allowing classes which use this view class to provide it with the data it should display.
If the property where addBalanceListener: stores the reference is weak, you don't need to do anything. Weak properties are automatically set to nil when the instance they are pointing to is dealloced.
I have an app that have database based on XML structure. XML is quite large, and what i want is, to parse it from very beginning, and then fill array that declared in other class. That is how i try to do this - when parsing ended, method -(void)dataFromXMLdidFinishExtractingToArray:(NSMutableArray*)array: is called through protocol, then i want to fill an array. Please, take a look at my code, that declared in appDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
dataManager *dataMgr =[[dataManager alloc]initWithDelegate:self];
[dataMgr loadInternalData];
return YES;
}
-(void)dataFromXMLdidFinishExtractingToArray:(NSMutableArray*)array{
ListViewController *listController = [[ListViewController alloc]init];
listController.listOfPlaceDetails = array;
NSLog(#"%# NAMES", array);
NSLog(#"delegate Called");
}
Firstly, i do parsing with my dataManager class, and when it finish, trying to fill data in array. Parsing is just fine, i can see everything in console. But, when i load my listViewController class, array - self.listOfPlaceDetails is empty. How can i pass values from appDelegate to other class in my app?
Any advice would be appreciated, thanks!
You are creating a new instance of ListViewController which is not the one you are using in your view controllers hierarchy. Instead, you need to grab a reference to your initiated ListViewController.
Depending on your UI structure and navigation you use, it will be either your rootViewController or any of its child viewControllers.
Alternatively, you can use notification to inform your ListViewController when your data model has changed.
I have my main app delegate
I have a few UIViewController derived instances driven by a Storyboard
Say I'd like to provide a centralized persistence layer for my application - perhaps Core Data of SQLite. Where would I put those objects? I'm missing some centrally accessible "Application" class you can access from all the UIViewController instances.
Is there a pattern to follow here?
you should check the singleton pattern:
In software engineering, the singleton pattern is a design pattern
that restricts the instantiation of a class to one object. This is
useful when exactly one object is needed to coordinate actions across
the system. The concept is sometimes generalized to systems that
operate more efficiently when only one object exists, or that restrict
the instantiation to a certain number of objects. The term comes from
the mathematical concept of a singleton.
here is a source for a example implementation: What should my Objective-C singleton look like?
and here is the direct link for the modern solution:
https://stackoverflow.com/a/145395/644629
What you're describing is your model layer. There are two main ways to manage the model:
At application startup, create the main model object and hand it to the first view controller.
Make the main model object a Singleton.
The "main model object" in both cases is generally some kind of object manager. It could be a document, or it could be a PersonManager if you have a bunch of Person objects. This object will vend model objects from your persistence store (generally Core Data).
The advantage of a Singleton here is that it's a little easier to implement and you don't have to pass around the manager. The advantage of a non-Singleton is that it's easier to have more than one (for a document-based system), and it's easier to test and reason about non-singletons than singletons. That said, probably 80% of my projects use a singleton model manager.
As a side note, that you appear to already understand: never store the model in the application delegate, and never use the application delegate as a "rendezvous point" to get to the model. That is, never have a sharedModel method on the application delegate. If you find yourself calling [[UIApplication sharedApplication] delegate] anywhere in your code, you're almost always doing something wrong. Hanging data on the application delegate makes code reuse extremely difficult.
Go with a singleton pattern, which has scope of application lifetime.
#interface DataManager ()
#end
#pragma mark -
#implementation DataManager
#pragma mark - Shared Instance
static DataManager* sharedInstance = nil;
#pragma mark - Singleton Methods
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
+ (DataManager*)sharedInstance
{
#synchronized([DataManager class])
{
if (!sharedInstance) {
//[[self alloc] init];
sharedInstance = [[DataManager alloc] init];
}
return sharedInstance;
}
return nil;
}
+ (id)alloc
{
#synchronized([DataManager class])
{
NSAssert(sharedInstance == nil, #"Attempted to allocate a second instance \
of a singleton.");
sharedInstance = [super alloc];
return sharedInstance;
}
return nil;
}
#end
Declare your properties in .h file and synthesize them here in .m file.
To use that property just call:
// set value
[[DataManager sharedInstance] setSharedProperty:#"ABC"]; // If its a string
// get Value
NSLog(#"value : %#", [[DataManager sharedInstance] sharedProperty]);
Hope this is what you required.
Enjoy Coding :)
As mentioned before - I'm an Objective-C newbie of the first order but having read 4 physical books on the subject and a bucket-load of ebooks and documentation I still can't find what I'm looking for.
I have a top-level content view controller that wants to configure its view property from the physical dimensions of the window property of the application delegate. This is something that several people have already asked questions on. ([UIScreen mainScreen] doesn't cut it for reasons already aired many times before on this forum). Therefore, the logical approach would be for the content view controller to read the frame of the application delegate's window. Now, the only answer I've found that comes close to this is to use [[[UIApplication sharedApplication] window] frame] - however, this only works once the window property has been made keyAndVisible. The content view controller needs to read the app delegate's window property before it gets to makeKeyAndVisible. The code goes in this order....
App Delegate:
- (BOOL) application: (UIApplication *) application didFinishLaunchingWithOptions: (NSDictionary *) launchOptions {
// This next line is a test window frame for R&D purposes....
[self setWindow: [[UIWindow alloc] initWithFrame: CGRectMake(0.0f, 20.0f, 320.0f, 320.0f)]];
if ([self window]) {
contentViewController = [[ContentViewControl alloc] initWithNibName: nil bundle: nil]; // Content view controller instantiated here
if (contentViewController) {
[[self window] addSubview: [contentViewController view]];
[[self window] layoutSubviews];
[[self window] makeKeyAndVisible]; // Window made key and visible here
return YES;
}
}
return NO;
}
In my content view controller's initWithNibName: nil bundle: nil method I have the following test code...
- (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil {
self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil];
if (self) {
NSLog(#"%#", NSStringFromCGRect([[[UIApplication sharedApplication] keyWindow] frame]));
// This does not work.
}
return self;
}
This does not work due to the fact that App Delegate's window is not yet key and visible.
So, my question is thus; What is the name of my App Delegate Class' instance? I know the App Delegate's Class name defaults to myApplicationNameAppDelegate but I'm after the instance name. I want to replace the call to [[UIApplication sharedApplication] keyWindow] by something like;
[myAppDelegatesInstanceName window].
Expanding this a little, how does one access methods in other target objects that are not scope descendants of the object doing the querying?
Like I said, I'm a total noob to all of this and this is probably another dumb noobie question but it's one that nobody yet appears to have answered in a simple way.
(Procedurally - my home turf - there are a plethora of ways to get the window stuff down into other levels of scope ranging from making the window globally accessible to the whole program suite to passing it down as a specific parameter through the various function hierarchies - but this Objective stuff seems to depart from established, procedural practice).
If anyone can help, I'd be really grateful. This stuff is definitely not intuitive!
V.V.
You can access the app delegate through the singleton UIApplication instance:
[[[UIApplication sharedApplication] delegate] window];
This is a special case, though, because you can access an object (the UIApplication instance) whose delegate you know is the object you want to access. The general case:
Expanding this a little, how does one access methods in other target objects that are not scope descendants of the object doing the querying?
You need to pass a reference to the target object to the object from which you want to access it. This is sometimes necessary but it also means that you introduce a tight coupling between these two objects, which you generally want to avoid if you can. So think about each case and see if there are other possible designs.
Swift 3
if let window = NSApplication.shared().windows.first {
// access window. properties now
}
Use this for Swift:
let window:UIWindow? = (UIApplication.sharedApplication().delegate?.window)!