On iPhone, we can use iBeacon to wake up the APP in the background for Bluetooth scanning connection, now we want to port the function to AppleWatch APP, but the API related to iBeacon is not applicable on watchOS, does watchOS have a similar wake up mechanism?
Post
Replies
Boosts
Views
Activity
We have to draw polygons inside a MKMapView based on coordinates retrieved from external source.
It seems that MapKit does not behave correctly where polygons have single-vertex self-intersection.
Here it's a simple point list example (every element is a pair of latitude and longitude values):
[(0, 0), (20, 0), (10, 10), (20, 20), (0, 20), (10, 10), (0, 0)]
The next image shows the rendering issue.
But if the list is slightly changed in this way
[(0, 0), (20, 0), (10, 10), (20, 20), (0, 20), (15, 10), (0, 0)]
the issue disappears. The next image shows it.
So it's not a self-intersection and self-tangency problem, but we think single-vertex self-intersection is a buggy edge case for MapKit.
Right now we fixed this problem by finding the duplicated coordinates and applying a small offset (1e-8) to one of them, but it's a temporary solution and adds rendering delay.
The problem is not due to iOS versions, since iOS 17 and 18 have the same issue. Also it happens on simulators and real devices.
Here is the playground example, based mostly on the "Map Playground" template Xcode offers. If you run it without modifying it, the playground shows the "bugged" polygon. If you use notBugPoints instead of the default bugPoints for the polygon, the playground shows the "not-bugged" polygon.
import MapKit
import PlaygroundSupport
// Create an MKMapViewDelegate to provide a renderer for our overlay
class MapViewDelegate: NSObject, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let overlay = overlay as? MKPolygon {
let polygonRenderer = MKPolygonRenderer(overlay: overlay)
polygonRenderer.fillColor = .red
return polygonRenderer
}
return MKOverlayRenderer(overlay: overlay)
}
}
// Create a strong reference to a delegate
let delegate = MapViewDelegate()
// Create an MKMapView
let mapView = MKMapView(frame: CGRect(x: 0, y: 0, width: 800, height: 800))
mapView.delegate = delegate
// Configure The Map elevation and emphasis style
let configuration = MKStandardMapConfiguration(elevationStyle: .realistic, emphasisStyle: .default)
mapView.preferredConfiguration = configuration
// Create an overlay
let bugPoints = [
MKMapPoint(CLLocationCoordinate2DMake(0, 0)),
MKMapPoint(CLLocationCoordinate2DMake(20, 0)),
MKMapPoint(CLLocationCoordinate2DMake(10, 10)),
MKMapPoint(CLLocationCoordinate2DMake(20, 20)),
MKMapPoint(CLLocationCoordinate2DMake(0, 20)),
MKMapPoint(CLLocationCoordinate2DMake(10, 10)),
MKMapPoint(CLLocationCoordinate2DMake(0, 0))
]
let notBugPoints = [
MKMapPoint(CLLocationCoordinate2DMake(0, 0)),
MKMapPoint(CLLocationCoordinate2DMake(20, 0)),
MKMapPoint(CLLocationCoordinate2DMake(10, 10)),
MKMapPoint(CLLocationCoordinate2DMake(20, 20)),
MKMapPoint(CLLocationCoordinate2DMake(0, 20)),
MKMapPoint(CLLocationCoordinate2DMake(15, 10)),
MKMapPoint(CLLocationCoordinate2DMake(0, 0))
]
let polygon = MKPolygon(points: bugPoints, count: notBugPoints.count)
mapView.addOverlay(polygon)
// Frame our annotation and overlay
mapView.camera = MKMapCamera(lookingAtCenter: CLLocationCoordinate2DMake(10, 10), fromDistance: 5000000, pitch: 0, heading: 0)
// Add the created mapView to our Playground Live View
PlaygroundPage.current.liveView = mapView
Hi,
Have been trying to work with MapkitJS for a website, but I'm stumped on once basic capability: I want to be able to click on a point of interest, and perform some actions such as:
Get its coordinates
Attach an annotation to it (e.g. a callout)
In my code, PointOfInterest's are selectable:
map.selectableMapFeatures = [
mapkit.MapFeatureType.PointOfInterest,
];
But when I click on one, I do see the marker pop up but nothing else (which is not much help since there is no additional information in the marker itself). I see no event getting triggered that I can do something with.
I am using an event listener as follows:
map.addEventListener('single-tap', (event) => {
const coordinate = map.convertPointOnPageToCoordinate(event.pointOnPage);
console.log('Map tapped at:', coordinate);
console.log('Map tapped event:', event);
...
I guess I have to grab the Place ID somehow but I don't know how to.
Thanks for any help.
I encountered a crash in iOS 17 related to CLBackgroundActivitySession, which appears to be due to misleading guidance in an Apple’s WWDC video.
Crash sample code: https://github.com/steve-ham/AppleLocationCrash
Simplified Reproduction Steps:
1. Open the GitHub sample app.
2. Archive and export (Distribute App -> Custom -> (Release Testing, Enterprise, or Debugging) -> Export).
3. Open the app.
4. Tap enableBackgroundLocation -> select Allow While Using App on the system popup.
5. Tap disableBackgroundLocation.
6. Go to the iOS home screen.
7. Wait for 10 seconds.
8. Reopen the app -> crash occurs.
The crash happens because setting CLBackgroundActivitySession to nil does not end the session, despite Apple’s guidance suggesting it should. Below is the exact quote from WWDC 2023, which explicitly states that both calling invalidate() or letting the object get destroyed (i.e., setting to nil) would end the session:
WWDC 2023 Discover Streamlined Location Updates (https://developer.apple.com/videos/play/wwdc2023/10180/)
“Before starting the updates, you should instantiate a CLBackgroundActivitySession object to start a new session. Note, we are assigning the session to self.backgroundActivity, which is a property and not to a local variable. And this is important because if we used a local variable, then when it goes out of scope, the object it holds would be deallocated, invalidating the session and potentially ending your app’s access to location. Then when we want to end our session, we can do that by sending the invalidate message or by letting the object be destroyed.”
I’ve submitted this to Apple for resolution but wanted to share this with the community. This misguidance has caused issues in my app’s release. If Apple could reply to confirm or provide clarification, it would be greatly appreciated.
P.S. Even a minimal implementation in viewDidLoad triggers the crash:
let session = CLBackgroundActivitySession()
print("session (session)")
Can I integrate Timer along with liveUpdates(_:) in order to manipulate the frequency. Let say for example I need the user location ever 1 min. Is it possible to do so?
In my app, I am using geofencing to perform an action when the user enter or leaves a specified location. The geofencing (CLMonitor) is active permanently, and should work across multiple app sessions or after the device is restarted. It should also work after the app was minimized or terminated. This worked perfectly with iOS 17 and prior, but with iOS 18, things changed. As soon as iOS 18 dropped, users were informing me that the app does no longer perform the entry/exit action reliably (without me making any changes to the app). Most of the times, events are missed entirely. Sometimes, after the user opens or resumes the app, duplicate events are delivered and/or events with the current time instead of the correct time of entry/exit.
I am making sure that the app has the "Always" location permission before geofencing is enabled
The gefocence radius is between 20 and 500m, but even with the max. radius specified, the geofencing is unreliable
For the same user and geofence, the entry/exit event is delivered occasionally, but not always
I am currently not using CLLocationManager.allowsBackgroundLocationUpdates (even though it's documented as "Apps that receive location updates when running in the background must include the UIBackgroundModes key (with the location value) in their app’s Info.plist file") because it wasn't necessary on iOS 17 and in my tests, using it didn't yield any improvements
In my search for what could have caused this change, I found this WWDC video about location authorization: . It appears that with iOS 18, it is now required to have an active CLServiceSession to ensure that location updates are delivered to my app. Even though the video is long (and I've watched it multiple times), some things are still unclear. For example, the docs state:
If your app actively receives and processes location updates and terminates, it should restart those APIs upon launch in order to continue receiving updates.
Also, in the video it is stated that:
... So your job, ..., is to make sure that your process launch logic knows what features it has been tasked with pursuing, and re-takes session objects...
But on the other hand it's also said that:
you can only start holding one (a CLServiceSession) when your app is in the foreground
and also
... CLMonitor.events won’t yield results when it is not in use, unless a session which was started in the foreground, ....
To summarize my questions, for the geofencing to work as described above:
when exactly do I need to create a CLServiceSession if the app is launched into the backgorund? Immediately in the applicationDidFinishLaunching method, even though the app is still in the background (applicationState is background)? Or later on, when the app is opened again by the user, e.g. in applicationDidBecomeActive (and applicationState is active)?
do I need to specify the background mode capability as noted in the Handling location updates in the background article?
do I need to create a CLBackgroundActivitySession as noted in the Handling location updates in the background article?
does it matter, which of the four initializer methods I am using to create the CLServiceSession (with CLServiceSessionAuthorizationRequirementAlways)?
does it matter if I specify NSLocationRequireExplicitServiceSession in the Info.plist or not when I already do ensure that the app has the "Always" location permission when the feature is being enabled
Does a CLServiceSession last indefinitely and should it only be invalidated once the user disables the feature?
Sometimes, I receive old location data from the location manager.
According to the following article, the CLCircularGeographicCondition has a limit whereby only 20 conditions can be monitored by any single app.
Monitoring the user’s proximity to geographic regions
While I understand the rationale behind this limit, 20 conditions seems quite low for some apps. It would be good if an app could request that the user opt-in to allowing more conditions if they understand the impact this might have on the battery etc.
I'm migrating an app presently to use CLCircularGeographicCondition instead of the now deprecated CLCircularRegion. It would be good if there were more guidance on how to use the new Core Location API's to monitor how many conditions are in use within an app and how they can be deactivated when no longer required, allowing the app to free up more of the 20 conditions available.