How to Monitor places swift 4 - ios

i work on app where i monitor places and send local notification when enter region.
i save these places in realm database and check for region.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Updated user's location")
shouldSetRegion = false
updateLocation()
print((locations.last?.coordinate.longitude)!)
if !(realm.objects(Coupon.self).isEmpty) {
for item in realm.objects(Coupon.self) {
let lat: CLLocationDegrees = CLLocationDegrees(item["lat"] as! String)!
let lng: CLLocationDegrees = CLLocationDegrees(item["lng"] as! String)!
center = CLLocationCoordinate2D(latitude: lat, longitude: lng)
region = CLCircularRegion(center: center, radius: 5000, identifier: "startPosition")
print(region)
manager.startMonitoring(for: region)
}
manager.startUpdatingLocation()
}
}
i use this code and he only get notification when user enter last region on database although the database contains more than one place.
func updateLocation(){
shouldSetRegion = true
locationManager.startUpdatingLocation()
}
so any help about monitoring places and send local notification.
#objc func didEnterRegion() {
print("da5alt hena")
for item in realm.objects(Coupon.self){
let title = item["name"] as! String
let body = item["desc"] as! String
let id = item["BranchId"] as! String
VKNotificationService.sharedInstance.locationRequest(title, body, for: id)
}
}

You must use a unique identifier for each region that you want to monitor. Since you are using a constant identifier each region replaces the previous one and you are left only monitoring the final region.
I suggest you use the item name as the region identifier

Related

Instead of logging a user's coordinates (double) when he logs in, how can location delegate check the location when he is on the move?

Currently the user location is logged each time he visits the home screen. It needs to also update when the user is on the move so that the location stays current.
Here I set the location upon login
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let databaseRef = Database.database().reference()
let uid = Auth.auth().currentUser!.uid
guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
print("locations = \(locValue.latitude) \(locValue.longitude)")
latestLocation = ["latitude" : locValue.latitude, "longitude" : locValue.longitude]
let lat = locValue.latitude
let lon = locValue.longitude
dict = CLLocation(latitude: lat, longitude: lon)
print("dict", dict)
if let locationDictionary = latestLocation {
databaseRef.child("people").child(uid).child("Coordinates").setValue(locationDictionary)
}
}
Now I just nee it to always setValue when user moves materially.
You can look into startMonitoringSignificantLocationChanges() method and don't for get to call startUpdatingLocation

Display the distance from user with mapKit Swift (miles/km)

I have been attempting to display the distance on a tableView but I am unable to get it to happen. This question follows up from this question: CLLocationDistance conversion. I have checked the distance. Using this function in my Location class:
// Get distance
func distance(to location: CLLocation) -> CLLocationDistance {
return location.distance(from: self.location)
}
How I get the users current location:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01, 0.01)
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
mapView.setRegion(region, animated: true)
// Add a lastUserLocation to LocationManager and update it every time that the delegate receives a new location
LocationManager.shared.lastUserLocation = locations.last
LocationManager.shared.sortLocationsInPlace()
self.mapView.showsUserLocation = true
}
Sort function in LocationManager:
func getSortedLocations(userLocation: CLLocation) -> [Location] {
return locations.sorted { (l1, l2) -> Bool in
return l1.distance(to: userLocation) < l2.distance(to: userLocation)
}
}
func sortLocationsInPlace() {
if let validLocation = lastUserLocation {
locations.sort { (l1, l2) -> Bool in
return l1.distance(to: validLocation) < l2.distance(to: validLocation)
}
}
}
cellForRowAt:
var sortedLocations = [Location]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "locationCell", for: indexPath)
let location = sortedLocations[indexPath.row]
cell.textLabel?.text = location.name
return cell
}
Update
Inside Location class:
class Location {
var name: String
var latitude: Double
var longitude: Double
var location:CLLocation {
return CLLocation(latitude: latitude, longitude: longitude)
}
init?(json: JSON) {
guard let name = json["name"] as? String, let latitude = json["latitude"] as? Double, let longitude = json["longitude"] as? Double else { return nil }
self.name = name
self.latitude = latitude
self.longitude = longitude
}
func distance(to location: CLLocation) -> CLLocationDistance {
return location.distance(from: self.location)
}
}
Considering your code, I am making some assumptions:
Your sortedLocations array has different locations that you extracted from a JSON or whatever.
You call startUpdatingLocation() or similar somewhere before loading your data.
You are receiving updates in your didUpdateLocations.
Your LocationManager keeps an ordered copy of all your locations in a variable called locations, the one you are ordering inside didUpdateLocations.
That considered, what I understand you want to do is to display your sortedLocations ordered according to a reference location.
What is missing is to update your UITableView data once your user location is received. You have two main options:
To only load your UITableView once you have already your first user location retrieved by didUpdateLocations.
To force a UITableView update once you get a new location, by calling tableView.reloadData() inside didUpdateLocations. This will redraw your list every time you receive a location update, sorting them by location.
However, in any of those cases you need to replace your cellForRow text to display your distance instead of location.name:
// Distance in meters
cell.textLabel?.text = String(location.distance(to: LocationManager.shared.lastUserLocation!))
// Distance in miles
cell.textLabel?.text = String(location.distance(to: LocationManager.shared.lastUserLocation!)*0.00062137)
And update your didUpdateLocations:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01, 0.01)
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
mapView.setRegion(region, animated: true)
// Add a lastUserLocation to LocationManager and update it every time that the delegate receives a new location
LocationManager.shared.lastUserLocation = location
LocationManager.shared.sortLocationsInPlace()
sortedLocations = LocationManager.shared.locations
tableView.reloadData()
self.mapView.showsUserLocation = true
}
}
With your current code you are comparing all distances with a self.location variable that its not being initialised anywhere apparently.

