Erfahrungen mit CLMonitor und CLServiceSession in iOS18

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Erfahrungen mit CLMonitor und CLServiceSession in iOS18

    Hallo Zusammen,

    hat jemand schon mal in iOS18 mit CLMonitor eine Region überwacht?
    Mit iOS17 hat bei mir die Überwachung einer Region wunderbar funktioniert, nach dem Update auf einem realen Gerät auf iOS18 regt sich die App im Hintergrund nicht mehr.

    Gruß Marvin

    Quellcode

    1. @Observable
    2. class LocationManager: NSObject, @preconcurrency CLLocationManagerDelegate, @unchecked Sendable {
    3. static let shared = LocationManager()
    4. // DispatchQueue für synchrone Zugriffe
    5. private let queue = DispatchQueue(label: "com.example.locationManager", attributes: .concurrent)
    6. let locationManager: CLLocationManager = CLLocationManager()
    7. var region: MKCoordinateRegion = MKCoordinateRegion()
    8. var error: LocationError? = nil
    9. var monitor: CLMonitor?
    10. var authorizationStatus : CLAuthorizationStatus = .authorizedAlways
    11. var authorizationFault: Bool = false
    12. var badge: Int = 0
    13. var userLocationData: UserLocationData?
    14. /** Für Authentifizierungssitzungen und Benachrichtigungen*/
    15. private var authSession: CLServiceSession?
    16. private let notificationCenter = UNUserNotificationCenter.current()
    17. private var notificationContent = UNMutableNotificationContent()
    18. /** Adressvariablen*/
    19. var strasse : String = ""
    20. var hausnummer : String = ""
    21. var plz : String = ""
    22. var ort : String = ""
    23. var ortsteil : String = ""
    24. var landKreis : String = ""
    25. var bundesland : String = ""
    26. var land : String = ""
    27. var isoCountryCode : String = ""
    28. var zeitzone : TimeZone?
    29. var detectedIBeacons: [iBeacon] = []
    30. var isScanning: Bool = false
    31. override init() {
    32. super.init()
    33. self.locationManager.delegate = self
    34. self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    35. self.locationManager.distanceFilter = kCLDistanceFilterNone
    36. self.locationManager.allowsBackgroundLocationUpdates = true
    37. self.locationManager.showsBackgroundLocationIndicator = true
    38. self.locationManager.activityType = .fitness
    39. /** Benachrichtigung konfigurieren*/
    40. notificationContent.title = "Location monitoring inactive"
    41. notificationContent.body = "Can't receive condition events while not in the foreground"
    42. Task {
    43. try await notificationCenter.requestAuthorization(options: [.badge])
    44. }
    45. }
    46. func requestUserAuthorization() async throws {
    47. queue.async(flags: .barrier) {
    48. self.locationManager.requestAlwaysAuthorization()
    49. }
    50. }
    51. // Implementiere die startAuthSession-Funktion
    52. func startAuthSession() {
    53. Task {
    54. print("Listening to session diagnostics")
    55. authSession = CLServiceSession(authorization: .always, fullAccuracyPurposeKey: "monitor")
    56. for try await diagnostics in authSession!.diagnostics {
    57. // Speichern der letzten Diagnosedaten
    58. // lastDiagnosticUpdate = diagnostics (optional, falls verwendet)
    59. print("Diadnostics: \(diagnostics)")
    60. let notification = UNNotificationRequest(identifier: "com.example.mynotification", content: notificationContent, trigger: nil)
    61. if diagnostics.insufficientlyInUse {
    62. try await notificationCenter.add(notification)
    63. }
    64. }
    65. }
    66. }
    67. // Funktion zum Stoppen der Authentifizierungssitzung
    68. func stopAuthSession() {
    69. Task {
    70. print("Stopping session diagnostics")
    71. authSession?.invalidate() // Sitzungsinvalidation
    72. authSession = nil
    73. }
    74. }
    75. func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
    76. queue.async(flags: .barrier) {
    77. switch self.locationManager.authorizationStatus {
    78. case .notDetermined:
    79. self.locationManager.requestWhenInUseAuthorization()
    80. case .authorizedAlways, .authorizedWhenInUse:
    81. self.locationManager.requestLocation()
    82. case .denied:
    83. self.error = .authorizationDenied
    84. case .restricted:
    85. self.error = .authorizationRestricted
    86. @unknown default:
    87. break
    88. }
    89. }
    90. }
    91. func checkLocationServicesAuthorizationOnAppStart(logbook: Bool) async {
    92. let authorizationStatus = LocationAuthorizationStatus(locationManager.authorizationStatus)
    93. switch authorizationStatus {
    94. case .notDetermined:
    95. print("Der Standortautorisierungsstatus wurde nicht ermittelt.")
    96. authorizationFault = true
    97. case .authorizedAlways:
    98. print("Der Standortautorisierungsstatus ist immer autorisiert.")
    99. authorizationFault = false
    100. case .authorizedWhenInUse:
    101. print("Der Standortautorisierungsstatus ist bei Verwendung autorisiert.")
    102. authorizationFault = true
    103. case .denied:
    104. print("Der Status der Standortautorisierung ist verweigert.")
    105. authorizationFault = true
    106. case .restricted:
    107. print("Der Standortautorisierungsstatus ist eingeschränkt.")
    108. authorizationFault = true
    109. }
    110. }
    111. func startUpdateLocation() {
    112. queue.async(flags: .barrier) {
    113. self.locationManager.startUpdatingLocation()
    114. }
    115. }
    116. func stopUpdateLocation() {
    117. queue.async(flags: .barrier) {
    118. self.locationManager.stopUpdatingLocation()
    119. }
    120. }
    121. func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    122. queue.async(flags: .barrier) {
    123. guard let location = locations.last else { return }
    124. let updatedData = UserLocationData(
    125. userLocation: location,
    126. coordinates: location.coordinate,
    127. lat: location.coordinate.latitude,
    128. lon: location.coordinate.longitude,
    129. speed: location.speed,
    130. speedAccuracy: location.speedAccuracy,
    131. altitude: location.altitude,
    132. floorLevel: location.floor?.level ?? 0,
    133. floor: location.floor,
    134. horizontalAccuracy: location.horizontalAccuracy,
    135. verticalAccuracy: location.verticalAccuracy,
    136. timestamp: location.timestamp,
    137. courseAccuracy: location.courseAccuracy,
    138. course: location.course
    139. )
    140. self.userLocationData = updatedData
    141. }
    142. }
    143. func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    144. queue.async(flags: .barrier) {
    145. print("Error \(error.localizedDescription)")
    146. self.locationManager.stopUpdatingLocation()
    147. self.locationManager.stopUpdatingHeading()
    148. if let clError = error as? CLError {
    149. switch clError.code {
    150. case .locationUnknown:
    151. self.error = .unknownLocation
    152. case .denied:
    153. self.error = .accessDenied
    154. case .network:
    155. self.error = .network
    156. default:
    157. self.error = .operationFailed
    158. }
    159. }
    160. }
    161. }
    162. func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
    163. print("Region: DetermineState \(region.identifier)")
    164. }
    165. func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
    166. print("Region: Enter \(region.identifier)")
    167. }
    168. func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
    169. print("Region: Exit \(region.identifier)")
    170. }
    171. func locationManager(_ manager: CLLocationManager, didVisit visit: CLVisit) {
    172. print("Region: Date\(visit.arrivalDate)")
    173. }
    174. func locationManager(_ manager: CLLocationManager, didFailRangingFor beaconConstraint: CLBeaconIdentityConstraint, error: Error) {
    175. print("Failed ranging beacons: \(error.localizedDescription)")
    176. }
    177. func startMonitoringCLMonitor(minVerzoegerung: TimeInterval) async {
    178. // Initialisiere den Monitor, wenn er noch nicht existiert
    179. if monitor == nil {
    180. monitor = await CLMonitor("StechuhrPro")
    181. }
    182. self.startAuthSession()
    183. do {
    184. // Durchlaufe die Events und fange Fehler ab
    185. for try await event in await monitor!.events {
    186. startUpdateLocation()
    187. let identifier = event.identifier
    188. let name = await checkOrteNameByUUID(id: UUID(uuidString: identifier)!)
    189. switch event.state {
    190. case .satisfied:
    191. await handleSatisfiedState(for: identifier, with: name, minVerzoegerung: minVerzoegerung)
    192. case .unsatisfied:
    193. await handleUnsatisfiedState(for: identifier, with: name, minVerzoegerung: minVerzoegerung)
    194. case .unknown:
    195. print("Status unbekannt \(event.identifier) Date/Time: \(Date())")
    196. await addLogbookEntry(category: .location, ereignis: "Der Status der Region \(name) ist unbekannt")
    197. case .unmonitored:
    198. print("Region nicht überwacht \(event.identifier) Date/Time: \(Date())")
    199. await addLogbookEntry(category: .location, ereignis: "Region \(name) wird nicht überwacht")
    200. @unknown default:
    201. print("Unbekannter Zustand")
    202. }
    203. stopUpdateLocation()
    204. }
    205. } catch {
    206. print("Fehler beim Empfangen der Events: \(error.localizedDescription)")
    207. // Hier kannst du den Fehler protokollieren oder spezifisch behandeln
    208. }
    209. }
    210. }
    Alles anzeigen
  • Edit
    Du hast Recht es gibt Einträge die ich übersehen habe, werde das mal ausprobieren.


    Es gibt ein Demo Projekt von Apple, da ist nur die Neuerung mit der CLServiceSession. Info.plist war keine Änderung zu erkennen. Das kuriose ist das der Code oben jetzt im Simulator läuft auch im Hintergrund aber auf dem Realen Gerät nicht. Heute wurde beim Testen ein Betreten einer Region erkannt und gemeldet, dann war wieder fertig. Ich habe leider auch keine Doku gefunden wie die CLServiceSession eigentlich korrekt aufgerufen wird. Ich habe sie da aufgerufen wo der CLMonitor aufgerufen wird. Sehr komisch das ganze. Beim Xcode >= 16 kann man auch keine GPX Dateien mehr auswählen, diese sind alle ausgegraut. Vielleicht ist das auch ein Swift iOS Bug.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Marvin75 ()