I have an app that has been working fine for several years, but location services are not working properly for iOS 13 on newer iPhones. Works fine on 7 and below. When the CLLocationManager is initialized the CLLocationManager: didChangeAuthorizationStatus method is not called.
Here is the code:
var firstMapLoad = true
let locationManager = CLLocationManager()
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
....
// MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print ("locationManager: didChangeAuthorization called")
if (CLLocationManager.locationServicesEnabled()) {
switch status {
case .notDetermined:
// Request when-in-use authorization initially
locationManager.requestWhenInUseAuthorization()
print ("locationManager: notDetermined & requestWhenInUseAuthorization")
break
case .authorizedWhenInUse:
// Enable basic location features
locationManager.startUpdatingLocation()
print ("locationManager: authorizedWhenInUse & startUpdatingLocation")
break
case .authorizedAlways:
// Enable any of your app's location features
//enableMyAlwaysFeatures()
break
case .restricted, .denied:
//determine if system or app loc svcs disabled
print ("locationManager: restricted or denied")
noAppLocationService()
break
#unknown default:
print("locationManager: unknown authorization state")
}
}else{
viewOnlyMode()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let GPSAccuracy:CLLocation = locations[0] as CLLocation
print ("locationManager: didUpdateLocations")
if let location = locations.last?.coordinate {
if (currentLocation.latitude != location.latitude) || (currentLocation.longitude != location.longitude) {
self.currentLocation = location
GPSHorizontalAccuracy = GPSAccuracy.horizontalAccuracy
// print("LocManAccuracy: \(GPSAccuracy.horizontalAccuracy)")
// print("LocManLatitude: \(GPSAccuracy.coordinate.latitude)")
// print("LocManLongitude: \(GPSAccuracy.coordinate.longitude)")
// print("LocManCurrLat: \(currentLocation.latitude)")
// print("LocManCurrLong: \(currentLocation.longitude)")
if firstMapLoad {
// Set the map’s center coordinate and zoom level.
let camera = GMSCameraPosition.camera(withTarget: self.currentLocation, zoom: 13)
self.viewMap?.animate(to: camera)
firstMapLoad = false;
}
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location Error = \(error.localizedDescription)")
}
When I run it on the iPhone 11 Pro Max simulator in Xcode, if the iPhone simulator has been reset and run the app for the first time I get the authorization alert with the 3 choices. I select "when in use" and the app works fine - the didChangeAuthorization function is called twice (once when location manager is initialized and then when I select "when in use"). didUpdateLocations is then called and the app gets location updates.
If I stop the app, delete it from the simulator and then re-run from Xcode the app does not request user permission for location services. didChangeAuthorization is not called when the location manager is initialized and thus location services are not started and the app doesn't receive any location updates. I have observed the same behavior on a real iPhone 11 Pro.
I tried setting the permission for the app to "never" in the app location settings, stopped the app and then deleted the app on the simulator. When I ran from Xcode the next time the app did not ask for location permissions, but did display an alert that location services were disabled. It appears that the settings from the previous installation were retained even though the app was deleted.
In my case, the delegate's method signature was wrong for iOS 14+
iOS <=13, deprecated in iOS 14
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
iOS 14+
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager)
If you're targeting iOS <14, you should implement both
Related
This app is intentionally tiny - the UI is just a button to initiate the api call. The appDelegate simply shows a console log proving the callback was executed. I did this because I can not get the Square payment system to work even thought the code and url setup etc looks right to me. I am wondering if I misunderstand something in the documentation.
My app ID is: sq0idp-TbgQGqSrC84qkfcXSTntNg
bundleID is: com.craig.POSSDKTest2
My info.plist setup is a screenshot:info.plist and URLScheme setup
My code in the api calling VC:
// ViewController.swift
// POSSDKTest2
//
// Created by Dr Craig E Curphey on 2022-06-14.
//
import UIKit
import SquarePointOfSaleSDK
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}//------------------------------------
#IBAction func doPaymentTapped(_ sender: Any) {
//------ API to Square right here ******-----------------
let callbackURL = URL(string: "POSSDKTest2Scheme://")!
SCCAPIRequest.setApplicationID("sq0idp-TbgQGqSrC84qkfcXSTntNg")
var money: SCCMoney?
do {
// Specify the amount of money to charge - $1.00
let todaysFeePennies = 100
money = try SCCMoney(amountCents: todaysFeePennies, currencyCode: "CAD")
} catch {
print("money conversion failed.")
}
let LoginID = "TestPatient"
//log values used to console:
print("Initiating payment api request")
print("callback: \(callbackURL)")
print("money: \(money!)")
print("LoginID: TestPatient")
print("Creating payment api request")
do
{
let apiRequest =
try SCCAPIRequest(
callbackURL: callbackURL,
amount: money!,
userInfoString: LoginID,
locationID: "",
notes: "TestingSDK2",
customerID: "tester2",
supportedTenderTypes: .all,
clearsDefaultFees: false,
returnsAutomaticallyAfterPayment: false,
disablesKeyedInCardEntry: false,
skipsReceipt: false
)
print("api initiating!")
try SCCAPIConnection.perform(apiRequest)
} catch let error as NSError {
print("Error detected: \(error.localizedDescription)")
}//end API
print("End of payment api function.")
}//------------------------------------
}
Here is my appDelegate code - I just want to see if I get a callback - I'll add the code to process the response later ...
// AppDelegate.swift
// POSSDKTest2
//
// Created by Dr Craig E Curphey on 2022-06-14.
//
var squareTokenID = "not specified - let it fail - I just want a callback!"
import UIKit
import SquarePointOfSaleSDK
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
//-------------------------------------------
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("Callback was executed!")
guard SCCAPIResponse.isSquareResponse(url) else {
print("\(SCCAPIResponse.isSquareResponse(url))")
return true
}
do {
let response = try SCCAPIResponse(responseURL: url)
if let error = response.error {
// Handle a failed request.
print(error.localizedDescription)
} else {
// Handle a successful request.
}
} catch let error as NSError {
// Handle unexpected errors.
print(error.localizedDescription)
}
return true
}//-------------------------------------------------------
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}//-------------------------------------------------------
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}//--------------------------------------------------------
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}//---------------------------------------------------------
}
Screenshot of console log from trying to do the api:
Console log
The work environment is Xcode 13.3, Swift, macOSMonterey 12.1 beta, iPad 10.2" (I think) mounted in a Square iPad kiosk with attached card reader, running iOS 15.5.
I would be very happy to send the whole project in a zip file to anyone who would be willing to take a look into why it is failing.
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!
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)
I am trying to present a local notification on an Apple Watch simulator with a button. This is the code:
#IBAction func buttonOne() {
print("button one pressed")
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey: "Notified!", arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey: "This is a notification appearing!", arguments: nil)
// Deliver the notification in five seconds.
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5,
repeats: false)
// Schedule the notification.
let request = UNNotificationRequest(identifier: "Notify", content: content, trigger: trigger)
center.add(request) { (error : Error?) in
if let theError = error {
print(theError.localizedDescription)
} else {
print("successful notification")
}
}
}
The console is successfully printing "successful notification," but the notification never appears on the watch simulator. I have no idea why this is
1) Before scheduling a notification, request permissions. Use requestAuthorization method, for example:
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization
}
2) Notifications will not appear on watch face if the application is running in foreground. Considering this you may use cmd + H to hide the app.
I'm trying to retrieve de Lat Long of my current position on my WatchOS2 app.
The app is authenticated and the iPhone app works fine.
info.plist: NSLocationWhenInUseUsageDescription is set.
I use the following code in the willActivate()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.AuthorizedWhenInUse)
{
locationManager.requestWhenInUseAuthorization()
print("Logon: Location not authorized1")
}
}
locationManager.requestLocation()
In the locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) i run this code:
if locationManager.location != nil
{
self.userLat = String(self.locationManager.location!.coordinate.latitude)
self.userLong = String(self.locationManager.location!.coordinate.longitude)
print("Lat\(userLat) Long \(userLong)")
}
else
{
print("An error occurred") <- It always prints this error
}
}
locationManager(manager: CLLocationManager, didFailWithError error: NSError)
is not being called
I've found a library which solved my problem. I don't know what exactly happend but it works now.
I used the OneShotLocationManger by icanzilb
I've you want to use this in your WatchOS2 app, you'll have to change startUpdatingLocation() to requestLocation(), because WatchOS doesn't allow continues updates.
like so:
//location authorization status changed
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .AuthorizedWhenInUse:
self.locationManager!.requestLocation()
case .Denied:
_didComplete(nil, error: NSError(domain: self.classForCoder.description(),
code: OneShotLocationManagerErrors.AuthorizationDenied.rawValue,
userInfo: nil))
default:
break
}
}
Give Credit Where Credit Is Due -