Reputation: 183
I am trying to inject a dependency (to a data store) into all the viewControllers in my tabbed app, from AppDelegate, rather than access the datastore by reaching back into the appDelegate. I am using a storyboard.
I do this in application:didFinishLaunchingWithOptions:, and the code executes without errors.
However, when any of the viewControllers is presented, the property into which I have injected the datastore contains nil. I was expecting it to have a reference to the datastore.
I thought maybe my datastore went out of scope after application:didFinishLaunchingWithOptions returns and caused the datastore to become nil. But to my knowledge ARC should prevent that.
I started to suspect that maybe the VCs might go out of existence after application:didFinishLaunchingWithOptions: finishes running. So I added a dealloc method to the view controllers to see if it gets called, and lo-and-behold, it did. That explains why the dependency I injected previously is no longer there.
Now I am stuck, as I don't know how else to inject the dependency into the view controllers. The only idea I have left is to add properties to my AppDelegate and use them to retain the view controllers, but that feels a bit dangerous cause I'm now interfering with iOS management of view controllers.
Here is the code in AppDelegate:
//AppDelegate.h
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
//AppDelegate.m
#import "AppDelegate.h"
#import "InjectedViewController.h"
#import "InjectedDataStore.h"
@interface AppDelegate ()
@property (strong, nonatomic) InjectedDataStore *myDataStore;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UIStoryboard *storyBoard;
storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *initViewController = [storyBoard instantiateInitialViewController];
[self.window setRootViewController:initViewController];
if (!_myDataStore) {
self.myDataStore = [[InjectedDataStore alloc]init];
NSLog(@"alloc inited %@", self.myDataStore);
}
UITabBarController *tabBarController = (UITabBarController *)initViewController;
for (InjectedViewController *ivc in tabBarController.viewControllers) {
ivc.dataStore = self.myDataStore;
NSLog(@"dataStore injected into ivc: %@", ivc.dataStore);
}
NSLog(@"application:didFinishLaunching... done");
return YES;
}
@end
here is my view controller subclass with the property into which I want to inject:
//InjectedViewController.h
#import <UIKit/UIKit.h>
#import "InjectedDataStore.h"
NS_ASSUME_NONNULL_BEGIN
@interface InjectedViewController : UIViewController
@property (strong, nonatomic) InjectedDataStore *dataStore;
@end
NS_ASSUME_NONNULL_END
InjectedViewController.m is boilerplate and otherwise empty. InjectedDataStore.m and .h are a boilerplate Cocoa Touch class without any properties or methods.
and here is one of the viewcontrollers - it is embedded in a tab view. (The other view controller for the other tab is identical.
//FirstViewController.h
#import <UIKit/UIKit.h>
#import "InjectedViewController.h"
@interface FirstViewController : InjectedViewController
@end
//FirstViewController.m
#import "FirstViewController.h"
@interface FirstViewController ()
@end
@implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"data store for FirstVC: %@", self.dataStore);
}
- (void) dealloc {
NSLog(@"First VC dealloc called");
}
@end
and finally, the console output:
alloc inited <InjectedDataStore: 0x600001d483a0>
dataStore injected into ivc: <InjectedDataStore: 0x600001d483a0>
dataStore injected into ivc: <InjectedDataStore: 0x600001d483a0>
application:didFinishLaunching... done
First VC dealloc called ///this is what causes the injected element to disappear.
Second VC dealloc called ///causes the injected element to disappear.
data store for FirstVC: (null)
data store for SecondVC: (null)
(I would eventually implement a protocol, but for now since I am stuck at getting the injection to work I have left that out.)
It seems to me what I have done here is very similar to @juanignaciosi 's answer to this question:
Appreciate any feedback, I am a relative newbie to iOS.
Upvotes: 0
Views: 148
Reputation: 4585
Your's window property is nil
.
Since iOS 13 system is using window
property from scene delegate.
Just remove scene delegate and UIApplicationSceneManifest
from plist if you don't need it.
Upvotes: 1