Function after presenting Alert Swift - swift

TLDR
Is there a way to be notified before a UIAlert is going to be presented. Is there a viewDidLoad/viewWillLoad type function that can be called in a ViewController either before or after an alert pops up?
My Problem
My View Controller is receiving an alert from a method in my app delegate. My View Controller calls a method in the app delegate which can then send a UIAlert if there was a problem. While this seems like bad design, I can't change it. I need some type of way of knowing that an alert showed up.

You can try
// do before
self.present(alert,animated:true) {
// do after
}
Sol1:
Add a completion block when you call the Appdelegate func and inside app delegate return that completion like code above in // do after
Sol2:
set a delegate between app delegate and the VC that calls it
Sol3:
use
NotificationCenter.default.addObserver(
self,
selector: #selector(listenForNotification),
name: Notification.Name(rawValue: "AlertShowed"),
object: nil)
inside the VC
and this in Appdelagete alert completion
NotificationCenter.default.post(name: Notification.Name("AlertShowed"), object:"")

The function present(_:animated:completion:) takes an Optional completion handler, completion. If you provide a completion handler it gets called once the modal is displayed. You display UIAlertControllers using present(_:animated:completion:), so just pass in a completion handler.

If you are opening the alert from within a method in appdelegate, then you should some how keep a reference on your view controller and call a specific method on it before showing the alert.
Or even, you can implement a pub sub design where you’d have your controller register for a custom notification name on the Notification Center and call/post that notification from the appdelegate just before showing the alert, don’t forget about removing the observer once the controller gets closed.
The choice is yours

Related

Avoid losing notifications sent by a widget because of a spash screen or wrong controller active

I have an widget that calls its corresponding app through NSURL and extensionContext to activate a specific action in the app.
In AppDelegate's application:openURL:options: method I have:
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
if let path = url.path{
if path.containsString("action"){
NSNotificationCenter.defaultCenter().postNotificationName(MyViewController.purchasmyActionKey, object: nil)
}
}
return true
}
When the app is open, and has MyViewController active, the action is executed perfectly. But, if I am on another view controller in the app or the app is closed, the action is not executed.
Can somebody set me on the right track?
NB: My main controller is a UITabBarController with various child view controllers. Some are UINavigationControllers (which contain grid controllers) and the other one is a ListViewController.
The simplest option is to show your view controller which handles this as a modal over the tab controller. This is generally the least complex and the cleanest as the user can be easily returned to what they were doing before this interaction when they're done.
If you can't do that for some reason:
You need to designate some class the responsibility of ensuring that the correct view controller is shown and told to action the request when the notification is seen. That could be the app delegate directly, the tab bar controller or some other specific class that you create and provide with a reference to the tab controller.
It's job is to check the state of the tab controller and show the correct view controller if required, then tell that view controller to begin some action.
This class who owns this logic could be the one that observes your notification, or you could just pass the message directly as your app delegate will likely know the instance or be creating a new instance.

Why would alert view delegate method not get called?

In my interface file I said I conform to the UIAlertViewProtocol and I implemented the alertView:clickedButtonAtIndex: method in my implementation file, and normally whenever the alertview button is pressed (the button that makes the alert go away)that method gets called. Well, it gets called most of the time, but for one of my alert views it doesn't get called after I press the cancel button on it, what would be a reason for this?
Oh Its because in one of my alert views for the delegate parameter i passed in nil instead of self, thats embarassing.

Application comes to foreground from background

When application comes to foreground from background then appixationDidBecomeActive gets called. Now, I want to refresh the page from which the home button was tapped. In which method I can write the refresh code. viewWillAppear is not getting called.
In this case viewWillAppear never gets called, you can fire notification(s) whenever -(void)applicationWillEnterForeground:(UIApplication *)application gets called and use it on the view you want.
You can use custom notification like : NSNotificationCenter.
And on call of this you can refresh your view.

Objective-C object dealloc'd while other objects still has a delegate reference to it causes crashes. How to prevent this?

