Call a method from AppDelegate - Objective-C - ios

I was trying to call an existing method of my ViewController.m from AppDelegate.m inside the applicationDidEnterBackground method, so I found this link: Calling UIViewController method from app delegate, which told me to implement this code:
In my ViewController.m
-(void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.myViewController = self;
}
In my AppDelegate:
#class MyViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (weak, nonatomic) MyViewController *myViewController;
#end
And in the AppDelegate's implementation:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self.myViewController method];
}
So I put this code in my project and it worked fine, but I didn't understand how the code works, line by line. What does the sharedApplication do? Why must I set a delegate instead of just creating an instance of ViewController, like:
ViewController * instance = [[ViewController alloc] init];
[instance method];

Background information (class definition vs class instance)
The important concept here is the difference between a class definition and a class instance.
The class definition is the source code for the class. For example ViewController.m contains the definition for the myViewController class, and AppDelegate.m contains the definition for the AppDelegate class. The other class mentioned in your question is UIApplication. That is a system-defined class, i.e. you don't have the source code for that class.
A class instance is a chunk of memory on the heap, and a pointer to that memory. A class instance is typically created with a line of code like this
myClass *foo = [[myClass alloc] init];
Note that alloc reserves space on the heap for the class, and then init sets the initial values for the variables/properties of the class. A pointer to the instance is then stored in foo.
When your application starts, the following sequence of events occurs (roughly speaking):
the system creates an instance of the UIApplication class
the pointer to the UIApplication instance is stored somewhere in a
system variable
the system creates an instance of the AppDelegate class
the pointer to the AppDelegate is stored in a variable called
delegate in the UIApplication instance
the system creates an instance of the MyViewController class
the pointer to the MyViewController class is stored somewhere
The storage of the pointer to MyViewController is where things get messy. The AppDelegate class has a UIWindow property called window. (You can see that in AppDelegate.h.) If the app only has one view controller, then the pointer to that view controller is stored in the window.rootViewController property. But if the app has multiple view controllers (under a UINavigationController or a UITabBarController) then things get complicated.
The spaghetti code solution
So the issue that you face is this: when the system calls the applicationDidEnterBackground method, how do you get the pointer to the view controller? Well, technically, the app delegate has a pointer to the view controller somewhere under the window property, but there's no easy way to get that pointer (assuming the app has more than one view controller).
The other thread suggested a spaghetti code approach to the problem. (Note that the spaghetti code approach was suggested only because the OP in that other thread didn't want to do things correctly with notifications.) Here's how the spaghetti code works
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.myViewController = self;
This code retrieves the pointer to the UIApplication instance that the system created, and then queries the delegate property to get a pointer to the AppDelegate instance. The pointer to self, which is a pointer to the MyViewController instance, is then stored in a property in the AppDelegate.
The pointer to the MyViewController instance can then be used when the system calls applicationDidEnterBackground.
The correct solution
The correct solution is to use notifications (as in kkumpavat's answer)
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void)didEnterBackground
{
NSLog( #"Entering background now" );
}
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
With notifications, you aren't storing redundant pointers to your view controllers, and you don't have to figure out where the system has stored the pointer to your view controller. By calling addObserver for the UIApplicationDidEnterBackgroundNotification you're telling the system to call the view controller's didEnterBackground method directly.

You have two question here.
1) What does the sharedApplication do?
The [UIApplication sharedApplication] gives you UIApplication instance belongs to your application. This is centralised point of control for you App. For more information you can read UIApplication class reference on iOS developer site.
2) Why must I set a delegate instead of just creating an instance of ViewController?
Creating controller in AppDelegate again using alloc/init will create new instance and this new instance does not point to the controller you are referring to. So you will not get result you are looking for.
However in this particular use case of applicationDidEnterBackground, you don't need to have reference of you controller in AppDelegate. You ViewController can register for UIApplicationDidEnterBackgroundNotification notification in viewDidLoad function and unregister in dealloc function.
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethod) name:UIApplicationDidEnterBackgroundNotification object:nil];
//Your implementation
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

