Reputation: 421
Problem:
I'm trying to make it so that it uses the user's location all the time and constantly checks to see if it is within 5 miles of the CLLocationCoordinate2D
points I have set. If so, it sends a alert if the app is open or a notification if the app is closed.
Useful Information:
In my project I have 4 swift files:
Locations.swift holds the CLLocationCoordinate2D
points.
Utilities.swift holds a simple alert.
UserLocation.swift retrieves and updates the user's location
GeoLocationViewController.swift monitors the locations
Note:
Some code may not be in the right place or relevant, I was looking at another project trying to extract relevant code from it to use in my project. Please tell me if you do catch a mistake.
Code:
Locations.swift:
import UIKit
import MapKit
class Locations: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var radius: CLLocationDistance = 5
let arroyo = CLLocationCoordinate2D (latitude: 33.781327997137595, longitude: -116.46394436519012)
var arroyoCoord: CLLocationCoordinate2D = arroyo
let buddyrogers = CLLocationCoordinate2D (latitude: 33.78051204742721, longitude: -116.46362250010833)
var buddyCoord: CLLocationCoordinate2D = buddyrogers
let chopsticks = CLLocationCoordinate2D (latitude: 33.815995425565184, longitude: -116.44107442645873)
let colorfulfountain = CLLocationCoordinate2D (latitude: 33.80443304398751, longitude: -116.45723923544313)
let diamond = CLLocationCoordinate2D (latitude: 33.80216859530781, longitude: -116.45711048941041)
let dinahshore = CLLocationCoordinate2D (latitude: 33.806554795852996, longitude: -116.47734507421876)
let fountoflife = CLLocationCoordinate2D (latitude: 33.78075282028137, longitude: -116.46407847564086)
let fountains = CLLocationCoordinate2D (latitude: 33.780141969313235, longitude: -116.46346156756744)
let historicphoto = CLLocationCoordinate2D (latitude: 33.78130570353292, longitude: -116.46389072100982)
let holistic = CLLocationCoordinate2D (latitude: 33.781338029257775, longitude: -116.46408249895438)
let hollywoodheroes = CLLocationCoordinate2D (latitude: 33.78095792254918, longitude: -116.45820483068849)
let indiangathering = CLLocationCoordinate2D (latitude: 33.78136366689296, longitude: -116.46371905963287)
let indianwomen = CLLocationCoordinate2D (latitude: 33.78622660767695, longitude: -116.45820483068849)
let cathedrals = CLLocationCoordinate2D (latitude: 33.844502990031124, longitude: -116.45834321534426)
let firehouse = CLLocationCoordinate2D (latitude: 33.78103817982461, longitude: -116.46700744788512)
let perfectunion = CLLocationCoordinate2D (latitude: 33.778193459376865, longitude: -116.45877843062743)
let lizards = CLLocationCoordinate2D (latitude: 33.78104263855992, longitude: -116.46340792338714)
let cactus = CLLocationCoordinate2D (latitude: 33.782598723009976, longitude: -116.46699671904906)
let swisscheese = CLLocationCoordinate2D (latitude: 33.78121541437478, longitude: -116.46472086469993)
let newbeginning = CLLocationCoordinate2D (latitude: 33.78049421237406, longitude: -116.46463101069793)
let thunderbolt = CLLocationCoordinate2D (latitude: 33.80140187863324, longitude: -116.46646603445436)
let tictoc = CLLocationCoordinate2D (latitude: 33.80156235478469, longitude: -116.45524367193605)
let wheeloftime = CLLocationCoordinate2D (latitude: 33.815987530910135, longitude: -116.45892863433227)
let artevita = CLLocationCoordinate2D (latitude: 33.7826633, longitude: -116.46041969999999)
let coachellaart = CLLocationCoordinate2D (latitude: 33.78012700000001, longitude: -116.46571840000001)
let colinfisher = CLLocationCoordinate2D (latitude: 33.7819228, longitude: -116.46002010000001)
let garycreative = CLLocationCoordinate2D (latitude: 33.782660, longitude: -116.462141)
let lesliejean = CLLocationCoordinate2D (latitude: 33.78404799999999, longitude: -116.4635222)
let rebeccafine = CLLocationCoordinate2D (latitude: 33.782487, longitude: -116.460564)
let agnes = CLLocationCoordinate2D (latitude: 33.77571242620008, longitude: -116.46372063254091)
let willardprice = CLLocationCoordinate2D (latitude: 33.77489419346815, longitude: -116.46667910908434)
let adobe = CLLocationCoordinate2D (latitude: 33.77479870632753, longitude: -116.46673050629039)
let valsamuelson = CLLocationCoordinate2D (latitude: 33.76802162366799, longitude: -116.46920998147584)
let gallito = CLLocationCoordinate2D (latitude: 33.7794358, longitude: -116.4612692)
let townsquare = CLLocationCoordinate2D (latitude: 33.7810365, longitude: -116.46464559999998)
let ocotillo = CLLocationCoordinate2D (latitude: 33.805963, longitude: -116.46349980000002)
let century = CLLocationCoordinate2D (latitude: 33.8269913, longitude: -116.4424588)
let denniskeat = CLLocationCoordinate2D (latitude: 33.8304982, longitude: -116.45744730000001)
let memorial = CLLocationCoordinate2D (latitude: 33.78318512716751, longitude: -116.46681405767208)
let patriot = CLLocationCoordinate2D (latitude: 33.8019902897174, longitude: -116.44000872473146)
let panorama = CLLocationCoordinate2D (latitude: 33.83861734636407, longitude: -116.46799619895023)
let secondst = CLLocationCoordinate2D (latitude: 33.78069442561766, longitude: -116.45910418200071)
let dogpark = CLLocationCoordinate2D (latitude: 33.7804269, longitude: -116.46041309999998)
}
}
Utilities.swift:
import UIKit
import MapKit
func showSimpleAlertWithTitle(title: String!, message: String, viewController: UIViewController) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
alert.addAction(action)
viewController.presentViewController(alert, animated: true, completion: nil)
}
UserLocation.swift:
import UIKit
import CoreLocation
class UserLocation: UIViewController, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled()) {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
}
}
GeoLocationViewController.swift:
import UIKit
import CoreLocation
class GeoLocationViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func regionWithGeotification(geotification: Locations) -> CLCircularRegion {
// 1
let region = CLCircularRegion(center: geotification.coordinate, radius: geotification.radius, identifier: geotification.identifier)
// 2
region.notifyOnEntry = (geotification.eventType == .OnEntry)
region.notifyOnExit = !region.notifyOnEntry
return region
}
func startMonitoringGeotification(geotification: Locations) {
// 1
if !CLLocationManager.isMonitoringAvailableForClass(CLCircularRegion) {
showSimpleAlertWithTitle("Error", message: "Geofencing is not supported on this device!", viewController: self)
return
}
// 2
if CLLocationManager.authorizationStatus() != .AuthorizedAlways {
showSimpleAlertWithTitle("Warning", message: "Your geotification is saved but will only be activated once you grant permission to access the device location.", viewController: self)
}
// 3
let region = regionWithGeotification(geotification)
// 4
locationManager.startMonitoringForRegion(region)
}
func stopMonitoringGeotification(geotification: Locations) {
for region in locationManager.monitoredRegions {
if let circularRegion = region as? CLCircularRegion {
if circularRegion.identifier == geotification.identifier {
locationManager.stopMonitoringForRegion(circularRegion)
}
}
}
}
func locationManager(manager: CLLocationManager, monitoringDidFailForRegion region: CLRegion?, withError error: NSError) {
print("Monitoring failed for region with identifier: \(region!.identifier)")
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Location Manager failed with the following error: \(error)")
}
}
Upvotes: 5
Views: 6475
Reputation: 87
Multiple GeoFencing with AppleMap In Xcode 12.3(Swift 5) 100% Working
import UIKit
import MapKit
import CoreLocation
struct GeotificationData {
var lat : String?
var long : String?
}
class AppleMapVC: UIViewController {
@IBOutlet weak var mapView: MKMapView!
lazy var locationManager = CLLocationManager()
var arrGeoFenceData = [GeotificationData]()
//MARK:- VIEW CONTROLLER LIFE CYCLE METHOD
override func viewDidLoad() {
super.viewDidLoad()
title = "GeoFence"
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in }
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest //optimize power performanc Battery
locationManager.startUpdatingLocation()
arrGeoFenceData = [GeotificationData(lat: "21.7469", long: "74.1240"),
GeotificationData(lat: "21.1702", long: "72.8311"),
GeotificationData(lat: "19.9975", long: "73.7898"),
GeotificationData(lat: "20.1738", long: "72.7640"),
GeotificationData(lat: "19.0760", long: "72.8777"),
GeotificationData(lat: "18.5204", long: "73.8567")]
}
getGeoFencing()
}
//show notification
func showNotification(title:String, message:String) {
let content = UNMutableNotificationContent()
content.title = title
content.body = message
content.badge = 1
content.sound = .default
let request = UNNotificationRequest(identifier: "notifi", content: content, trigger: nil)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
func monitorRegionAtLocation(center: CLLocationCoordinate2D, identifier: String ) {
// Make sure the devices supports region monitoring.
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
// Register the region.
let maxDistance = CLLocationDistance(30000)
let region = CLCircularRegion(center: center,
radius: maxDistance, identifier: identifier)
region.notifyOnEntry = true
region.notifyOnExit = false
let circle = MKCircle(center: center, radius: maxDistance)
mapView.addOverlay(circle)
locationManager.startMonitoring(for: region)
}
}
func getGeoFencing() {
for item in arrGeoFenceData {
print("Your location with lat and long :- \(item)")
let cordi = CLLocationCoordinate2D(latitude: Double(item.lat!)!, longitude: Double(item.long!)!)
monitorRegionAtLocation(center: cordi, identifier: "Geofence")
}
}
//MARK:- UIBUTTON CLICKED
@IBAction func btnRotationClicked(_ sender: Any) {
mapView.setUserTrackingMode(.followWithHeading, animated: true)
}
}
//MARK:- LOCATIONMANAGER DELEGATE METHOD FOR UPDATE LOCATION
extension AppleMapVC : CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
locationManager.stopUpdatingLocation()
// locationManager.startUpdatingLocation()
render(location)
}
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
let alert = UIAlertController.init(title: "You enter in location", message: "enter in geofence", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Continue", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
showNotification(title: "You entered in geofence", message: "Welcome")
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
let alert = UIAlertController.init(title: "You exit in location", message: "exit in geofence", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Continue", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
showNotification(title: "You exit in geofence", message: "come again")
}
func render(_ location: CLLocation) {
let coordinate = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
let region = MKCoordinateRegion(center: coordinate, span: span)
mapView.setRegion(region, animated: true)
mapView.showsUserLocation = true
// let pin = MKPointAnnotation()
// pin.coordinate = coordinate
// mapView.addAnnotation(pin)
}
}
//MARK:- MKMAPVIEW DELEGATE METHOD
extension AppleMapVC : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard let circleOverlay = overlay as? MKCircle else {
return MKOverlayRenderer()
}
let circleRender = MKCircleRenderer(circle: circleOverlay)
circleRender.strokeColor = .red
circleRender.fillColor = .red
circleRender.alpha = 0.4
return circleRender
}
}
Upvotes: 0
Reputation: 352
First and foremost, change back startMonitoringGeotification()
, regionWithGeotification()
, and stopMonitoringGeotification()
to take in a Geotification
like the Ray Wenderlich tutorial. Make sure you have added the file Geotification.swift from his starter code to your project.
Also, make sure your Main.storyboard launches your ViewController
. Without this step, none of your code will run.
1) redefine your Locations
class more simply in Locations.swift:
import UIKit
import MapKit
class Locations {
static let locations:[String:CLLocationCoordinate2D] = [
"buddyrogers" : CLLocationCoordinate2D(latitude: 33.815995425565184, longitude: -116.44107442645873),
"diamond" : CLLocationCoordinate2D(latitude: 33.802168595307814, longitude: -116.45711048941041),
.
. // add your locations
.
]
}
}
like the @hungry-yeti suggested
2) You can define showSimpleAlertWithTitle()
in your GeotificationViewController
class. Try calling it in your ViewDidLoad()
to test it. You can now delete Utilities.swift.
3) I think you can ignore/remove UserLocation.swift, this seems unnecessary
4) Put this code inside GeotificationViewController
's ViewDidLoad
:
let radius = CLLocationDistance(8046.72) // 5 miles in meters
for location in Locations.locations {
let g = Geotification(coordinate: location.1, radius: radius, identifier: location.0, note: "test", eventType: EventType.OnEntry)
startMonitoringGeotification(g)
}
5) I hope this helps and simplifies your code. Party on, reply here if you have any issues.
Upvotes: 4
Reputation: 584
It looks like you're using the Ray Wenderlich tutorial. That's a good one, I found it very useful too.
First off, the unit for CLLocationDistance is meters so the code you have specifies a radius of 5 meters which won't be quite as useful as you may hope; a value of 8046.72 is closer to 5 miles.
Regarding the specific error, Locations is the class that you stuffed all CLLocationCoordinate2D values in, it certainly does not have any member called coordinate. If you're using the tutorial I'm thinking of you will need to load those coords into instances of the Geotification class.
Here is some untested code:
// Load the various coords into an array:
var locations:[(note:String, coords:CLLocationCoordinate2D)] = []
locations +=[(note: "arroyo", CLLocationCoordinate2D( latitude: 33.781327997137595, longitude: -116.46394436519012)]
locations +=[(note: "buddyrogers", CLLocationCoordinate2D( latitude: 33.78051204742721, longitude: -116.46362250010833)]
// ...
let radius = 8000 // ~5 miles rounded to nearest km
// Load the locations into geotifications:
for location in locations {
let geotification = Geotification(coordinate: location.cords, radius: radius, identifier: NSUUID().UUIDString, note: location.note, eventType: EventType.OnEnter)
startMonitoringGeotification(geotification)
}
Now bear in mind that there is a hard limit of 20 monitored regions per app, so if you have more than that you will need to dynamically determine the nearest 20 regions and then monitor those regions.
Upvotes: 1