// // 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{ assert(shared_ != nil, "HMG_Geofence is not initialized, call initGeofencing() function first.") return shared_! } class func initGeofencing(){ shared_ = HMG_Geofence() shared_?.locationManager = CLLocationManager() } func register(geoZones:[GeoZoneModel]){ self.geoZones = geoZones // self.geoZones?.forEach({ (zone) in // startMonitoring(zone: zone) // }) let z = geoZones[14] if monitoredRegions().filter { $0.identifier == z.identifier()}.isEmpty{ startMonitoring(zone: z) } } func monitoredRegions() -> Set{ return locationManager.monitoredRegions } } // CLLocationManager Delegates extension HMG_Geofence : CLLocationManagerDelegate{ func startMonitoring(zone: GeoZoneModel) { 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) } if let fenceRegion = region(with: zone){ locationManager.startMonitoring(for: fenceRegion) locationManager.requestState(for: fenceRegion) debugPrint("Monitering region: \(fenceRegion.center) | \(fenceRegion.identifier)") }else{ debugPrint("Invalid region: \(zone.latitude ?? ""),\(zone.longitude ?? ""),r\(zone.radius ?? 0) | \(zone.identifier())") } } 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?) { if let zone = geoZone(by: region.identifier){ notifyUser(forZone: zone, transition: transition, location: locationManager.location) notifyServer(forZone: zone, transition: transition, location: locationManager.location) } } func region(with geoZone: GeoZoneModel) -> CLCircularRegion? { if !geoZone.identifier().isEmpty, let radius = geoZone.radius, let lat = geoZone.latitude?.removeSpace(), let long = geoZone.longitude?.removeSpace(), let radius_d = Double("\(radius)"), let lat_d = Double(lat), let long_d = Double(long){ let coordinate = CLLocationCoordinate2D(latitude: lat_d, longitude: long_d) let region = CLCircularRegion(center: coordinate, radius: radius_d, identifier: geoZone.identifier()) region.notifyOnEntry = true region.notifyOnExit = true return region } return nil } func geoZone(by id: String) -> GeoZoneModel? { return geoZones?.first(where: { $0.identifier() == id}) } func notifyUser(forZone:GeoZoneModel, transition:Transition, location:CLLocation?){ if UIApplication.shared.applicationState == .active { mainViewController.showAlert(withTitle: transition.name(), message: forZone.identifier()) } else { let notificationContent = UNMutableNotificationContent() notificationContent.title = transition.name() notificationContent.body = forZone.identifier() notificationContent.sound = UNNotificationSound.default let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false) let request = UNNotificationRequest(identifier: "\(Date().timeIntervalSinceNow)", content: notificationContent, trigger: trigger) UNUserNotificationCenter.current().add(request) { error in if let error = error { print("Error: \(error)") } } } } func notifyServer(forZone:GeoZoneModel, transition:Transition, location:CLLocation?){ flutterMethodChannel?.invokeMethod("getLogGeofenceFullUrl", arguments: nil){ fullUrlString in if let url = fullUrlString as? String{ flutterMethodChannel?.invokeMethod("getDefaultHttpParameters", arguments: nil){ params in if let body = params as? [String : Any]{ httpPostRequest(urlString: url, jsonBody: body){ (status,json) in if let json_ = json , status{ }else{ } } } } } } } }