The view controller is already instantiated as part of the NIB/storyboard process, so if your app delegate does its own alloc/init, you are simply creating another instance (which bears no relation to the one created the NIB/storyboard).
The purpose of the construct you outline is merely to give the app delegate a reference to the view controller that the NIB/storyboard instantiated for you.

Related

Block-Based NSNotificationCenter Observing With Subclasses?

I have a class Poster which posts notifications through the NSNotificationCenter. I have two different observers: ObserverSuperclass and ObserverSubclass. ObserverSuperclass is the superclass of ObserverSubclass. I'd like each class to respond differently to the notification.
According to NSHipster, I should use the modern block-based api: addObserverForName:object:queue:usingBlock:.
In ObserverSubclass's initialization method, I need to remove the superclass as an observer. Because I'm using the block-based API, I need to reference the return value of addObserverForName:object:queue:usingBlock: - an "opaque object to act as the observer". So I write the following code:
In ObserverSuperclass.h
#property (nonatomic, strong) id observer;
In ObserverSuperclass.m
self.observer = [NSNotificationCenter defaultCenter] addObserverForName:#"Help!" object:nil queue:nil usingBlock:^{old block}];
In ObserverSubclass.m
[[NSNotificationCenter defaultCenter] removeObserver:self.observer name:#"Help!" object:nil];
self.observer = [NSNotificationCenter defaultCenter] addObserverForName:#"Help!" object:nil queue:nil usingBlock:^{new block}];
Is this actually the best way to do this? I'm not sure it makes sense to use the block-based API here.
This is a case where, in my opinion, you should use the traditional #selector callbacks instead. Your superclass can register the observation and implement the default behavior. The subclass then needs only override the called back #selector.
The method that you should implement is:
- (void)addObserver:(id)notificationObserver
selector:(SEL)notificationSelector
name:(NSString *)notificationName
object:(id)notificationSender
For example, in the superclass:
- (instancetype) init
{ ...
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(oberserveThis:)
name:#"someEventName"
object:nil];
}
- (void)observeThis:(NSNotification*)notification
{
// ... Do something here ...
}
In your subclass:
- (void)observeThis:(NSNotification*)notification
{
// ... Do something else here ...
}
If you need the code in the observer to execute on a specific queue you can always call dispatch within the observation selector. I think that, for your use case, it makes for much cleaner code than using the block based approach.
Your code looks fine.
The logic you've mapped out enforced the idea that you only want one observer, either the superclass or the subclass. Your subclass' observer removes and replaces the superclass' observer.
It's also valid to have them both observing the same notification name. In that case you'd add a new instance variable self.newObserver to the subclass and add an observer to that property as well. Then both blocks would get invoked when a "Help!" notification is posted.
Which you do depends on what you're trying to accomplish.
You DO need to remove an observer when you don't need it any more, or you risk crashing when the objects the observer block deals with get deallocated out from under it. (This is less likely with block-based observers than with selector-based observers, because the notification center retains the block automatically, and even if the object that adds the observer gets deallocated the observer block will still run. With a selector based observer you MUST remember to remove your observer in your dealloc method or you crash after your object is deallocated if the type of notification you are observing gets posted again.)

Access View Controller's properties from AppDelegate

