watchOS didUpdateLocation freezes during location change - swift

I am developing a running app in SwiftUI for watchOS. The problem is that when I start the run, the measuring works fine as 0.55 0.56 0.57 km, etc. Then it randomly freezes, and jumps from 0.57 straight to 0.71. Even the total distance is sometimes inaccurate. When I look at the map of the run, I can sometimes see sharp corners and straight lines in curves, implying very inaccurate measurement. The OS is watchOS 8.0. I have tried kCLLocationAccuracyBest to no avail.
I have used CLLocationManager in an ObservableObject called DistanceObservable, and I have this exact code in iOS app that measures the distance perfectly.
... #Published var distance: Double = 0.0
func startUpdate() {
self.manager = CLLocationManager()
self.manager?.delegate = self
self.manager?.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.manager?.requestWhenInUseAuthorization()
self.manager?.distanceFilter = 10.0
self.manager?.allowsBackgroundLocationUpdates = true
self.manager?.startUpdatingLocation()
}
Here is my watchOS didUpdateLocations method.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var locations = locations
for newLocation in locations {
let howRecent = newLocation.timestamp.timeIntervalSinceNow
guard newLocation.horizontalAccuracy < 20 && abs(howRecent) < 10 else { continue }
if let lastLocation = locationList.last {
let delta = newLocation.distance(from: lastLocation)
if (delta < 60) { // To prevent inaccurate "jumps" in distance
distance = distance + delta
if UserDefaults.standard.value(forKey: "hasSet") as!Bool == false {
distance = 0
locations.removeAll()
UserDefaults.standard.setValue(true, forKey: "hasSet")
}
latPoints.append(Double(locationList.last!.coordinate.latitude))
lonPoints.append(Double(locationList.last!.coordinate.longitude))
}
}
locationList.append(newLocation)
}
}
Finally I use the #EnvironmentObject in a SwiftUI View.
struct MyView: View {
#EnvironmentObject var loc: DistanceObservable
var body: some View {
Text("\(loc.distance)")
}
}

I have solved the issue by starting new HealthKit workout.
import HealthKit
Add conformance to ObservableObject and its methods
class SwiftUILocation: NSObject, CLLocationManagerDelegate, ObservableObject, HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate {
func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
print("A")
}
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
print("B")
}
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
print("C")
}
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
print("D")
}
//.. rest of the code
}
Start HealthKit at the start of the workout
func startHealtKit(){
let configuration = HKWorkoutConfiguration()
configuration.activityType = .running
configuration.locationType = .outdoor
do {
self.workoutSession = try HKWorkoutSession(healthStore: healthStore,
configuration:configuration)
self.builder = self.workoutSession?.associatedWorkoutBuilder()
self.builder?.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore,
workoutConfiguration: configuration)
self.workoutSession?.delegate = self
self.builder?.delegate = self
self.workoutSession?.startActivity(with: Date())
self.builder?.beginCollection(withStart: Date()) { (success, error) in
// Indicate that the session has started.
}
}
catch {
print("HealthKit problem \(error.localizedDescription)")
}
}
Stop HealtkKit at the end of the workout
func stopHealthKit(){
self.builder?.workoutSession?.stopActivity(with: Date())
self.builder?.endCollection(withEnd: Date()) { (success, error) in
// Indicate that the session has started.
}
}

Related

Display the distance between two coordinates in km in SwiftUI

