Greeting pros,
As I know we can make a map by using google map api for IOS like the code below here.
import UIKit
import GoogleMaps
/* For cocoa:
Import Cocoa
Import MapKit
*/
class ViewController: NSViewController, CLLocationManagerDelegate {
#IBOutlet var mapView: MKMapView!
var locationManager = CLLocationManager()
var didFindMyLocation = false
var strForCurLatitude = "";
var strForCurLongitude = "";
var currentLocation = locManager.location!
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
print("User allowed us to access location")
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error while get location \(error)")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation? = locationManager.location
let coordinate: CLLocationCoordinate2D? = location?.coordinate
print(coordinate!)
print(coordinate!.latitude)
print(coordinate!.longitude)
strForCurLatitude = "\(coordinate!.latitude)"
strForCurLongitude = "\(coordinate!.longitude)"
let camera = GMSCameraPosition.camera(withLatitude: coordinate!.latitude, longitude: coordinate!.longitude, zoom: 15)
let mapView = GMSMapView.map(withFrame: .zero, camera: camera)
mapView.isMyLocationEnabled = true
self.view = mapView
let marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake(coordinate!.latitude, coordinate!.longitude)
marker.map = mapView
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
But when I tried the similar method for osx (The difference is that I am using mapkit instead of google map), it says requestWhenInUseAuthorization()' is unavailable. I red this thread How to get user location ? [macOS] but it seems goes without clear resolved whether it is available to get current location for osx or not. So is it inaccessible to get current location for macOS/cocoa app? If it is not, then how to get current location in cocoa app?
I am pretty sure that many xcode programmers like me tried to solve this problem. Any answer you will get big appreciation tho. :)
requestWhenInUseAuthorization is unavailable on macOS.
Here's the source for a NSViewController subclass that checks for location manager and the current location successfully in Xcode 10.1 on macOS 10.13.6:
import Cocoa
import MapKit
import CoreLocation
class MapViewController: NSViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) {
print("location manager auth status changed to:" )
switch status {
case .restricted:
print("status restricted")
case .denied:
print("status denied")
case .authorized:
print("status authorized")
let location = locationManager.location
print("location: \(String(describing: location))")
case .authorizedAlways:
print("status authorized always")
case .notDetermined:
print("status not yet determined")
}
}
func locationManager(_ manager: CLLocationManager,
didFailWithError error: Error) {
print( "location manager failed with error \(error)" )
}
}
works for me on macOS if you say yes to the "enable location services" prompt when you launch the app the first time.
Note you also need the NSLocationWhenInUseUsageDescription property list entry as indicated in the documentation.
console output is (slightly obfuscated):
location manager auth status changed to: status not yet determined
location manager auth status changed to: status authorized location:
Optional(<+4X.48,-12X.62632228> +/- 65.00m (speed -1.00 mps /
course -1.00) # 11/3/18, 11:42:48 AM Pacific Daylight Time)
Related
I am attempting a very simple AppKit app on MacOS which displays nearby iBeacons.
I don't appear to ever have region scanning available on my system though. Am I doing something wrong?
FYI: My system is a MacBook Pro (16-inch, 2019) running Catalina (10.15.7)
The target has 'Bluetooth' and 'App Location' enabled in the 'App Sandbox' capabilities section and 'Location' enabled under 'Hardened Runtime'.
The code below always gives the following:
LM Auth Status is now: Authorised Always
Beacon monitoring is not available
starting scanning
region monitoring failed: Error Domain=kCLErrorDomain Code=5 "(null)"
// ViewController.swift
// Beacons
import Cocoa
import CoreLocation
class ViewController: NSViewController, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.delegate = self
}
#IBAction func buttonTapped(_ sender: NSButton) {
self.startScanning()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func startScanning() {
print("starting scanning")
//UUID Obscured for forum post...
guard let uuid = UUID(uuidString: "xxxxxxxx-E4A1-4720-9034-xxxxxxxxxxxx") else {
print("Couldn't make UUID")
return
}
let beaconID = "com.monkeyfood.myBeaconRegion"
let beaconRegion = CLBeaconRegion(uuid: uuid, identifier: beaconID)
// let beaconRegion = CLBeaconRegion(uuid: UUID(), identifier: beaconID)
self.locationManager.startMonitoring(for: beaconRegion)
}
//. CLLocationManagerDelegate Methods
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
var newStatus = ""
switch status {
case .notDetermined:
newStatus = "Not determined"
case .restricted:
newStatus = "Restricted"
case .denied:
newStatus = "Denied"
case .authorizedAlways:
newStatus = "Authorised Always"
case .authorized:
newStatus = "Authorised"
default:
newStatus = "What?"
}
print("LM Auth Status is now: \(newStatus)")
// Check for iBeacon monitoring status
let regionMonitoringIsAvailable = CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self)
let not = regionMonitoringIsAvailable ? "" : "not "
print("Beacon monitoring is \(not)available")
}
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
if beacons.count > 0 {
// updateDistance(beacons[0].proximity)
print("\(beacons.count) beacons found.")
print("\(beacons[0].proximity) dist.")
} else {
// updateDistance(.unknown)
}
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("region entered... \(region)")
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("region exited... \(region)")
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
print("determined region state")
}
func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
print("region monitoring failed: \(error)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("LM failed \(error)")
}
}
I do not believe that Beacon monitoring with CoreLocation works on any MacOS version.
I looked at the class docs to confirm this and was surprised to see that the CLBeacon class docs do say it is supported on MacOS 10.15+. I don't know if this is a mistake, or simply an indication that the SDKs for MacOS 10.15+ support these classes even though you can't really use them.
The last time I worked with these APIs on MacOS was in 2018 when MacOS 2.14 was new. Back then, I had to use CoreBluetooth to do raw BLE advertisement scanning on MacOS and parse out beacons myself. I doubt anything has changed since then. I do not remember hearing anything about beacon support being added to MacOS, and just doing a quick search now, I can find no discussion of this starting with WWDC 2019. I doubt I missed such a change, but I would be thrilled to learn that I did!
After hours of reading and re-coding I still can't find a solution.
I want to build a simple app, that tracks my location in the background. On first application load I set up a region around my current location, start monitoring for exactly this region and stopUpdatingLocations. Once the app gets the didExitRegion event, I again get the current location, set up another region (after cleaning all other monitored regions) and it all starts again.
The app has alwaysAuthorization for location, background mode for location updates is set, PList contains LocationAlwaysAndWhenInUseUsageDescription and LocationWhenInUseUsageDescription.
For some time, it works great. But after many hours of not moving, the app gets suspended and does not receive any more location updates, not even the monitor event, which should relaunch the app when I understand this () correctly.
Could anyboy give me a hint?
Thanks.
import UIKit
import CoreLocation
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
let locationManager = CLLocationManager()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let currentNotifications = UNUserNotificationCenter.current()
currentNotifications.getNotificationSettings { (settings) in
if settings.authorizationStatus == .notDetermined {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { (granted, error) in }
}
}
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.startUpdatingLocation()
return true
}
}
extension AppDelegate: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
let now = Date()
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
for region in locationManager.monitoredRegions {
locationManager.stopMonitoring(for: region)
}
var regionIdentifier = String(format: "%f", location.coordinate.latitude) + ", " + String(format: "%f", location.coordinate.latitude) + "( set on " + formatter.string(from: now) + ")"
var geofenceRegion = CLCircularRegion(center: CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude), radius: 250, identifier: regionIdentifier)
geofenceRegion.notifyOnExit = true
locationManager.startMonitoring(for: geofenceRegion)
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
locationManager.stopMonitoring(for: region)
sendNotification(title: "Event triggered", subtitle: "Region exit", body: region.identifier)
locationManager.startUpdatingLocation()
}
}
public func sendNotification(title: String, subtitle: String, body: String) {
let content = UNMutableNotificationContent()
content.title = title
content.subtitle = subtitle
content.body = body
content.sound = .default
let identifier = "geoStalker4_notification_" + String(format: "%f", NSDate().timeIntervalSince1970)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
Try adding this to your locationManager settings:
locationManager.pausesLocationUpdatesAutomatically = false
I am building an app using UITabBar and one of the tab items is a map. I only want the user to have to respond to the LocationService() request when they tap on the map tab. However, when the app loads to the tabBar, the location permission request pops up first thing.
Here is the code for the LocationService()
import CoreLocation
protocol LocationServiceDelegate: class {
func authorizationDenied()
func setMapRegion(center: CLLocation)
}
class LocationService: NSObject {
var locationManager = CLLocationManager()
weak var delegate: LocationServiceDelegate?
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
private func checkAuthorizationStatus() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
locationManager.requestAlwaysAuthorization()
case .denied:
delegate?.authorizationDenied()
case .authorizedAlways, .authorizedWhenInUse:
startUpdatingLocation()
default:
break
}
}
private func startUpdatingLocation() {
locationManager.startUpdatingLocation()
}
}
extension LocationService: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkAuthorizationStatus()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.stopUpdatingLocation()
if let location = locations.last {
delegate?.setMapRegion(center: location)
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}
The method was originally called in viewDidLoad so the map would center on the user's location, so I moved it to viewDidAppear, but I still get the same notice when the app launches.
How do I delay the permission request until the user actually taps on the map tab - in this case the DIY Stores tab?
I apologize for not providing it with a code. The reason I posted it with images because xcode is LLVM(low level virtual machine) compiler which has mostly UI environment especially for the configuration part. Such as creating either an outlet or action for object by dragging them to the view controller instead defining it by a code directly. Thus since it was so, I think people would easily notice my mistake faster.
import Cocoa
import MapKit
class ViewController: NSViewController, CLLocationManagerDelegate {
#IBOutlet var mapView: MKMapView!
var locManager = CLLocationManager()
var currentLocation = CLLocation()
var locationManager = CLLocationManager()
var didFindMyLocation = false
var strForCurLatitude = "";
var strForCurLongitude = "";
override func viewDidLoad() {
super.viewDidLoad()
let distancespan:CLLocationDegrees = 2000
let busCScampuslocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(currentLocation.coordinate.latitude, currentLocation.coordinate.longitude)
mapView.setRegion(MKCoordinateRegion.init(center: bsuCScampuslocation, latitudinalMeters: distancespan, longitudinalMeters: distancespan), animated: true)
print(currentLocation.coordinate.latitude)
}
...
}
Swift 5:
ViewController.swift
import Cocoa
import CoreLocation
import MapKit
class ViewController: NSViewController, CLLocationManagerDelegate {
let manager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
//This is where you can update the MapView when the computer is moved (locations.last!.coordinate)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) {
print("location manager auth status changed to: " )
switch status {
case .restricted:
print("restricted")
case .denied:
print("denied")
case .authorized:
print("authorized")
case .notDetermined:
print("not yet determined")
default:
print("Unknown")
}
}
Add these lines to Info.plist or set the NSLocationAlwaysAndWhenInUseUsageDescription and/(or?) NSLocationUsageDescription to a plaintext description of why you need to access the users location
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Allows us to locate you</string>
<key>NSLocationUsageDescription</key>
<string>Allows us to locate you</string>
If the user has Location Services off, the authorization status will be 'denied'
I build a app with Estimotes indoor Location ,any I use CoreLocation to find our beacon, and my app will change label.text. App can run,but didn't work ,label.text can't change.
override func viewDidLoad() {
super.viewDidLoad()
let path = NSBundle.mainBundle().pathForResource("location", ofType: "json")
do {
let content = try String(contentsOfFile: path!, encoding: NSUTF8StringEncoding)
let locationSetup = ESTLocationBuilder.parseFromJSON(content)
//set up delegate
manager.delegate = self
locationManager.delegate = self
if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.AuthorizedWhenInUse) {
locationManager.requestWhenInUseAuthorization()
}
locationManager.startRangingBeaconsInRegion(region)
}
func locationManager(manager: CLLocationManager, didRangeBeacons beacons:[CLBeacon], inRegion region: CLBeaconRegion){
let knownBeacons = beacons.filter{ $0.proximity != CLProximity.Unknown }
if (knownBeacons.count > 0){
let closestBeacon = knownBeacons[0] as CLBeacon
if(closestBeacon.minor.integerValue == 41016)
{
self.snow.text = "find"
}
}
}
First thing to try:
I would move the initialization of locationManger = CLLocationManager() outside the class instance declaration and inside viewDidLoad. I have seen problems initializing the location manager during object construction.