2

Hi all am developing a app using swift 2 in my app am updating current location for every x minutes so am using NSTimer am able to update location and everything works fine.In my app(first view controller) when a user successfully logged in and moves to second view controller in(second view controller) i wrote the function for updating location using NSTimer it works fine but when the user moves to third view controller and return to second view controller it again start updating location so i cant able to stop the timer twice it invalidate timer one time only other one keep on updating the location even after the users logged out.so please someone help me out to solve this problem.

my second view controller:

class secondviewcontroller:UIViewController{

in view did load method:

  var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
             appDelegate.timer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: #selector(timeToMoveOn), userInfo: nil, repeats: true)

 //i declared var timer:nstimer! in appdelegate.swift and i performed some function using nstimer

my third view controller:

I created a button action for logout code is:

  var appDelegate:AppDelegate = (UIApplication.sharedApplication().delegate as?
            AppDelegate)!
         appDelegate.timer.invalidate()

Thanks in advance

Hi all thank you for your support i found the answer for my question

i just kept timer.invalidate() in two different methods according to my project

antonyraja
  • 13
  • 7

4 Answers4

2

Instead of pushing from third view controller to second view controller. You can pop to second view controller. Then your viewDidLoad will not call.

User511
  • 1,456
  • 10
  • 28
  • this app should update locations when users logged in then user will perform something on other view controller so they need to work on second view controllers....i cant stop timer before user logged out – antonyraja Nov 10 '16 at 07:37
  • Can you please explain a bit more? – User511 Nov 10 '16 at 07:44
  • am developing a app for drivers when a user successfully logged in it moves to other controller it should update current location of user(driver) for every x minutes so that passenger can easily notify where the driver in exact place.in that second view controller(after successfull login) there is a controller which display upcoming trips for driver in that controller only i wrote functions for updating location and there is many controllers like current trip and finished trip – antonyraja Nov 10 '16 at 08:04
  • so the driver will visit other controllers also if they again comes to second controller means it again updating current location so timer fuction calls again and i cant able to stop the timer twice...understood????...thanks for your support – antonyraja Nov 10 '16 at 08:06
  • Can you write what you are doing when user again comes to second view controller? – User511 Nov 10 '16 at 08:08
  • in second vc: var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate appDelegate.timer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: #selector(timeToMoveOn), userInfo: nil, repeats: true).....then i created function timetomoveon and it performs if i moves to other controller and return to second vc means it again calls that function and again updating the location – antonyraja Nov 10 '16 at 08:14
  • i want to use that function only once even after am returning to that controller...how to do that – antonyraja Nov 10 '16 at 08:17
  • vc: var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate appDelegate.timer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: #selector(timeToMoveOn), userInfo: nil, repeats: false) – User511 Nov 10 '16 at 08:29
  • Set repeats false and check – User511 Nov 10 '16 at 08:29
  • if i set false means it will execute only one time...i want to update location for every x minutes..so i have to keep true – antonyraja Nov 10 '16 at 09:05
  • You mean to say suppose it updates at 10 sec. Then you push to third view controller. When you will come back . It started from 1 sec. But it should not. Correct? – User511 Nov 10 '16 at 09:06
  • like that only..once nstimer called it should keep update current location for every 10 seconds still users logged out from the app..when the user moves to that same controller it should not call that nstimer fnction again – antonyraja Nov 10 '16 at 09:11
  • What is inside this? - #selector(timeToMoveOn). Will you write that? – User511 Nov 10 '16 at 09:37
  • i found the solution by invalidate timer in two different places...it works fine...thank you for your patience and support – antonyraja Nov 10 '16 at 10:14
  • in func timetomoveon:i just print(latitude) and (longitude) – antonyraja Nov 10 '16 at 10:28
  • Ok I think it works for you by invalidating timer on 2 sides. – User511 Nov 10 '16 at 10:32
2

I would suggest to do this:

this singleton to wrap location.

Sorry, for swift3 but it have look something like this:

class LM: NSObject, CLLocationManagerDelegate {
    //MARK: Public Variable

    var lat = 0.0
    var lon = 0.0
    var CAST = "location_cast"
    public static let i : LM = {
        let instance = LM()
        return instance
    }()

    //MARK: Local Variable

    fileprivate var locationManager: CLLocationManager?

