Getting current location but only once - ios

In my ViewController I need to update to current location on viewDidLoad just to download right data from server. Data should be downloaded only once just to fill the tableView but user has right to pull-to-refresh which will get the current location and call reloadData.
I don't want this data to be reloaded automatically when locationManager:didUpdateLocations: method is called what is the cleanest way to refill the tableView. another soultion is KVO on currentLocation but the results are the same - need to call reload everytime location is updated. It should be managed by user manually (just like in example above). I don't have any idea how to do this without raloading tableView every time locations are updated.
Any lead in this case would be a big help!

User [locationmanager stopUpdatingLocation] in locationManager:didUpdateLocations: and on pulltorefresh [locationmanager startUpdatingLocation].
To update a particular cell you can find the reference from the table view and try to get the reference of the control to update that
Hope it will help you.

Accepted answer is the right one for my purpose and it's totally sufficient but... Ifanyone ever would be interested in ready soultion which is clean, nice and supports Cocoapods you should try INTULocationManager.
This little library gives you a pretty handy methods which will wait for your localization or give you back the best possible result. Everything packed in nice block syntax.

Related

Alternatives to CoreLocation's requestLocation method

I have an IOS app where I require the user's current location in order to make an HTTP request to get data about nearby locations. Since I just need the user's location once, I am using the CoreLocation requestLocation method. This issue with this is that it takes roughly 10 seconds (which is stated in the documentation). I realize that there will always be a delay which I am prepared for, but the delay on requestLocation is just too long and ruins the user experience.
I was wondering if anyone has thought of a way to get the users location (just once) in a more timely way.
One solution I thought of was perhaps calling the method within the AppDelegate, as the app starts, and then passing the data to the ViewControllers via some shared object. The issue I foresee here is that AppDelegate does complete it's application:didFinishLaunchingWithOptions: function very quickly so I don't think it'll improve my problem
Another solution I thought of could be to use the startUpdatingLocation method, and then use stopUpdatingLocation after receiving the first location. My issue with this is that startUpdatingLocation may also take a few seconds to start up (documentation), so again this may not improve my problem.
The location I get for the user does not need to be super accurate so I am willing to compromise on precision for performance.

Where to put code that gets checked frequently?

I have some code that needs to get called frequently, such as check what day it is, if it's the next day then move the day strings in the tableView.
Now I thought that the viewDidLoad would get called all the time and so it would be 'fine' to put it in there. However, I've left the simulator overnight, and I've pressed the home button and clicked again, changed VCs etc. and viewDidLoad hasn't been hit.
What are my options for doing sporadic checks such as, is it a new day? As x happened etc.
In this specific case, you can subscribe to NSCalendarDayChangedNotification to be notified when the date changes and respond accordingly in your view controller. In general, didBecomeActive or viewDidAppear would likely work.
What are my options for doing sporadic checks such as, is it a new day
It depends what the meaning of "is" is! In particular, "is" when? You say "sporadic", but that's just fluff. When do you need to know this? To what stimulus do you want to respond? When the user opens your app? Then put it in applicationDidBecomeActive. Every day at noon? Then run an NSTimer. Really, the problem here is that you don't seem to know, yourself, just when you need to perform these checks.
Whilst in your app, its quite easy to continually check for something. You simply create a background thread. However, what you describe is a thread that persists from outside the app's lifecycle.
Have a read on this documentation provided by Apple itself. You have to have good excuse to put a background thread. The scope of such thread is limited to only certain scenarios such as downloading background stuff, playing sounds etc.
For your scenario, I'd look at applicationDidBecomeActive(_:) found in your Application Delegate. There you can mimic such continual check. Beware however, don't put heavy word load on start up or your app might be killed automatically if it fails to become active in reasonable amount of time.

Add UIProgressView for [PFObject pinInBackground] to monitor when pin is complete

I think this is a fairly simple question...but we'll see about that.
My setup:
Xcode: 6.3.2;
Parse: 1.7.4;
Language: Obj-C
I have a bunch of PFObjects that are displayed in a TableView and within each Cell there is a button that the use can tap to pin that individual PFObject to localDatastore, so if they were to lose WiFi later on he/she could still access that object and view its contents.
What I would like to do is display a UIProgressView/UIProgressBar to monitor the progress of the object being pinned (some of my objects contain large files that may take up to 30 seconds to pin). This way the user knows for sure that the object has been saved completely and there is smaller chance they will assume the object save immediately and turned off WiFi or something else happens to jeopardize his/her internet connection.
Is there straightforward way to do this with Parse??
Negative.
PFObject currently doesn't have a 'save' with a progress block like a PFFile does (https://parse.com/docs/ios/api/Classes/PFFile.html#//api/name/saveInBackgroundWithProgressBlock:). See SDK reference here for future references. This is a great resource I suggest you save it to your bookmarks, not specifically the PFObject page but the SDK API reference in general: https://parse.com/docs/ios/api/Classes/PFObject.html
There are ways to do this, mostly personal preference, however its just like any other async call, you will have to populate a progress HUD yourself, since its not API friendly yet. This is a fairly simple process to start yourself, and as a developer you should learn how to do this anyways, you learn a lot about network calls and how tasks operate on threads etc. In the meantime, as a quick fix you could simply just populate a UIActivityIndicator collectively with pinInBackgroundWithBlock: and set userInteractionEnabled to No for whatever views you want simply as a visual aide on the users end, and then hide it when complete and set userInteractionEnabled back to Yes.

Objective C: get location without delegate

