How to perform delayed selector safely iOS - ios

In viewDidAppear I show a popup to users after 3 seconds. What if user navigates to another viewController after timer begins. The selected function will try to execute & show popup when superview is no longer on screen. App does not crash or throw any errors but I want to confirm this is safe. Should I set a BOOL and assert isCurrentView is YES, within selector method?
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self performSelector:#selector(showPopup) withObject:nil afterDelay:2.5];
}

in viewDidDisappear
-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:(BOOL)animated];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(showPopup) object:nil];
}

Related

How can I observe beingPresented, beingDismissed properties of UIViewController Object?

I want to know how I can get to know when a viewController appears, disappears from the main window from the UIApplication. I don't want to put code in each and every UIViewController, but observe lifecycle of each viewcontroller from the UIApplication.
Every view controller has a life cycle. So every view controller has separate life cycle method. So you have to put code each and every UIViewController. In app delegate we check the application state.
The state's are:
Active state
Inactive state
Background state
Not Running state
Suspended state
Delegate Method:
application:didFinishLaunchingWithOptions:
applicationWillResignActive:
applicationDidBecomeActive:
applicationDidEnterBackground:
applicationWillEnterForeground:
applicationWillTerminate:
View Controller Life cycle method:
-(void)viewDidLoad:(BOOL)animated{
[super viewDidLoad:animated];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}

Cancel perform selector method in recursion

I am repeating a function again and again using this code
- (void)refresh {
[self performSelector:#selector(refresh) withObject:nil afterDelay:5.0];
}
On viewWillDisappear I wrote the code to cancel this but still the function called. How can we cancel this perform selector repeating itself ?
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(refresh) object:nil];
}
please First of all , check this you might be running on different run loops in creation and cancellation.I faced this problem in past.
or
I think no need to write selector again. Just right this and try.
With this
// cancel the above call (and any others on self)
[NSObject cancelPreviousPerformRequestsWithTarget:self];
Refer apples document.
NSObject_Class
Discussion All perform requests are canceled that have the same target
as aTarget, argument as anArgument, and selector as aSelector. This
method removes perform requests only in the current run loop, not all
run loops.

Auto Signout timer in app?

