Swift Beacon doesn't work now using Swift 3.0 - swift

I have been following a guide to using Swift to create a Beacon proximity app, but since updating Xcode and updating the code to Swift 3.0 I am getting a fatal error.
Going through the functions I think there is an issue with the startScanning function, when it fires I get fatal error message.
Any hints at to what could help would be greatly appreciated:
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var distanceLabel: UILabel!
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
view.backgroundColor = UIColor.gray
print("did load")
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == CLAuthorizationStatus.authorizedAlways{
print("status authorized")
if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self){
print("is monitoring")
if CLLocationManager.isRangingAvailable() {
print("scanning")
startScanning()
}
}
}
}
func startScanning() {
print("start Scanning")
let uuid = NSUUID(uuidString: "695e5f08824c785cadc72e1dde23be04")
let beaconRegion = CLBeaconRegion(proximityUUID: uuid as! UUID, identifier: "MyBeacon")
locationManager.startMonitoring(for: beaconRegion)
locationManager.startRangingBeacons(in: beaconRegion)
}
func updateDistance(distance: CLProximity){
UIView.animate(withDuration: 1) { [unowned self] in
switch distance {
case .unknown:
self.view.backgroundColor = UIColor.gray
self.distanceLabel.text = "UNKNOWN"
print("distance Unknown")
case .far:
self.view.backgroundColor = UIColor.blue
self.distanceLabel.text = "FAR"
print("distance Far")
case .near:
self.view.backgroundColor = UIColor.orange
self.distanceLabel.text = "NEAR"
print("distance Near")
case .immediate:
self.view.backgroundColor = UIColor.red
self.distanceLabel.text = "BOOM!"
print("distance Immediate")
}
}
}
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
if beacons.count > 0 {
let beacon = beacons.first! as CLBeacon
updateDistance(distance: beacon.proximity)
print("found more than one beacon")
} else {
updateDistance(distance: .unknown)
print("found only one beacon")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

The problem is that your UUID is in the wrong format, so this line fails to parse it, assigning nil to the variable uuid:
let uuid = NSUUID(uuidString: "695e5f08824c785cadc72e1dde23be04")
The program will then crash using the uuid as! UUID operation because ! will crash if there is a nil value.
To fix this, you need to add dashes at the appropriate places in the UUID string. You should also avoid using the ! operator to force unwrap optional variables in Swift, as it can cause crashes like this. Try this:
if let uuid = NSUUID(uuidString: "695e5f08-824c-785c-adc7-2e1dde23be04") {
let beaconRegion = CLBeaconRegion(proximityUUID: uuid, identifier: "MyBeacon")
locationManager.startMonitoring(for: beaconRegion)
locationManager.startRangingBeacons(in: beaconRegion)
}
else {
NSLog("Invalid UUID format")
}
When running check that the code does not go down the "Invalid UUID format" path.

Related

why app crashes when I use localization and LocationButton in swiftui? I get this error:Thread 1: "Invalid parameter not satisfying: width && height"

In my app I used locationButton and everything worked fine. Then I added Localizable string file and localized for English. Still, everything worked OK. Then I added localized String file for Hebrew, and it compiles, opens my simulator and shows everything in Hebrew But then I try to use the app and click something and it crashes and gives me this error:
Thread 1: "Invalid parameter not satisfying: width && height".
Can't find anything about this anywhere. Did anyone encountered this?
The String files:
English:
/*
Localizable.strings
*/
"location" = "Location:";
Hebrew:
"location" = "מיקום:";
This is my code:
import SwiftUI
import MapKit
import CoreLocationUI
struct ContentView: View {
#StateObject var vm = ViewModel()
var body: some View {
VStack {
Text("location")
LocationButton(.currentLocation) { vm.reqestUserLocationOnce() }
.foregroundColor(.white)
.symbolVariant(.fill)
.labelStyle(.iconOnly)
.clipShape(Capsule())
}
}
}
class ViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func reqestUserLocationOnce() { locationManager.requestLocation() }
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let currenLocation = locations.last else { return }
CLGeocoder().reverseGeocodeLocation(currenLocation) { placemarks, error in
guard error == nil else { return }
guard let placemark = placemarks?.first else { return }
guard placemark.location?.coordinate != nil else { return }
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { print(error.localizedDescription) }
}

Swift: Get Current User Coordinates and Store them into a Variable

I am currently trying to get the current coordinates of the user and ultimately store those values into variables.
I have created the following class to define the users current location and set up functions to pull data.
import Foundation
import CoreLocation
class MyCurrentCoordinate: NSObject {
private var currentLocation: CLLocation!
var myLatitude = 0.0
var myLongitude = 0.0
var myAltitude = 0.0
override init() {
super.init()
}
func getLat() {
myLatitude = currentLocation.coordinate.latitude
}
func getLong() {
myLongitude = currentLocation.coordinate.longitude
}
func getAlt() {
myAltitude = currentLocation.altitude
}
}
This does not show any errors. However, when I go to call any function (getLat, getLong, or getAlt) to pull a piece of the users location data, the app crashes due the value being nil. Does anyone have any insight as to why the actual user lat, long, or altitude is not being passed?
I have the location permission and info.plist updated to allow the user to give location tracking permission.
import Foundation
import CoreLocation
import UIKit
public protocol LocalizationHelperDelegate: class {
func didUpdateLocation(_ sender: CLLocation)
}
public class LocalizationHelper: NSObject {
public weak var delegate: LocalizationHelperDelegate?
public static var shared = LocalizationHelper()
private lazy var locationManager: CLLocationManager = {
let locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.desiredAccuracy = kCLLocationAccuracyBest
return locationManager
}()
private var currentLocation: CLLocationCoordinate2D?
public func startUpdatingLocation() {
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
public func stopUpdatingLocation() {
locationManager.stopUpdatingLocation()
}
public func getCurrentLocation() -> CLLocationCoordinate2D? {
return currentLocation
}
public func getLat() -> Double{
return currentLocation?.latitude ?? 0.0
}
public func getLon() -> Double{
return currentLocation?.longitude ?? 0.0
}
}
extension LocalizationHelper: CLLocationManagerDelegate {
public func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]){
guard let location = locations.first else { return }
currentLocation = location.coordinate
print("[Update location at - \(Date())] with - lat: \(currentLocation!.latitude), lng: \(currentLocation!.longitude)")
delegate?.didUpdateLocation(location)
}
}
How to use
LocalizationHelper.shared.Start()
...
let lat = LocalizationHelper.shared.getLat()
let lon = LocalizationHelper.shared.getLon()
...
LocalizationHelper.shared.Stop()
You can use CLLocationManager
Add app capability, you can open your info.plist like source code and add:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>App requires allways tracking</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>App requires background tracking</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>App requires tracking when be in use</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>location</string>
<string>remote-notification</string>
</array>
Ask for authorization like locationManager.requestAlwaysAuthorization() and manage if have the correct access... CLLocationManager.authorizationStatus() == .authorizedAlways
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager: CLLocationManager?
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.requestAlwaysAuthorization()
if CLLocationManager.authorizationStatus() == .authorizedAlways {
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
locationManager.allowsBackgroundLocationUpdates = false
locationManager.pausesLocationUpdatesAutomatically = true
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
}
}
extension ViewController: CLLocationManagerDelegate {
public func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]){
guard let location = locations.first else { return }
print(location)
}
public func locationManager(_: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
print("Autorization status did change \(status)")
case .authorizedWhenInUse:
print("Autorization status did change \(status)")
case .authorizedAlways:
print("Autorization status did change \(status)")
case .restricted:
print("Autorization status did change \(status)")
case .denied:
print("Autorization status did change \(status)")
#unknown default:
fatalError()
}
}
}
Don't forget to stop it in some place locationManager.stopUpdatingLocation()

