iOS CLLocationManager in a separate class
Create a singleton class which has a latitude
and longitude
properties, startLocating
and endLocating
. In the class, create a CLLocationManager
instance, and set its delegate to be the singleton. In startLocating
and endLocating
, call the appropriate methods of the CLLocationManager
instance. Make the delegate methods update the latitude
and longitude
properties. In other ViewControllers
, read this singleton's latitude
and longitude
properties.
To know when to read those properties from another ViewController
, set an observer on these properties (see the NSKeyValueObserving Protocol Reference
Before doing this, look up the Internets for existing code.
After doing this, upload it to GitHub with a permissive license.
Add CLLocationManagerDelegate to a separate class
As written, you have to ensure that the LocationHandler
instance is a property of the view controller, not a local variable. You want to make sure it doesn’t fall out of scope and is deallocated before the delegate method has a chance to be called.
Location Manager in seperate class in Swift
First of all please name custom structs and classes with starting uppercase letter.
The error occurs because there is no strong reference to the locationRequest
class.
Either design the class as singleton
class LocationRequest: NSObject, CLLocationManagerDelegate {
static let shared = LocationRequest()
...
}
...
class TodayViewController: UIViewController {
var locationRequest = LocationRequest.shared
override func viewDidLoad() {
super.viewDidLoad()
}
or create a lazy instantiated property
class LocationRequest: NSObject, CLLocationManagerDelegate { ... }
...
class TodayViewController: UIViewController {
lazy var locationRequest = LocationRequest()
override func viewDidLoad() {
super.viewDidLoad()
}
}
Handling CLLocationManager delegate from another class
You have several options, really. One could be making your second class a property of your first class (singleton pattern would fit nice here, I guess). Then you can either declare a protocol in your second class and notify your first class via delegate methods (non-singleton implementation) or use NSNotificationCenter
to post a notification (singleton implementation).
The second option would be to pass a block with completion handler to the second class. You could declare your method in the second class like this, for example (adjust return type and arguments of the block, if needed):
- (void)updateLocationWithCompletionHandler:(void (^)(void))completion;
Implement it so that you call the completion
block after you get the geolocation update results.
And call it from the first class like:
[self.secondClass updateLocationWithCompletionHandler:^
{
// Your completion code here.
}];
Hope this helps (sorry, didn't check the code in Xcode, get rid of typos, if any).
Location class using CLLocationManager in swift
The problem is that locUtil
, to which you have assigned your LocationUtil instance, is an automatic variable — that is, it is local to the viewDidLoad
function. Thus, when viewDidLoad
runs, locUtil
(and your LocationUtil instance) comes into existence, exists for one more line, and vanishes in a puff of smoke. Your LocationUtil instance is thus destroyed once more — taking its instance variables, such as the location manager, with it.
Thus there is no time for anything to happen. The location manager and its delegate exist for about a millionth of a second. That is not long enough to work out your location!
How to use locationManager() in multiple ViewControllers
I tried to implement a singleton CLLocationManager
class, I think you can modify the following class to implement some additional methods.
import Foundation
class LocationSingleton: NSObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
private var latitude = 0.0
private var longitude = 0.0
static let shared = LocationSingleton()
private override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLLocationAccuracyHundredMeters
locationManager.requestAlwaysAuthorization() // you might replace this with whenInuse
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
latitude = location.coordinate.latitude
longitude = location.coordinate.longitude
}
}
private func getLatitude() -> CLLocationDegrees {
return latitude
}
private func getLongitude() -> CLLocationDegrees {
return longitude
}
private func zipCode() {
// I think you can figure way out to implemet this method
}
private func city() {
// I think you can figure way out to implemet this method
}
}
iOS 10: CLManager as a separate class - receiving call backs
In LocClass.h file add the protocol method
@protocol LocationUpdateProtocol <NSObject>
@optional
// Override this method in UIViewController derived class to get back the location details.
-(void)locationUpdatedWithManager:(CLLocationManager *)manager newLocations:(NSArray *)locations;
@end
@interface LocClass : NSObject
@property (nonatomic, weak) NSObject<LocationUpdateProtocol> *delegate;
@end
Then in your .m file
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
//the usual stuff here...
if([self.delegate conformsToProtocol:@protocol(LocationUpdateProtocol)] && [self.delegate respondsToSelector:@selector(locationUpdatedWithManager:newLocations:)]) {
[self.delegate locationUpdatedWithManager:manager newLocations:locations];
}
}
Now the mainViewcontroller class should implement this Protocol
@interface ViewController () <LocationUpdateProtocol>
And the place where you initilaze location manager class, set the delegate
- (void)viewDidLoad {
locMan = [[LocClass alloc] init];
[locMan startLocationManager];
locMan.delegate = self;
...
}
//Will get the delegate callback here.
- (void)locationUpdatedWithManager:(CLLocationManager *)manager newLocations:(NSArray *)locations{
}
Get location results from another class in swift
You can define a closure to run after getting location in your GPSLocation
class. You can achieve it in two way:
You can have a variable of a block code in your GPSLocation
class like below :
typealias completionHanlder = (_ lat: String, _ lng: String) -> Void
var completion: completionHanlder?
then in your Post
class after instantiating GPSLocation
you can pass a block of codes like :
getUserLocation.completion = {
// do any stuff here
}
OR you can pass a block of code to your getGPSLocation
function. here is how to redefine your function :
func getGPSLocation(completion: (_ lat: String, _ lng: String) -> Void) {
let locManager = CLLocationManager()
var currentLocation: CLLocation!
locManager.desiredAccuracy = kCLLocationAccuracyBest
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse || CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways) {
currentLocation = locManager.location
let latitude = String(format: "%.7f", currentLocation.coordinate.latitude)
let longitude = String(format: "%.7f", currentLocation.coordinate.longitude)
let location = CLLocation(latitude: currentLocation.coordinate.latitude, longitude: currentLocation.coordinate.longitude)
fetchCountryAndCity(location: location, completion: { countryCode, city in
delegate?.fetchedLocationDetails(location: location, countryCode: countryCode, city: city)
}) { delegate?.failedFetchingLocationDetails(error: $0) }
debugPrint("Latitude:", latitude)
debugPrint("Longitude:", longitude)
completion(latitude, longitude) // your block of code you passed to this function will run in this way
}
}
and this would be your Post
class :
class Post {
func getLocation() {
// Get user GPS location (longitude, latitude, Country Code, City)
let getUserLocation = GPSLocation()
getUserLocation.getGPSLocation { (lat, lng) in
debugPrint("Latitude:", lat)
debugPrint("Longitude:", lng)
// HERE I NEED to PRINT longitude, latitude, Country Code, City
}
}
}
This closure you put here would run whenever completion block called in getGPSLocation
function as I rewrite your function above.
How do I get Location coordinates to update in another class?
If you keep the location manager as an instance variable, you can access them from anywhere by accessing the current application's delegate property like this.
if let delegate = UIApplication.sharedApplication().delegate as? AppDelegate {
let manager = delegate.locationManager
}
The reality though is that if you are trying to improve your app architecture, I would not personally put the location logic in your app delegate. That feels like the wrong decision to me. If I were you, I would make a LocationManager singleton class to address both issues.
If you need dependent classes to update in real-time, I would post an NSNotification and listen for it on the classes that need to update.
Related Topics
How to Draw a Single Point Line in iOS
Wrap Items in a Horizontal Uistackview on Multiple Lines
How to Print Boolean Flag in Nslog
Centering a View in Its Superview Using Visual Format Language
How to Send Multiple Parameterts to PHP Server in Http Post
Is Possible to Simulate Touch Event Using an External Keyboard on iOS Jailbroken
Replace Occurrences of Nsnull in Nested Nsdictionary
Will iOS Region Monitoring Call Didenterregion on My App If My App Is in a Terminated State
Aes/Cbc/Pkcs5Padding in iOS Objective C Result Differs from Android
Xcode 6 Gm - Cllocationmanager
How to Use Call Directory Extension to Identify a Incoming Call in My Application
Xcode 10, Command Codesign Failed with a Nonzero Exit Code
Error: Cuicatalog: Invalid Asset Name Supplied: (Null), or Invalid Scale Factor:2.000000
Avcapturesession Audio Doesn't Work for Long Videos
iOS Getting Location Updates When App Terminated Without Using Significantchange
Hide Device Volume Hud View While Adjusitng Volume with Mpvolumeview Slider