CLMonitor Add region after starting to monitor for event changes

I am currently working on an app that uses the CLMonitor to check when the user has entered a specific region. When the user enters the region, a new region should be monitored, and the old region should be removed.

Currently, I have a startMonitoring() method that contains the event handling logic:

func startMonitoringConditions() {
        Task {
            monitor = await CLMonitor(MonitorNames.monitorName)
            
            if let identifiers = await monitor?.identifiers {
                if identifiers.count == 0 {
                    if let coordinate = manager.location?.coordinate {
                        await addNewRegionAtCoordinate(coordinate: coordinate)
                    }
                }
                else {
                    print("Previous Monitor Region is used.")
                }
            }

            for try await event in await monitor!.events {
                if let coordinate = manager.location?.coordinate {
                    // do something...                  
                    await monitor!.remove(event.identifier)
                    await addNewRegionAtCoordinate(coordinate: coordinate)                    
                }
            }
        }
    }

Unfortunately, adding a new region will not update the events collection in the CLMonitor, so the new region's events will not be handled in this method.

Any help on how I could fix this problem would be greatly appreciated!

Add a Comment

Accepted Reply

I managed to create a workaround that is poor code design but should solve the issue.

func startMonitoringConditions() {
        Task {
            monitor = await CLMonitor(MonitorNames.monitorName)
            
            if let identifiers = await monitor?.identifiers {
                if identifiers.count == 0 {
                    if let coordinate = manager.location?.coordinate {
                        await addNewRegionAtCoordinate(coordinate: coordinate)
                    }
                }
                else {
                    print("Previous Monitor Region is used.")
                }
            }
             
            while continueTrackingRegions {
                for try await event in await monitor!.events {
                    if let coordinate = manager.location?.coordinate {
                        // do something                       
                        await monitor!.remove(event.identifier)
                        await addNewRegionAtCoordinate(coordinate: coordinate)
                        break
                    }
                }
            }
        }
    }

This update seems to work for me although I am unable to fully confirm if it works 100% of the time because I am now running into an issue with the new SwiftData API. Gotta love ModelContexts...

Replies

I managed to create a workaround that is poor code design but should solve the issue.

func startMonitoringConditions() {
        Task {
            monitor = await CLMonitor(MonitorNames.monitorName)
            
            if let identifiers = await monitor?.identifiers {
                if identifiers.count == 0 {
                    if let coordinate = manager.location?.coordinate {
                        await addNewRegionAtCoordinate(coordinate: coordinate)
                    }
                }
                else {
                    print("Previous Monitor Region is used.")
                }
            }
             
            while continueTrackingRegions {
                for try await event in await monitor!.events {
                    if let coordinate = manager.location?.coordinate {
                        // do something                       
                        await monitor!.remove(event.identifier)
                        await addNewRegionAtCoordinate(coordinate: coordinate)
                        break
                    }
                }
            }
        }
    }

This update seems to work for me although I am unable to fully confirm if it works 100% of the time because I am now running into an issue with the new SwiftData API. Gotta love ModelContexts...

Forgot to give an explanation for how I think this fix works:

My understanding is that CLMonitor.events will only contain events from regions that were added to the CLMonitor before running the for loop. Adding a new region to the CLMonitor inside of the for loop means that the new region's event updates will not appear in the CLMonitor.events collection in this loop. The workaround then is to stop listening for events and restart the for loop.

  • I have the same bug and I'm adding the first region before I start the event loop. The blue location arrow appears in the task bar and the simulated location is in the region, yet no even update happens and state remains as unknown.

Add a Comment

I experienced the same bug and have reported it as FB13696956

You can find my report on openradar (the link is not allowed here for some reason?). Another issue I noticed is the for try await events does not throw a cancellation exception when the outer task is cancelled like normal streams do.

Please submit your own feedback and reference my report so hopefully it will bring more attention to it so it can be fixed!