CoreLocation enabled in simulator

didUpdateLocations is not firing
- CLLocationManagerDelegate was implemented correctly with the viewcontroller as a delegate
import Foundation
import UIKit
import CoreLocation
protocol LocationServiceDelegate {
func tracingLocation(currentLocation: CLLocation)
func tracingLocationDidFailWithError(error: NSError)
}
class LocationService: NSObject, CLLocationManagerDelegate {
static var sharedInstance = LocationService()
var locationManager: CLLocationManager?
var currentLocation: CLLocation?
var delegate: LocationServiceDelegate?
var paymentVC: PaymentViewController?
override init() {
super.init()
self.locationManager = CLLocationManager()
guard let locationManager = self.locationManager else {
return
}
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestAlwaysAuthorization()
}
locationManager.distanceFilter = 200
}
func startUpdatingLocation() {
print("Starting Location Updates")
self.locationManager!.startUpdatingLocation()
}
func stopUpdatingLocation() {
print("Stop Location Updates")
self.locationManager?.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
self.currentLocation = location
updateLocation(currentLocation: location)
}
private func locationManager(manager: CLLocationManager, didFailWithError error: Error) {
updateLocationDidFailWithError(error: error as NSError)
}
private func updateLocation(currentLocation: CLLocation){
guard let delegate = self.delegate else {
return
}
delegate.tracingLocation(currentLocation: currentLocation)
}
private func updateLocationDidFailWithError(error: NSError) {
guard let delegate = self.delegate else {
return
}
delegate.tracingLocationDidFailWithError(error: error)
}
}
this is the extension of the viewcontroller where I implement to custom protocole for corelocation tracking
I call startUpdatingLocations() in viewDidLoad
extension PaymentViewController: LocationServiceDelegate,CLLocationManagerDelegate {
func tracingLocation(currentLocation: CLLocation) {
locationService.currentLocation = currentLocation
}
func tracingLocationDidFailWithError(error: NSError) {
print("Error message: \(error.localizedDescription)")
}
func startUpdatingLocations() {
locationService.locationManager?.delegate = self
locationService.delegate = self
locationService.startUpdatingLocation()
}
func stopUpdatingLocations() {
LocationService.sharedInstance.stopUpdatingLocation()
}
}
the corelocation tracking is not firing in the simulator. However this is enabled.
enter image description here
unfortunately, I have no way to test with a device right now
The only way I know of to simulate core location tracking on a simulator is by choosing one of the location options available in debug tab of the simulator.