I currently have an Xcode project in which I am using Storyboards. In my AppDelegate, I need to set some properties that are contained in the .h files of other View Controllers in response to notifications that the app receives.
How do I instantiate a object of those view controllers in the AppDelegate so that I can access and modify their properties?
There are ways for the app delegate to get a handle to the right vc and communicate with it, but the better design is to have the information flow the other way around, letting the view controllers ask for information and update their own properties.
To do this, when the app delegate receives a notification, have it post a corresponding NSNotification (via NSNotificationCenter). The view controllers who care about the change can add themselves as observers for this notification and get the information. How can they get it? A few ways:
The textbook way is to have a model on the application, probably a singleton that has properties relevant to the view controllers. Idea two is to effectively make your app delegate a model by giving it properties that the vcs can interrogate. Last idea, the userInfo param on postNotificationName:(NSString *)notificationName object:(id)notificationSender userInfo:(NSDictionary *)userInfo can convey information to the observer.
EDIT - NSNotificationCenter is pretty easy to use. It goes like this:
In AppDelegate.m, when you get an external notification:
// say you want a view controller to change a label text and its
// view's background color
NSDictionary *info = #{ #"text": #"hello", #"color": [UIColor redColor] };
[[NSNotificationCenter defaultCenter] postNotificationName:#"HiEverybody" object:self userInfo:info];
In SomeViewController.m, subscribe to the message:
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(observedHi:)
name:#"HiEverybody"
object:nil];
}
// unsubscribe when we go away
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
// this method gets run when the notification is posted
// the notification's userInfo property contains the data that the app delegate provided
- (void)observedHi:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
self.myLabel.text = userInfo[#"text"];
self.view.backgroundColor = userInfo[#"color"];
}

iOS - Using dealloc to remove observer

I have a basic question regarding removing observer.
I have a ViewController parent class which is inherited by 3 ViewController child classes.
eg. BookVC -> BookHotelVC, BookFlightVC, BookTrainVC
Here, I added an observer in the viewDidLoad of parent class (I do [super viewDidLoad] in child ViewControllers) which notifies a method written in parent class. My code-
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(BookingCompleted:) name:#"BookingCompleted" object:nil];
Now I want to remove the observer when I move away from any of the child ViewControllers, but I can't write [super dealloc] in dealloc of each child ViewController because ARC doesn't permit this.
How can I remove the observer which is set ? Because whenever I move to child ViewController, a new observer is added which causes weird things (like, calling that method twice/thrice... - invoking alert twice/thrice...).
Kindly suggest.
Removing the observers in dealloc is fine, do not call [super dealloc] (as you saw, with ARC enabled, the compiler won't let you), simply write:
- (void)dealloc {
[self removeYourObservers];
}
Just don't call super! In ARC it's not required (see http://clang.llvm.org/docs/AutomaticReferenceCounting.html#dealloc).
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

Change UILabel in View Controller from Singleton

I am very new to iOS development and I'm struggling to make an app which connects to BLE devices. As I have many view controllers I need to keep the peripheral always connected in all of them.
To achieve this, I implemented all the BLE connection methods in a Singleton. This works just great, I call the connect method from View Controller and the Singleton connects to the peripheral.
Now, the problem is I have a UILabel in my view controller which I would like to update with the connection state (scanning, connecting, connected, disconnected) from the Singleton.
So I tried to get instance from the View Controller and change the label directly like:
MainViewController *controller = [[MainViewController alloc] init];
controller.myLabel.text = #"TEST";
I also instantiated the view controller class like:
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MyStoryboard" bundle: nil];
MainViewController *controller = (MainViewController*)[mainStoryboard instantiateViewControllerWithIdentifier:#"MainVC"];
Then I tried to create a method in the main View Controller:
- (void) updateLabel:(NSString *) labelText{
NSLog(#"CALLED IN MAIN");
self.myLabel.text = labelText;
}
And call it from Singleton like:
MainViewController *controller = [[MainViewController alloc] init];
[controller updateLabel:#"TEST"]
Which was called properly (NSLog was shown) but the label was not updated.
I don't really know how to update my View Controller label from the Singleton. Don't know neither if the way I'm trying to do it is the right one or not.
Any advice or help would be much appreciated. Thanks.
----- UPDATE: -----
Thanks to Mundi and Nikita, I got a better way to implement what I need through NSNotification. For all those who need it here is how I do it:
In my View Controller in viewDidLoad I call:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateConnectionLabel:) name:#"connectionLabelNotification" object:nil];
Then in the same class I implement the notification observer method like:
- (void)updateConnectionLabel:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"connectionLabelNotification"]) {
self.connectionLabel.text = notification.object; //The object is a NSString
}
}
Then in my Singleton, when I need I call:
[[NSNotificationCenter defaultCenter] postNotificationName:#"connectionLabelNotification" object:[NSString stringWithFormat:#"CONNECTED"]];
When the View Controller receives the notification from the Singleton it updates the label with the text I add on the notification object (in this case #"CONNECTED").
You need to use NSNotification.
Here is sample code:
in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mySelector:)
name:DeviceStateChanged
object:nil];
in dealloc:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:DeviceStateChanged
object:nil];
also add a method in ViewController:
- (void) mySelector:(NSNotification *) notification {
// action performed
}
in Sigleton
- (void) foo {
/// some actions
// device connected
[[NSNotificationCenter defaultCenter] postNotificationName:DeviceStateChanged object:self];
///
}
Recommendation: move notification name to your constants and use constant name. For naming convention look at Apple guidelines
The proper way to do this is via NSNotification. This communication device is meant for exactly this kind of situation. It broadcast a message without caring whether the potential receiver is available.
In your view controllers, you call NSNotificationCenter's addObserver / removeObserver when they appear / disappear. You post the notification via postNotification:.

Why is my NSNotificationObserver deallocated while a message runs on it?

I have a situation in which it can happen, that the last strong reference to an observer is removed while the observer processes an incoming notification.
That leads to the observer being deallocated immediately. I would normally expect, that a currently running method can finish before an object is deallocated. And this is what happens during normal message dispatch.
A simplified version of the code:
TKLAppDelegate.h:
#import <UIKit/UIKit.h>
#import "TKLNotificationObserver.h"
#interface TKLAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) TKLNotificationObserver *observer;
#end
TKLAppDelegate.m:
#import "TKLAppDelegate.h"
#implementation TKLAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Create an observer and hold a strong reference to it in a property
self.observer = [[TKLNotificationObserver alloc] init];
// During the processing of this notification the observer will remove the only strong reference
// to it and will immediatly be dealloced, before ending processing.
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationName" object:nil];
// Create an observer and hold a strong reference to it in a property
self.observer = [[TKLNotificationObserver alloc] init];
// During the manual calling of the same method the observer will not be dealloced, because ARC still
// holds a strong reference to the message reciever.
[self.observer notificationRecieved:nil];
return YES;
}
#end
TKLNotificationObserver.m:
#import "TKLNotificationObserver.h"
#import "TKLAppDelegate.h"
#implementation TKLNotificationObserver
- (id)init {
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notificationRecieved:) name:#"NotificationName" object:nil];
}
return self;
}
- (void)notificationRecieved:(NSNotification *)notification {
[self doRemoveTheOnlyStrongReferenceOfThisObserver];
NSLog(#"returing from notification Observer");
}
- (void)doRemoveTheOnlyStrongReferenceOfThisObserver {
TKLAppDelegate * delegate = [[UIApplication sharedApplication] delegate];
delegate.observer = nil;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"NotificationName" object:nil];
NSLog(#"dealloc was called");
}
#end
Using the App Delegate in this way is no good style and only done for demonstration purposes, the real code does not involve the app delegate.
The output is:
dealloc was called
returing from notification Observer
returing from notification Observer
dealloc was called
That is in the first case dealloc is called before the notification processing finished. In the second case it behaves as I expected.
If I keep a strong reference to self inside notificationReceived the dealloc only happens after the processing. My expectation was, that ARC, the runtime or whoever else keeps this strong reference for me.
What is wrong with my code?
Or is something wrong with my expectation?
Is there any Apple- or Clang-provided documentation on this?
My expectation was, that ARC, the runtime or whoever else keeps this
strong reference for me.
That is not the case, as documented in the Clang/ARC documentation:
The self parameter variable of an Objective-C method is never actually
retained by the implementation. It is undefined behavior, or at least
dangerous, to cause an object to be deallocated during a message send
to that object.
Therefore, if calling doRemoveTheOnlyStrongReferenceOfThisObserver
can have the side-effect of releasing self, you would have to use
an temporary strong reference to avoid deallocation:
- (void)notificationRecieved:(NSNotification *)notification {
typeof(self) myself = self;
[self doRemoveTheOnlyStrongReferenceOfThisObserver];
NSLog(#"returing from notification Observer");
}
A better solution would probably to avoid this side-effect.
the first dealloc probably happens as you set the observer property of the appDelegate twice and therefore the first instance is dealloced as soon as you set it the second time

Resources