I have an app with a navigation controller as the root view. There are many views that can be pushed in.
The user has to create an account to use the app. The user can then log into this account from other devices, but only one device can be logged onto the same account at a time. So if multiple devices try to log into an account, only the latest device will be logged in and the other devices are logged off (a push is sent to the devices).
Since there are multiple views that the device could be showing before it was logged off, I call popToRootViewControllerAnimated: to get back to the root view. This is because when the user logs in the next time I only want the root view to be shown (the new account might not have access to the previously shown view).
If the user has an alert view or action sheet presented (which uses the current view as its delegate) before the push is received, the view will still be shown after the popToRootViewControllerAnimate: method is called. If the user then taps on a button for the alert view or action sheet, it will send a message to the dealloc'd view and crash the app.
An example:
myViewController is being shown to the user.
myViewController create an action sheet prompting the user for a decision.
The push is received for the device to log out.
The navigation controller pops all the views controllers and now shows myRootViewController.
Since the view controllers are popped, myViewController is now dealloc'd.
The action sheet from myViewController is still shown.
When the user selects an option form the action sheet, a message is sent to myViewController, and since it is already dealloc'd, a crash will occur.
Is there any way to prevent this?
One solution I have considered would be to keep track of all the objects that uses a specific view controller as its delegate. Then when that view controller dealloc's it will also set all the object's delegates to nil. This requires me to manually take care of every view controller when they create an object that uses itself as the delegate, since I cannot think of a way to automatically create and update this list.
Any better solution (or improvement to mine) would be appreciated!
Edit: The alert view and action sheet are only examples of some objects that I would use myViewController as a delegate. I am also using a number of other classes (and third-party libraries) that implements this delegate pattern.
A few ideas:
you can encapsulate the alert/action sheet view and delegate in a single class. Then when you need an alert view, create MyAlertView instead, which will also be its own delegate and will do [self release] after the user taps a button.
make your App Delegate the only delegate for all your alert views and action sheets. App Delegate is always around while the application is running, so there won't be a problem with a released delegate.
The problem with both solutions is that if you need your application to know what happened in the alert view/action sheet, you somehow need to tell the interested class of the user's choice.
You can do that by either using delegates of your own - which would mean you're back to square one - or use notifications: when the alert view/action sheet delegate is called, it would post a notification ([[NSNotificationCenter defaultCenter] postNotificationName:NotificationName object:self userInfo:userInfo];), while the interested object would look for that notification ([[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onNotification:) name:NotificationName object:nil];) and perform whatever tasks necessary in onNotification:(NSNotification*)aNotification method.
You'll be able to agree with yourself on what type of information is passed in those notifications (I would think the button number in a NSNumber class would be enough, or perhaps pass the button text, too). And you won't have to keep track of all alert views - just don't forget to remove the observer ([[NSNotificationCenter defaultCenter] removeObserver:self name:postNotificationName object:nil];) in the views' dealloc.
Edit:
"This requires me to manually take care of every view controller when they create an object that uses itself as the delegate, since I cannot think of a way to automatically create and update this list."
Actually you probably can do this in a semi-automated way: make a singleton object with a method like
-(id)delegate:(id)delegate for:(id)forWhom
And then instead of
someThingy.delegate = self;
you'd do
someThingy.delegate = [[DelegateLocker defaultLocker] delegate:self for:someThingy];
Inside the DelegateLocker you'd have a MutableDictionary with delegate class as a key and a MutableArray of someThingies as a value. Then in your view controllers' deallocs you'd call
[[DelegateLocker defaultLocker] delegateIsDying:self];
which would go through the thingies and assign delegate = nil for each
The drawback of course is that you'll be retaining all the thingies for an indefinite period of time instead of releasing them immediately.
So the ViewController that presented the action sheet iand set itself as the delegate right? So why dont you keep a reference to the ActionSheet in the ViewController, in the dealloc method of the view controller, you can check if the action sheet is visible, if it is then set the delegate of the action sheet to nil,and dismiss it...
so
-(void)dealloc
{
if(myActionSheet && [myActionSheet visible])
{
[myActionSheet setDelegate: nil];
//dismiss
}
}
Hope that helps
If you want automated solution, I think you can make a function to iterate through Ivars of your view controller to see if any Ivar has delegate property and set it to nil.

iphone sdk calling dismiss alert function from another class?

how can i call dismiss alert function from another class?
Set the alert delegate to class where you want to receive button callbacks..