Why is this Singleton Location Manager class returning a nil Location?

I am trying to use the below LocationSingleton Class from this blog in my project. I like the simplicity of its usage.
You start updating location by simply calling:
LocationSingleton.sharedInstance.startUpdatingLocation()
Get the last location by simply calling:
LocationSingleton.sharedInstance.lastLocation
My intention is to start location services, get the last location so that I can then fetch users from Firebase using the location returned.
The issue is that if I call lastLocation straight after startUpdatingLocation it returns nil.
After some debugging I've found the reason is because location services are slow to start on the device and therefore when lastLocation is called the devices hasn't acquired the location yet. I would like to execute the next command as soon as the lastLocation has been recorded. How can I achieve that?
I would like to understand how the Protocol is being used?
import UIKit
import CoreLocation
protocol LocationServiceDelegate {
func locationDidUpdateToLocation(currentLocation: CLLocation)
func locationUpdateDidFailWithError(error: NSError)
}
class LocationSingleton: NSObject,CLLocationManagerDelegate {
var locationManager: CLLocationManager?
var lastLocation: CLLocation?
var delegate: LocationServiceDelegate?
static let sharedInstance:LocationSingleton = {
let instance = LocationSingleton()
return instance
}()
override init() {
super.init()
self.locationManager = CLLocationManager()
guard let locationManagers=self.locationManager else {
return
}
if CLLocationManager.authorizationStatus() == .notDetermined {
//locationManagers.requestAlwaysAuthorization()
locationManagers.requestWhenInUseAuthorization()
}
if #available(iOS 9.0, *) {
// locationManagers.allowsBackgroundLocationUpdates = true
} else {
// Fallback on earlier versions
}
locationManagers.desiredAccuracy = kCLLocationAccuracyBest
locationManagers.pausesLocationUpdatesAutomatically = false
locationManagers.distanceFilter = 0.1
locationManagers.delegate = self
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
self.lastLocation = location
updateLocation(currentLocation: location)
}
#nonobjc func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
locationManager?.requestWhenInUseAuthorization()
break
case .authorizedWhenInUse:
locationManager?.startUpdatingLocation()
break
case .authorizedAlways:
locationManager?.startUpdatingLocation()
break
case .restricted:
// restricted by e.g. parental controls. User can't enable Location Services
break
case .denied:
// user denied your app access to Location Services, but can grant access from Settings.app
break
default:
break
}
}
// Private function
private func updateLocation(currentLocation: CLLocation){
guard let delegate = self.delegate else {
return
}
delegate.locationDidUpdateToLocation(currentLocation: currentLocation)
}
private func updateLocationDidFailWithError(error: NSError) {
guard let delegate = self.delegate else {
return
}
delegate.locationUpdateDidFailWithError(error: error)
}
func startUpdatingLocation() {
print("Starting Location Updates")
self.locationManager?.startUpdatingLocation()
// self.locationManager?.startMonitoringSignificantLocationChanges()
}
func stopUpdatingLocation() {
print("Stop Location Updates")
self.locationManager?.stopUpdatingLocation()
}
func startMonitoringSignificantLocationChanges() {
self.locationManager?.startMonitoringSignificantLocationChanges()
}
}
The location manager works asynchronously and provides delegate methods to get the result.
In your class adopt LocationServiceDelegate, implement the delegate methods and set the delegate for example in viewDidLoad
func locationDidUpdateToLocation(currentLocation: CLLocation)
{
print(LocationSingleton.sharedInstance.lastLocation)
}
func locationUpdateDidFailWithError(error: NSError)
{
print(error)
}
func viewDidLoad()
super viewDidLoad()
let locationSingleton = LocationSingleton.sharedInstance
locationSingleton.delegate = self
locationSingleton.startUpdatingLocation()
}
When a location is detected, one of the delegate methods is called