What I need to do is this; I will have a timer that will tick away and when 30 minutes is up I'll auto signout the user. But if there's any interaction with the application I will reset the timer to 30 min. I have an idea on what to do but I'm sure there's a better way to accomplish this.
What I'll do is make a singleton class that holds a timer and posts a notification when the timer is up. So what I'm thinking is I'll have to reset the timer when ever the user presses a button, goes to the next screen etc.
My quesiton though is is it possible to respond to any touches in the app in one piece of code? Like somehow there's a superclass I can add this to and it will always reset the timer no matter what kind of interaction has happened? Or do I need to add the code to all the places where the user will interact with the application?
You can try this, subclass UIApplication and add following code in implementation
#implementation MyApplication
- (instancetype)init {
self = [super init];
if (self) {
[self reset];
}
return self;
}
- (void)reset {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(logout) object:nil];
[self performSelector:#selector(logout) withObject:nil afterDelay:30*60];
}
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
[self reset];
NSLog(#"event detected");
}
- (void)logout {
NSLog(#"logout now");
}
#end
Then in main.m change the implementation like this
return UIApplicationMain(argc, argv, NSStringFromClass([MyApplication class]), NSStringFromClass([AppDelegate class]));
Here what is happening is, - (void)sendEvent:(UIEvent *)event method will get called after each user activity, Then we are registering a perform selector request after 30 mins. Once user touches the screen within 30 mins cancel previous request and register new one.

How is the better way to create background loop routines in objective-c?

I need to create a routine that save automatically a file content in a constant time period, ie, a backgroung loop that perform the save instructions. I thinked in use a recursive call of performSelector like below:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self performSelector:#selector(saveMethod) withObject:nil afterDelay:kTimeConstant];
}
- (void)saveMethod{
//The save logic should to be here
[self performSelector:#selector(saveMethod) withObject:nil afterDelay:kTimeConstant];
}
It works, but when I get out of viewController, it still running, and it must to stop.
are there any better way to execute it? Thank you!
This is probably a better implementation:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Start timer and sets it to a property called saveTimer
self.saveTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(saveMethod:)
userInfo:nil
repeats:YES];
}
- (void)saveMethod:(NSTimer*)theTimer {
// The save logic should to be here
// No recursion
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Stop timer
[self.saveTimer invalidate];
}
This is running on the main thread so it is probably not the best implementation but it should work better than what you currently have.
There is a function NSRunLoop cancelPreviousPerformRequestsWithTarget:selector:object: which allows you to cancel the performSelector call. Call this when you unload the view controller
ie.
[NSRunLoop cancelPreviousPerformRequestsWithTarget:self selector:#selector(saveMethod) object:nil];

Dealing with two screens and one activity indicator in iOS

I have 3 screens on my app.First is login. Second is search and third is process the task.
On login i retrieve data from a web service. It returns data in XML format. So the data is considerably large. So i am doing that task on a background thread like this to stop Mainthread freezing up on me:
-(BOOL)loginEmp
{
.....some computation
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
[self getAllCustomerValues];
});
}
-(void)getAllCustomerValues
{
....more computation.Bring the data,parse it and save it to CoreData DB.
//notification - EDIT
NSNotification *notification =[NSNotification notificationWithName:#"reloadRequest"
object:self];
[[NSNotificationCenter defaultCenter] postNotification : notification];
}
//EDIT
//SearchScreenVC.m
- (void)viewDidLoad
{
....some computation
[self.customerActIndicator startAnimating];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(stopActivityIndicator)
name:#"reloadRequest"
object:nil];
}
- (void)stopActivityIndicator
{
[self.customerActIndicator stopAnimating];
self.customerActIndicator.hidesWhenStopped = YES;
self.customerActIndicator.hidden =YES;
NSLog(#"HIt this at 127");
}
So on condition that login was successful, i move to screen 2. But the background thread is still in process( i know because i have logs logging values) . I want an activity indicator showing up here (2nd screen)telling user to wait before he starts searching. So how do i do it?How can i make my activity indicator listen/wait for background thread. Please let me know if you need more info.Thanks
EDIT: so I edited accordingly but the notification never gets called. I put a notification at the end of getAllCustomerValues and in viewDidLoad of SearchScreen i used it. That notification on 2nd screen to stop animating never gets called. What is the mistake i am doing.?Thanks
EDIT 2: So it finally hits the method. I dont know what made it to hit that method. I put a break point. I wrote to stop animating but it wouldn't. I wrote hidesWhenStoppped and hidden both to YES. But it still keeps animating.How do i get it to stop?
Ok, if it is not the main thread, put the following in and that should fix it.
- (void)stopActivityIndicator
{
if(![NSThread isMainThread]){
[self performSelectorOnMainThread:#selector(stopActivityIndicator) withObject:nil waitUntilDone:NO];
return;
}
[self.customerActIndicator stopAnimating];
self.customerActIndicator.hidesWhenStopped = YES;
self.customerActIndicator.hidden =YES;
NSLog(#"HIt this at 127");
}
Could you put your background operation into a separate class and then set a delegate on it so you can alert the delegate once the operation has completed?
I havent tried this, its just an idea :)
You could use a delegate pointing to your view controller & a method in your view controller like:
- (void) updateProgress:(NSNumber*)percentageComplete {
}
And then in the background thread:
float percentComplete = 0.5; // for example
NSNumber *percentComplete = [NSNumber numberWithFloat:percentComplete];
[delegate performSelectorOnMainThread:#selector(updateProgress:) withObject:percentageComplete waitUntilDone:NO];

Resources