In my project, I'm fetching the data about the houses/buildings and along with other details, I show how far it is from the user. I'm only a beginner, so I will try to be as much explicit as possible.
My issue is that I don't know where to put the function that calculates the distance in KM and how to call it properly in the MVVM project. See I have a ViewModel file that includes ViewModel class responsible for Networking and a LocationManager class
responsible for tracking user location. The latitude and longitude come from the ViewModel from API and I believe the distance calculation should be made in LocationManager. I'm not sure how can I "connect" these two classes.
My main goals are:
Figure out where to put the func to calculate the distance. User coords are accessible from the LocationManager and house coords are accessible from the API. I would like to know if there's a way to merge these two classes to use the data in one func.
Understand if the distanceInKM method is correct. Despite it doesn't throw, it still displays a placeholder value.
As the minimum reproducible project, here's the code:
ContentView:
struct ContentView: View {
#ObservedObject var viewModel = ViewModel()
#StateObject var locationManager = LocationManager()
var body: some View {
VStack {
HouseListView(housesVM: viewModel)
}
.onAppear {
viewModel.fetchHouses()
}
}
}
HouseListView(A view that's called in ContentView:
struct HouseListView: View {
#ObservedObject var housesVM: ViewModel
#StateObject var locationManager = LocationManager()
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading) {
ForEach(housesVM.info, id: \.id) { house in
GetHouseCellView(
distance: Double(locationManager.distanceInKM(latitude: house.latitude, longitude: house.longitude)) ?? 0.00, //Here's the place where function is called
bedrooms: house.bedrooms)
}
}
}
}
private func GetHouseCellView(distance: Double, bedrooms: Int) -> some View {
HStack(spacing: 20) {
Label("\(bedrooms)", image: "bed-2")
Label("\(distance) km", image: "pin") //Here the final distance should be displayed, i.e 36,4 km
}
}
Quite basic ViewModel:
class ViewModel: ObservableObject {
#Published var info: [Houses] = []
func fetchHouses() {
//URL & URLRequest here
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
let decoder = JSONDecoder()
do {
if let data = data {
let result = try decoder.decode([Houses].self, from: data)
DispatchQueue.main.async {
self.info = result
}
}
} catch {
print("whoopsie! There's an error: \(error.localizedDescription)")
}
}
dataTask.resume()
}
}
}
And a LocationManager:
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
#Published var locationStatus: CLAuthorizationStatus?
#Published var lastLocation: CLLocation?
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
var statusString: String {
guard let status = locationStatus else {
return "unknown"
}
switch status {
case .notDetermined: return "notDetermined"
case .authorizedWhenInUse: return "authorizedWhenInUse"
case .authorizedAlways: return "authorizedAlways"
case .restricted: return "restricted"
case .denied: return "denied"
default: return "unknown"
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
locationStatus = status
print(#function, statusString)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
lastLocation = location
print(#function, location)
}
func distanceInKM(latitude: Int, longitude: Int) -> Double { //Here's the method I made to calculate an actual distance
let houseCoordinates = CLLocation(latitude: CLLocationDegrees(latitude), longitude: CLLocationDegrees(longitude))
let userCoordinates = CLLocation(latitude: lastLocation?.coordinate.latitude ?? 50, longitude: lastLocation?.coordinate.longitude ?? 30)
let distance = userCoordinates.distance(from: houseCoordinates) / 1000 //.distance comes in meters so /1000 is to have a KM value
let s = String(format: "%.0f", distance)
return Double(s + "Km") ?? 35.5 // This value of 35.5 as placeholder is constantly displayed instead of the actual value
}
}
This func logic was taken from this post
Simple type error :
Double(s + "Km")
will always return nil as s + "Km" is not a valid double.
Just Return :
Double(distance)
If you want to return a String change the type of the method and return
s + "km"
EDIT :
If you want to display distance in km : in GetHouseCellView change distance label to :
Label(String(format: "%.2f km", distance), image: "pin")
It is better to format the distance Double when you use it.

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

What is the cause of the zombies in the following code

I have the following class for collecting device motion data:
class MotionManager: NSObject {
static let shared = MotionManager()
private override init() {}
// MARK: - Class Variables
private let motionManager = CMMotionManager()
fileprivate lazy var locationManager: CLLocationManager = {
var locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.activityType = .fitness
locationManager.distanceFilter = 10.0
return locationManager
}()
private let queue: OperationQueue = {
let queue = OperationQueue()
queue.name = "MotionQueue"
queue.qualityOfService = .utility
return queue
}()
fileprivate var motionDataRecord = MotionDataRecord()
private var attitudeReferenceFrame: CMAttitudeReferenceFrame = .xTrueNorthZVertical
var interval: TimeInterval = 0.01
var startTime: TimeInterval?
// MARK: - Class Functions
func start() {
startTime = Date().timeIntervalSince1970
startDeviceMotion()
startAccelerometer()
startGyroscope()
startMagnetometer()
startCoreLocation()
}
func startCoreLocation() {
switch CLLocationManager.authorizationStatus() {
case .authorizedAlways:
locationManager.startUpdatingLocation()
locationManager.startUpdatingHeading()
case .notDetermined:
locationManager.requestAlwaysAuthorization()
case .authorizedWhenInUse, .restricted, .denied:
break
}
}
func startAccelerometer() {
if motionManager.isAccelerometerAvailable {
motionManager.accelerometerUpdateInterval = interval
motionManager.startAccelerometerUpdates(to: queue) { (data, error) in
if error != nil {
log.error("Accelerometer Error: \(error!)")
}
guard let data = data else { return }
self.motionDataRecord.accelerometer = data
}
} else {
log.error("The accelerometer is not available")
}
}
func startGyroscope() {
if motionManager.isGyroAvailable {
motionManager.gyroUpdateInterval = interval
motionManager.startGyroUpdates(to: queue) { (data, error) in
if error != nil {
log.error("Gyroscope Error: \(error!)")
}
guard let data = data else { return }
self.motionDataRecord.gyro = data
}
} else {
log.error("The gyroscope is not available")
}
}
func startMagnetometer() {
if motionManager.isMagnetometerAvailable {
motionManager.magnetometerUpdateInterval = interval
motionManager.startMagnetometerUpdates(to: queue) { (data, error) in
if error != nil {
log.error("Magnetometer Error: \(error!)")
}
guard let data = data else { return }
self.motionDataRecord.magnetometer = data
}
} else {
log.error("The magnetometer is not available")
}
}
func startDeviceMotion() {
if motionManager.isDeviceMotionAvailable {
motionManager.deviceMotionUpdateInterval = interval
motionManager.startDeviceMotionUpdates(using: attitudeReferenceFrame, to: queue) { (data, error) in
if error != nil {
log.error("Device Motion Error: \(error!)")
}
guard let data = data else { return }
self.motionDataRecord.deviceMotion = data
self.motionDataRecord.timestamp = Date().timeIntervalSince1970
self.handleMotionUpdate()
}
} else {
log.error("Device motion is not available")
}
}
func stop() {
locationManager.stopUpdatingLocation()
locationManager.stopUpdatingHeading()
motionManager.stopAccelerometerUpdates()
motionManager.stopGyroUpdates()
motionManager.stopMagnetometerUpdates()
motionManager.stopDeviceMotionUpdates()
}
func handleMotionUpdate() {
print(motionDataRecord)
}
}
// MARK: - Location Manager Delegate
extension MotionManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedAlways || status == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
} else {
locationManager.stopUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
motionDataRecord.location = location
}
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
motionDataRecord.heading = newHeading
}
}
However I'm getting EXC_BAD_ACCESS after it runs for a while. I ran the zombie instrument and it appears that handleMotionUpdate() is the caller at fault. And MotionDataRecord or some of it's properties are what are being deallocated somehow...
MotionDataRecord is a struct:
struct MotionDataRecord {
var timestamp: TimeInterval = 0
var location: CLLocation?
var heading: CLHeading?
var motionAttitudeReferenceFrame: CMAttitudeReferenceFrame = .xTrueNorthZVertical
var deviceMotion: CMDeviceMotion?
var altimeter: CMAltitudeData?
var accelerometer: CMAccelerometerData?
var gyro: CMGyroData?
var magnetometer: CMMagnetometerData?
}
Any ideas what's going on here?
Edit:
Have added a stripped down version of the project to github here
Edit:
Screenshot of zombies instrument:
Okay, I'm going to try to do a little thought-experiment to suggest what might be happening here.
Keep in mind first the following points:
Your MotionDataRecord is a struct consisting almost entirely of reference type instance properties. This forces the struct to participate in reference counting.
You are wildly accessing the properties of this struct on different threads. Your locationManager:didUpdateLocations: sets motionDataRecord.location on the main thread, while e.g. your motionManager.startDeviceMotionUpdates sets motionDataRecord.deviceMotion on a background thread (queue).
Every time you set a struct property, you mutate the struct. But there is actually no such thing as struct mutation in Swift: a struct is a value type. What really happens is that the entire struct is copied and replaced (initializeBufferWithCopyOfBuffer in the zombie log).
Okay, so on multiple simultaneous threads you are coming in and replacing your struct-full-of-references. Each time you do that, one struct copy goes out of existence and another comes into existence. It's a struct-full-of-references, so this involves reference counting.
So suppose the process looks like this:
Make the new struct.
Set the new struct's reference properties to the old struct's reference properties (except for the one we are changing) by copying the references. There is some retain-and-release here but it all balances out.
Set the new struct's reference property that we are replacing. This does a retain on the new value and releases the old value.
Swap the new struct into place.
But none of that is atomic. Thus, those steps can run out of order, interleaved between one another, because (remember) you've got more than one thread accessing the struct at the same time. So imagine that, on another thread, we access the struct between steps and 3 and 4. In particular, between steps 3 and 4 on one thread, we perform steps 1 and 2 on the other thread. At that moment, the old struct is still in place, with its reference to the property that we are replacing pointing to garbage (because it was released and deallocated in step 3 on the first thread). We attempt to do our copy on the garbage property. Crash.
So, in a nutshell, I would suggest (1) make MotionDataRecord a class instead of a struct, and (2) get your threading straightened out (at the very least, get onto the main thread in the CMMotionManager callbacks before you touch the MotionDataRecord).

Swift Beacon doesn't work now using Swift 3.0

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.