How to get single dataBase reference from Firebase

I'm sharing and retrieving coordinates with Firebase, but when I print them in my console..I get same coordinates 3-4 time.
Which creates an odd effect on my custom marker image file.
How can I get the coordinates from Firebase only once?
Here is my code:
var posts=[postStruct]()
var mapView : GMSMapView? = nil
var friendLocator : [Locator] = [Locator]()
struct Locator {
let name: String
let long: CLLocationDegrees
let lat: CLLocationDegrees
}
var latPass: Double!
var longPass: Double!
var fetchLat: Double!
var fetchLong: Double!
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
var location=locations[0]
let span:MKCoordinateSpan=MKCoordinateSpanMake(0.01, 0.01)
var myLocation:CLLocationCoordinate2D=CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion=MKCoordinateRegionMake(myLocation, span)
latPass=28.3217378
longPass=75.6895935
post()
self.configureMapView()
let dataBaseRef=FIRDatabase.database().reference()
dataBaseRef.child("Raunak Trikha").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: {(snapshot) in
let postDict = snapshot.value as? [String : AnyObject] ?? [:]
var fetchLat = postDict["lat"] as! Double
var fetchLong = postDict["long"] as! Double
let locator = Locator(name: "Raunak Trikha", long: fetchLong, lat: fetchLat)
self.friendLocator.append(locator)
self.locateFriend()
print(fetchLat)
print(fetchLong)
})
manager.stopUpdatingLocation()
self.view = mapView
}
func locateFriend() {
for friend in friendLocator{
let friendMarker = GMSMarker()
friendMarker.position=CLLocationCoordinate2D(latitude: friend.lat, longitude: friend.long)
friendMarker.title=friend.name
friendMarker.map=mapView
mapView?.selectedMarker=friendMarker
if friend.name=="Virat Singh"{
friendMarker.icon=UIImage(named: "ViratPin.png")
}
else if friend.name=="Raunak Trikha"{
friendMarker.icon=UIImage(named: "currentLocation.png")
}
}
do {
mapView?.mapStyle = try GMSMapStyle(jsonString: kMapStyle)
} catch {
NSLog("One or more of the map styles failed to load. \(error)")
}
}
func configureMapView(){
let camera = GMSCameraPosition.camera(withLatitude: latPass, longitude: longPass, zoom: 10)
self.mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
view = mapView
mapView?.settings.scrollGestures = true
mapView?.settings.zoomGestures = true
mapView?.settings.myLocationButton = true
//mapView?.addSubview(searchBar)
//mapView?.addSubview(searchSupporter)
//mapView?.bringSubview(toFront: searchBar)
for gesture in (mapView?.gestureRecognizers!)! {
mapView?.removeGestureRecognizer(gesture)
}
}
when I print fetchLat & fetchLong I get the same coordinates 4 time, which overlaps my custom marker image that creates the weird effect.
Since your code that adds a particular Locator struct is called multiple times, check your array to make sure it doesn't already contain the exact same struct before adding it to the array locally.
This will evaluate your array of structs and determine if there is no value for it. But it also assumes that name property of the struct is a unique identifier for each struct, which may not be your case. You can alternatively compare any value within the filter closure that you want to make sure isn't duplictated, i. e. lat and long.
let locator = Locator(name: "Raunak Trikha", long: fetchLong, lat: fetchLat)
if self.friendLocator.filter({ $0.name == locator.name }).count == 0 {
self.friendLocator.append(locator)
}
self.locateFriend()
This function func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) will get called whenever your location changes/updates or until the GPS settles (Warms up) on your location.
I notice you are using the firebase single event obeserver function for database updates using .observeSingleEvent() which is correct however since you have defined the call in the above didUpdateLocations function it will be called multiple times.
Either move the call to Firebase out of the function or supply some conditional to call firebase only once. I.e only update if the location has changed more than X range/distance etc.