Swift2 didRangeBeacons never called

I'm trying to implement a dedicated class to scan for a specific iBeacon (in foreground and in background) but the function didRangeBeacons is never called.
I also set the parameter NSLocationAlwaysUsageDescription in my info.plist.
here is the class I developed:
class iBeacon: NSObject, UIApplicationDelegate, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
var beaconRegion:CLBeaconRegion!
override init(){
super.init()
print("init...")
let uuidString = "B737D0E7-AF53-9B83-E5D2-922140A91234"
let beaconIdentifier = "nbuit-06B908"
let beaconUUID:NSUUID = NSUUID(UUIDString: uuidString)!
beaconRegion = CLBeaconRegion(proximityUUID: beaconUUID,
identifier: beaconIdentifier)
beaconRegion.notifyOnEntry = true
locationManager = CLLocationManager()
locationManager.delegate = self
if(locationManager!.respondsToSelector("requestAlwaysAuthorization")) {
locationManager!.requestAlwaysAuthorization()
}
locationManager!.pausesLocationUpdatesAutomatically = false
locationManager!.startMonitoringForRegion(beaconRegion)
locationManager!.startRangingBeaconsInRegion(beaconRegion)
locationManager!.startUpdatingLocation()
}
func sendLocalNotificationWithMessage(message: String!) {
let notification:UILocalNotification = UILocalNotification()
notification.alertBody = message
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
func locationManager(manager: CLLocationManager!, didStartMonitoringForRegion region: CLRegion!){
locationManager.requestStateForRegion(region)
}
func locationManager(manager: CLLocationManager!, didDetermineState state: CLRegionState, forRegion region: CLRegion!) {
if state == CLRegionState.Inside {
locationManager.startRangingBeaconsInRegion(beaconRegion)
}
else {
locationManager.stopRangingBeaconsInRegion(beaconRegion)
}
}
func locationManager(manager: CLLocationManager,
didRangeBeacons beacons: [CLBeacon],
inRegion region: CLBeaconRegion) {
print("didRangeBeacons");
var message:String = ""
if(beacons.count > 0) {
let nearestBeacon:CLBeacon = beacons[0] as! CLBeacon
switch nearestBeacon.proximity {
case CLProximity.Far:
message = "You are far away from the beacon"
case CLProximity.Near:
message = "You are near the beacon"
case CLProximity.Immediate:
message = "You are in the immediate proximity of the beacon"
case CLProximity.Unknown:
return
}
} else {
message = "No beacons are nearby"
}
print("%#", message)
sendLocalNotificationWithMessage(message)
}
func locationManager(manager: CLLocationManager!, didEnterRegion region: CLRegion!) {
print("Beacon in range")
}
func locationManager(manager: CLLocationManager!, didExitRegion region: CLRegion!) {
print("No beacons in range")
}
func locationManager(manager: CLLocationManager!, monitoringDidFailForRegion region: CLRegion!, withError error: NSError!) {
print("Failed monitoring region: \(error.description)")
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
print("Location manager failed: \(error.description)")
}
}
I'm not able to find the reason why my iBeacon is not detected. I also checked for the UUID and name in a BLE scanner app and it seems to be correct.
If you have any idea to help me that would be great.
EDIT:
I Finally found the reason why the LocationManager was never started, I was instantiating my class like this:
let qualityOfServiceClass = DISPATCH_QUEUE_PRIORITY_DEFAULT
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
print("Running beacon detection in the background queue")
beaconSharedInstance
})
I was expecting to get this instance running in background, but by simply calling my instance like this
beaconSharedInstance
it starts and scans for regions correctly. but as soon as my app is inactive, I have the following log:
Ending background task
and my app stops scanning. I set the following parameters
locationManager.allowsBackgroundLocationUpdates = true
and also set Background Mode capability of my project.
any idea why my instance is stopped when the app is in background ?