    //MARK: Init
    public override init() {
        super.init()
        locationManager = CLLocationManager()
        locationManager?.delegate = self
    }
    internal func start() {
        locationManager?.startUpdatingLocation()
    }
    internal func stop() {
        locationManager?.startUpdatingLocation()
    }
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        //code
        print("\(error)")
    }
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        //code
//        print("\(locations)")
        if let loc = locations.last {
//simple store to retrieve it later
            lat = loc.coordinate.latitude
            lon = loc.coordinate.longitude


                //cast here notification
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: CAST), object: ["lat": lat, "lon": lon])
        }

    }
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        switch status {
        case .notDetermined:
            manager.requestAlwaysAuthorization()
            break
        case .authorizedWhenInUse:
            manager.startUpdatingLocation()
            break
        case .authorizedAlways:
            manager.startUpdatingLocation()
            break
        case .restricted:
            // restricted by e.g. parental controls. User can't enable Location Services
            break
        case .denied:
            // user denied your app access to Location Services, but can grant access from Settings.app
            break
        }
    }
}

to start and stop this locationmanager use this:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
//...
var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
// Override point for customization after application launch.
        _  = LM.i
        return true
    }

func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.

       LM.i.stop()
    }

 func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

        LM.i.start()
    }
}

You can add inside AppDelegate your custom timer with casting variables each 1 second

let CASTTOVIEWCONTROLLER = "CASTTOVIEWCONTROLLER"
 NotificationCenter.default.post(name: NSNotification.Name(rawValue: CASTTOVIEWCONTROLLER), object: ["lat": LM.i.lat, "lon": LM.i.lon])

or even simple

 let CASTTOVIEWCONTROLLER_PING = "CASTTOVIEWCONTROLLER_PING"
NotificationCenter.default.post(name: NSNotification.Name(rawValue: CASTTOVIEWCONTROLLER), object: nil)

to catch a new data from casted values use something like this:

https://stackoverflow.com/a/38615219/1979882

If you will not manage .start() and .stop() iOS will kick your background timer application each in several minutes if your will press 'home' button.

Community
  • 1
  • 1
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
  • will this execute on when app in background?? – antonyraja Nov 10 '16 at 07:39
  • am fetching current location in three view controllers..first one is for login because i have to check the user lat long second one is for updating current location for every x minutes and third one is am using mapview...will your code cause any problem to other view controllers location or it will stop timer in particular view controller.......thanks for your support – antonyraja Nov 10 '16 at 07:49
  • this location listener works if app is in foreground. It doesn't matter which viewcontroller is in foreground. – Vyacheslav Nov 10 '16 at 08:24
  • i want work when app is in background also...will this help – antonyraja Nov 10 '16 at 08:28
  • In this case, you have to use background fetch. – Vyacheslav Nov 10 '16 at 08:58
  • @antonyraja , so, approve this or the other answer if it was helpful. – Vyacheslav Nov 10 '16 at 09:10
  • still not check...due to some issue in other project..if i done means surely i will accept the answer...thank you man – antonyraja Nov 10 '16 at 09:23
2

timer's target is second viewController, second viewController owns this object, AppDelegate only reference to this object, AppDelegate can't release this object and only secondViewController can. Unless secondViewController is destruction

so:

var timer:NSTimer//global variable

in secondViewController's viewDidLoad method:

timer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: #selector(timeToMoveOn), userInfo: nil, repeats: true)

in thirdViewController:

let timer =  (secondViewControllers object)'s timer
timer.invalidate()
Adam Bardon
  • 3,829
  • 7
  • 38
  • 73
J.Doe
  • 86
  • 1
  • 5
  • everything works fine...but when the user moves to second view controller again and again it call that function again and keep on updating location...if users moves twice or three times to that controller i can able to invalidate timer only once...other things keep on updating – antonyraja Nov 10 '16 at 08:10
1

As I see your code, Please add a flag or Bool variable in your AppDelegate Class. Now when you comes on second screen from First screen the flag will be enable and when you want to navigate to third screen set false to flag and by checking flag is true or not your timer will be run by true condition. Now when you again come in second screen from 3rd and if timer case again arise then plz refer this link: NSTimer Not Stopping When Invalidated

Hopes it will helps you

Community
  • 1
  • 1
Gourav Joshi
  • 2,419
  • 2
  • 27
  • 45