I have a problem in Objective C (writing an app for the iPhone). I know how to use the locationManager and its delegates. Once I called startUpdatingLocation on the locationManager, it will call the delegate method didUpdateToLocation whenever the location is updated. But the problem is, that this way is now suitable for what I want to do. I have a method (in a class) which looks as follows:
#implementation SomeClass
- (someType)getDbContentsOrderByDistance
{
... //here I need the current location
return dbContents;
}
#end
Now this function selects entries from a sqlite database ordered by their distance to the users current location. That is why I need the location in this function. What I want to do in the above method is: get the location, then select the stuff from the database and return it. I know that I could just try to access locationManager.location.coordinate.longitude, but I know that there won't be anything useful inside until the first location update has arrived. And I don't want to start the location updates before (e.g. when starting the app) because that would not be very efficient if I only need the location once.
I know how to do it the way that the delegate is called as soon as a location update arrives. The problem is that I don't know how to get that updated location from there into my method above. Basically I would need to let the method 'wait' until there is the first location update and then continue execution with that location.
Thank you for your help!
The short answer is, you can't. Getting the users location is an asynchronous process, and MUST be an asynchronous process. The device has to fire up various hardware like the GPS, cell tower locator, and WiFi triangulation system, get input from those different devices, and synthesize that into a location. That takes multiple seconds to do.
Putz's suggestion of starting a timer is a good one. I would add a few things however.
Typically the first readings you get from the location manager are really bad and should be discarded. The first reading you get is usually the last location reading when the GPS was active, and will have an out-of-date timestamp. The accuracy reading in that location might appear quite good, but it's a lie. I have seen the first location reading be off by several kilometers. You need to check the timestamp on your location updates and discard any reading that is more than 1 second old.
Once you've discarded the stale readings, the first several location updates are often really bad because the GPS hasn't settled down yet. The horizontal accuracy reading (which is really a "circle of confusion", or radius of possible positions) is an absurdly large value, sometimes a kilometer or more. Again, you need to write your location manager delegate method to discard readings that are too inaccurate. I suggest discarding values with a horizontal accuracy reading of >= 100 meters. How much inaccuracy you can tolerate depends on the specific appellation, but beware of making the accuracy requirement too accurate. Sometimes the GPS refuses to settle down. If you require a 5 meter accuracy, you might not get an acceptable accuracy during the entire run of your app. (If you're in an urban environment, a building, or other area where there is a lot of interference/obstruction of GPS signals)
Once you finally do get a value that's accurate enough, save the location and set your locationAvailable flag to YES.
Note that instead of a timer you could use the notification manager to broadcast a "got a good location" message. Any object that needs location information could register for your notification, and get called when the location manager gets an acceptable location reading.
Start a timer that fires every second or so that calls the function you need the location in. check to see if the location has been set. If the location is valid then kill the timer, if not then let the timer continue and check again in one second or so.
The way of knowing if the location is valid is in the delegate method. Set a global variable like locationAvailable = false. Then when the delegate method gets called set that variable to true.
Using location property can solve the purpose
From apple documentation
location The most recently retrieved user location. (read-only)
#property(readonly, nonatomic, copy) CLLocation *location
The value of this property is nil if no location data has ever been retrieved.
In iOS 4.0 and later, this property may contain a more recent location object at launch time. Specifically, if significant location updates are running and your app is terminated, this property is updated with the most recent location data when your app is relaunched (and you create a new location manager object). This location data may be more recent than the last location event processed by your app.
It is always a good idea to check the timestamp of the location stored in this property. If the receiver is currently gathering location data, but the minimum distance filter is large, the returned location might be relatively old. If it is, you can stop the receiver and start it again to force an update.
Whenever I create a new instance of location manager I get the most recent value of location in location property and since my distance filter is not set , it has the current timestamp. This means I dont have to call startUpdatingLocation and my data is accurate. I also need this for calculating distance between user location and a place so I can immediately return true or false if it is within the range. I find this very useful as I get location within 0.006 seconds and handy and got it from apple documentation only but still I dont know if it is a best practice.

When close view that started locations service notification error occure

I have created small app that use this project as it's core:
https://github.com/dsdavids/TTLocationHandler
And it worked fine until I have moved stating location services from another view in app.
What app is doing: When it is started you can tap on START button and (in emulator locations must be enabled) on the map route of movement is displayed as you move.
The problem came when I moved starting action in a second view.
In that second view I just want to start location service and close it.
The problem is when I start locating on the second view I get error (application crash EXC_BAD) here:
TTLocationHandler
...
dispatch_async(dispatch_get_main_queue(), ^{
if (OUTPUT_LOGS) NSLog(#"Sending notification out");
NSNotification *aNotification = [NSNotification notificationWithName:LocationHandlerDidUpdateLocation object:[locationToSave copy]];
[[NSNotificationCenter defaultCenter] postNotification:aNotification];
});
...
I think that it is because I close the second view (view that started service) and TTLocationHandler still tries to send it something.
For better understanding my problem I have added project at git hub:
https://github.com/1110/common-location-features
You can download it and run start service from second view and when close that view app will crash in a few seconds.
I would be really thankful if someone can find a little time to tell me what am I doing wrong here as I am sure that it is some small thing that I probably doing wrong.
Whole code is in SecondViewController.m
Thanks
The problem is probably that the location manager is sending location updates to an object that no longer exists.
I don't have the time to dig through all of your code, but generally speaking once you tell the location manager to startUpdatingLocation, you need to keep your location manager delegate object around until you tell it to stop. If what you refer to as the "second view" is your delegate object, then you can't let that view get deallocated until you tell the location manager to stopUpdatingLocation.
Generally you want to have one object be the CLLocationManagerDelegate, and keep that object around for as long as you need it. The delegate will get notified every time the location changes, and it is the delegate's responsibility to update any views that care about the location.

Resources