Pass longitude and latitude from CLLocationManager to URL?

i'm trying to pass my latitude and longitude to my url params but is returning Nil, but when i print within the delegate it returns the longitude and latitude and i can't seem to find the issue, i've tried many different ways and nothing seems to work
this are the variable where i store my latitude and longitude
var lat: Double!
var long: Double!
this is my delegate
func locationManager(_ manager:CLLocationManager, didUpdateLocations locations: [CLLocation]){
currentLocation = manager.location!.coordinate
let locValue:CLLocationCoordinate2D = currentLocation!
self.long = locValue.longitude
self.lat = locValue.latitude
print(lat)
print(long)
}
and here pass them to variables i'm using in my URL parameters but they return nil and i don't understand why
let userLat = String(describing: lat)
let userLong = String(describing: long)
Thank You
Try something like:
Swift 3
func locationManager(_ manager:CLLocationManager, didUpdateLocations locations: [CLLocation]){
if let last = locations.last {
sendLocation(last.coordinate)
}
}
func sendLocation(_ coordinate: CLLocationCoordinate2D) {
let userLat = NSString(format: "%f", coordinate.latitude) as String
let userLong = NSString(format: "%f", coordinate.longitude) as String
// Run API Call....
}
I think the Joseph K's answer is not correct. It rounds off the values of the latitude and longitude. It will be something like the code below.
let coordinate = CLLocationCoordinate2D(latitude: CLLocationDegrees(exactly: 35.6535425)!, longitude: CLLocationDegrees(exactly: 139.7047917)!)
let latitude = coordinate.latitude // 35.6535425
let longitude = coordinate.longitude // 139.7047917
let latitudeString = NSString(format: "%f", latitude) as String // "35.653543"
let longitudeString = NSString(format: "%f", longitude) as String // "139.704792"
So the correct and simpler code is:
Swift 3
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let coordinate = locations.last?.coordinate else { return }
let latitude = "\(coordinate.latitude)"
let longitude = "\(coordinate.longitude)"
// Do whatever you want to make a URL.
}

Swift - Return value from locationManager function in class extension

I'm trying to get directions from the users current location to a destination using Google Maps. I want this to be done when the showDirection button is pressed, however I can't figure how to return or pass the users location into the IBAction function from func locationManager(... didUpdateLocation) as the IBAction doesn't use parameters in which I can pass locValue to.
Here is the showDirection button function:
#IBAction func showDirection(sender: AnyObject) {
print("Running showDirection")
let instanceOne = ParseViewController() // Create ParseViewController instance to operate on
print("Created ParseView instance")
let Coord = instanceOne.returnParse()
let latitude = (Coord.lat as NSString)
let longitude = (Coord.long as NSString)
var urlString = "http://maps.google.com/maps?"
urlString += "saddr= // Users location from didUpdateLocation"
urlString += "&daddr= \(latitude as String), \(longitude as String)"
print(urlString)
if let url = NSURL(string: urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
{
UIApplication.sharedApplication().openURL(url)
}
}
and here is the locationManager function with the locValue:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
let locValue:CLLocationCoordinate2D = (manager.location?.coordinate)!
print("Coordinates = \(locValue.latitude), \(locValue.longitude)")
locationManager.stopUpdatingLocation()
}
}
Any help is greatly appreciated!
You need to create an internal variable in the class to store the location if you want to use it in another function. E.g.
class YourViewController: UIViewController ... {
var lastLocation: CLLocation? = nil
...
}
In didUpdateLocations:
if let location = locations.first {
lastLocation = location
...
}
And now you can access it in func showDirection()

Resources