// // HMG_Geofence.swift // Runner // // Created by ZiKambrani on 13/12/2020. // import UIKit import CoreLocation fileprivate var df = DateFormatter() fileprivate var transition = "" enum Transition:Int { case entry = 1 case exit = 2 func name() -> String{ return self.rawValue == 1 ? "Enter" : "Exit" } } class HMG_Geofence:NSObject{ var geoZones:[GeoZoneModel]? var locationManager:CLLocationManager!{ didSet{ // https://developer.apple.com/documentation/corelocation/cllocationmanager/1423531-startmonitoringsignificantlocati locationManager.allowsBackgroundLocationUpdates = true locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters locationManager.activityType = .other locationManager.delegate = self locationManager.requestAlwaysAuthorization() // locationManager.distanceFilter = 500 // locationManager.startMonitoringSignificantLocationChanges() } } private static var shared_:HMG_Geofence? class func shared() -> HMG_Geofence{ if HMG_Geofence.shared_ == nil{ HMG_Geofence.initGeofencing() } return shared_! } class func initGeofencing(){ shared_ = HMG_Geofence() shared_?.locationManager = CLLocationManager() } func register(geoZones:[GeoZoneModel]){ self.geoZones = geoZones let monitoredRegions_ = monitoredRegions() self.geoZones?.forEach({ (zone) in if let region = zone.toRegion(locationManager: locationManager){ if let already = monitoredRegions_.first(where: {$0.identifier == zone.identifier()}){ debugPrint("Already monitering region: \(already)") }else{ startMonitoring(region: region) } }else{ debugPrint("Invalid region: \(zone.latitude ?? "invalid_latitude"),\(zone.longitude ?? "invalid_longitude"),r\(zone.radius ?? 0) | \(zone.identifier())") } }) } func monitoredRegions() -> Set{ return locationManager.monitoredRegions } func unRegisterAll(){ for region in locationManager.monitoredRegions { locationManager.stopMonitoring(for: region) } } } // CLLocationManager Delegates extension HMG_Geofence : CLLocationManagerDelegate{ func startMonitoring(region: CLCircularRegion) { if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) { return } if CLLocationManager.authorizationStatus() != .authorizedAlways { let message = """ Your geotification is saved but will only be activated once you grant HMG permission to access the device location. """ debugPrint(message) } locationManager.startMonitoring(for: region) locationManager.requestState(for: region) debugPrint("Starts monitering region: \(region)") } func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) { debugPrint("didEnterRegion: \(region)") if region is CLCircularRegion { handleEvent(for: region,transition: .entry, location: manager.location) } } func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) { debugPrint("didExitRegion: \(region)") if region is CLCircularRegion { handleEvent(for: region,transition: .exit, location: manager.location) } } func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) { debugPrint("didDetermineState: \(state)") } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { debugPrint("didUpdateLocations: \(locations)") } } // Helpers extension HMG_Geofence{ func handleEvent(for region: CLRegion!, transition:Transition, location:CLLocation?) { notifyUser(forRegion: region, transition: transition, location: locationManager.location) notifyServer(forRegion: region, transition: transition, location: locationManager.location) } func geoZone(by id: String) -> GeoZoneModel? { var zone:GeoZoneModel? = nil if let zones_ = geoZones{ zone = zones_.first(where: { $0.identifier() == id}) }else{ // let jsonArray = UserDefaults.standard.string(forKey: "hmg-geo-fences") } return zone } func notifyUser(forRegion:CLRegion, transition:Transition, location:CLLocation?){ if let zone = geoZone(by: forRegion.identifier){ if UIApplication.shared.applicationState == .active { mainViewController.showAlert(withTitle: transition.name(), message: zone.message()) }else{ } } } func notifyServer(forRegion:CLRegion, transition:Transition, location:CLLocation?){ df.dateFormat = "MMM/dd/yyyy hh:mm:ss" if let userProfileJson = UserDefaults.standard.string(forKey: "flutter.user-profile"), let userProfile = dictionary(from: userProfileJson), let patientId = userProfile["PatientID"] as? Int{ if let idString = forRegion.identifier.split(separator: "_").first, let idInt = Int(idString){ let body:[String:Any] = [ "PointsID":idInt, "GeoType":transition.rawValue, "PatientID":patientId ] let url = "https://hmgwebservices.com/Services/Patients.svc/REST/GeoF_InsertPatientFileInfo" httpPostRequest(urlString: url, jsonBody: body){ (status,json) in let status_ = status ? "Notified" : "Not notified" showNotification(title: transition.name(), subtitle: forRegion.identifier, message: status_) var logs = UserDefaults.init(suiteName: "GeoFenceLog")?.dictionary(forKey: "LOGS") ?? [:] if var geo = logs[forRegion.identifier] as? [String]{ geo.append("\(status_) at \(df.string(from: Date()))") }else{ logs.updateValue(["\(status_) at \(df.string(from: Date()))"], forKey: forRegion.identifier) } UserDefaults.init(suiteName: "GeoFenceLog")?.set(logs, forKey: "LOGS") } } } } }