feat: Add background task to get user location

This commit is contained in:
Andrea Busi
2023-08-18 16:10:11 +02:00
parent 91a9bce03c
commit ec94db29b9
7 changed files with 296 additions and 28 deletions
@@ -28,11 +28,13 @@
654D18D625F9420500BB6DB0 /* EQNMapAnnotationPastquake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654D18C825F93CD700BB6DB0 /* EQNMapAnnotationPastquake.swift */; };
654D18DA25F9424700BB6DB0 /* EQNUtility+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0188E252F09C500C783F0 /* EQNUtility+Extensions.swift */; };
654D18DE25F943E200BB6DB0 /* EQNMapAnnotationUserReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653C67FB25F3D63500FE52AC /* EQNMapAnnotationUserReport.swift */; };
65506C052A8F950000AB6448 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65506C042A8F94FF00AB6448 /* CoreLocation.framework */; };
6552C13829262119008E723C /* Shogun in Frameworks */ = {isa = PBXBuildFile; productRef = 6552C13729262119008E723C /* Shogun */; };
6552C13A2926261D008E723C /* Shogun in Frameworks */ = {isa = PBXBuildFile; productRef = 6552C1392926261D008E723C /* Shogun */; };
6552C1462926DBA1008E723C /* AppPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6552C1452926DBA1008E723C /* AppPreferences.swift */; };
65583A05261B83BE00ECA9F9 /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65583A04261B83BE00ECA9F9 /* UIKit+Extensions.swift */; };
6562C80725FFA6B100C85273 /* SeismicNetworkViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6562C80625FFA6B100C85273 /* SeismicNetworkViewModel.swift */; };
6563DAA42AAF515F0072D309 /* BackgroundTaskIdentifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6563DAA32AAF515F0072D309 /* BackgroundTaskIdentifiable.swift */; };
656EB9362A15FD16009DADF3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 656EB9382A15FD16009DADF3 /* Localizable.stringsdict */; };
656EB9412A16288A009DADF3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 656EB9382A15FD16009DADF3 /* Localizable.stringsdict */; };
6586971125F44C26009C0182 /* EQNBlurredCloseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6586971025F44C26009C0182 /* EQNBlurredCloseButton.swift */; };
@@ -53,6 +55,9 @@
65DBFB7625E2BBF20041CBA6 /* GADTTemplateView.m in Sources */ = {isa = PBXBuildFile; fileRef = 65DBFB7225E2BBF20041CBA6 /* GADTTemplateView.m */; };
65EA58802A60269C0038EE9D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8C10B0BD2281FE7F00125C9F /* Localizable.strings */; };
65EA58822A60360D0038EE9D /* EQNRealtimePushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65EA58812A60360D0038EE9D /* EQNRealtimePushNotification.swift */; };
65F9B49C2A8CA22800F13260 /* BackgroundTaskManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9B49B2A8CA22800F13260 /* BackgroundTaskManager.swift */; };
65F9B49E2A8CA2AC00F13260 /* EQNBackgroundPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9B49D2A8CA2AC00F13260 /* EQNBackgroundPosition.swift */; };
65F9B4A02A8CC58200F13260 /* UpdateUserLocationTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9B49F2A8CC58200F13260 /* UpdateUserLocationTask.swift */; };
65FFDC95292F672B00EA821B /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65FFDC94292F672B00EA821B /* NotificationService.swift */; };
65FFDC99292F672B00EA821B /* EQNNotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 65FFDC92292F672B00EA821B /* EQNNotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
65FFDC9E292F682600EA821B /* Shogun in Frameworks */ = {isa = PBXBuildFile; productRef = 65FFDC9D292F682600EA821B /* Shogun */; };
@@ -311,10 +316,12 @@
6544416A25E9599000C41714 /* EQNDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNDebugViewController.swift; sourceTree = "<group>"; };
654D18C325F93C0600BB6DB0 /* PasquakesMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasquakesMapViewController.swift; sourceTree = "<group>"; };
654D18C825F93CD700BB6DB0 /* EQNMapAnnotationPastquake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNMapAnnotationPastquake.swift; sourceTree = "<group>"; };
65506C042A8F94FF00AB6448 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
6552C1452926DBA1008E723C /* AppPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPreferences.swift; sourceTree = "<group>"; };
6557CBBC26078A1700962757 /* EQNNotificationContent.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = EQNNotificationContent.entitlements; sourceTree = "<group>"; };
65583A04261B83BE00ECA9F9 /* UIKit+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKit+Extensions.swift"; sourceTree = "<group>"; };
6562C80625FFA6B100C85273 /* SeismicNetworkViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworkViewModel.swift; sourceTree = "<group>"; };
6563DAA32AAF515F0072D309 /* BackgroundTaskIdentifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskIdentifiable.swift; sourceTree = "<group>"; };
656EB9372A15FD16009DADF3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
656EB9392A15FD19009DADF3 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ar; path = ar.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
656EB93A2A15FD1B009DADF3 /* hr-HR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "hr-HR"; path = "hr-HR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
@@ -351,6 +358,9 @@
65DBFB7225E2BBF20041CBA6 /* GADTTemplateView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GADTTemplateView.m; sourceTree = "<group>"; };
65DBFB7325E2BBF20041CBA6 /* GADTMediumTemplateView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GADTMediumTemplateView.h; sourceTree = "<group>"; };
65EA58812A60360D0038EE9D /* EQNRealtimePushNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNRealtimePushNotification.swift; sourceTree = "<group>"; };
65F9B49B2A8CA22800F13260 /* BackgroundTaskManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskManager.swift; sourceTree = "<group>"; };
65F9B49D2A8CA2AC00F13260 /* EQNBackgroundPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBackgroundPosition.swift; sourceTree = "<group>"; };
65F9B49F2A8CC58200F13260 /* UpdateUserLocationTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateUserLocationTask.swift; sourceTree = "<group>"; };
65FFDC92292F672B00EA821B /* EQNNotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = EQNNotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
65FFDC94292F672B00EA821B /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
65FFDC96292F672B00EA821B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -607,6 +617,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
65506C052A8F950000AB6448 /* CoreLocation.framework in Frameworks */,
6552C13829262119008E723C /* Shogun in Frameworks */,
8C5EA22D21763103002DC156 /* MapKit.framework in Frameworks */,
8CC2B44F214AC7F8002ED1B2 /* CoreMotion.framework in Frameworks */,
@@ -679,6 +690,17 @@
path = Filters;
sourceTree = "<group>";
};
65F9B49A2A8CA21800F13260 /* Background */ = {
isa = PBXGroup;
children = (
6563DAA32AAF515F0072D309 /* BackgroundTaskIdentifiable.swift */,
65F9B49B2A8CA22800F13260 /* BackgroundTaskManager.swift */,
65F9B49F2A8CC58200F13260 /* UpdateUserLocationTask.swift */,
65F9B49D2A8CA2AC00F13260 /* EQNBackgroundPosition.swift */,
);
path = Background;
sourceTree = "<group>";
};
65FFDC93292F672B00EA821B /* EQNNotificationService */ = {
isa = PBXGroup;
children = (
@@ -851,6 +873,7 @@
8CC2B44D214AC7F8002ED1B2 /* Frameworks */ = {
isa = PBXGroup;
children = (
65506C042A8F94FF00AB6448 /* CoreLocation.framework */,
8C483CAD21FDA53B00259FD2 /* StoreKit.framework */,
8C465D9721F6539700F04673 /* Earthquake Network.xcodeproj */,
8C5EA22C21763102002DC156 /* MapKit.framework */,
@@ -1071,6 +1094,7 @@
children = (
8C4E343D215012FA008B0D2A /* EQNManager.h */,
8C4E343E215012FA008B0D2A /* EQNManager.m */,
65F9B49A2A8CA21800F13260 /* Background */,
DC3640DF257E19A00037A4B7 /* Monitoring */,
DCF9E15024F6EA0B002B6B1D /* Networks */,
DC99A50124E66DFB0071BC9F /* Commands */,
@@ -1611,6 +1635,7 @@
DCBB267E24D1EA2000F04559 /* SubscriptionProductTableViewCell.swift in Sources */,
65EA58822A60360D0038EE9D /* EQNRealtimePushNotification.swift in Sources */,
6525A82625E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift in Sources */,
65F9B49E2A8CA2AC00F13260 /* EQNBackgroundPosition.swift in Sources */,
DC99A50524E66E430071BC9F /* EQNAppearanceCommand.swift in Sources */,
65DBFB7525E2BBF20041CBA6 /* GADTMediumTemplateView.m in Sources */,
DC03BEAB250BC0A60084769B /* EQNRoundedButton.swift in Sources */,
@@ -1650,6 +1675,7 @@
6586971125F44C26009C0182 /* EQNBlurredCloseButton.swift in Sources */,
8CAFD7C521825E4A00F8BD29 /* EQNSisma.m in Sources */,
DCC23DEC24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift in Sources */,
6563DAA42AAF515F0072D309 /* BackgroundTaskIdentifiable.swift in Sources */,
8CF6604F214C0E58009F4314 /* EQNCalibrazione.m in Sources */,
65355FFF25F38D3300BB57D2 /* SegnalazioniMapViewController.swift in Sources */,
DCBB84F0252CFC4600F12633 /* AlertsNoLocationTableViewCell.swift in Sources */,
@@ -1671,6 +1697,7 @@
658BC0292859A456009EECAA /* RealtimeAlertViewController.swift in Sources */,
8CBD3DD82149B9AD0070C963 /* main.m in Sources */,
8CF05B57218C93BA0055012B /* EQNUtility.m in Sources */,
65F9B4A02A8CC58200F13260 /* UpdateUserLocationTask.swift in Sources */,
8C4E34422152B5E8008B0D2A /* EQNRilevamento.m in Sources */,
8C7A3B66225A5EA40045B266 /* NSDictionary+EQNExtensions.m in Sources */,
8CF66053214C12DC009F4314 /* EQNMath.m in Sources */,
@@ -1685,6 +1712,7 @@
8CF4F4D8216D3A110057110B /* EQNAreaCheck.m in Sources */,
8C4E34452152B707008B0D2A /* EQNAccelerometroManager.m in Sources */,
65D409942619BA34008CF356 /* SegnalazioniSendReportCell.swift in Sources */,
65F9B49C2A8CA22800F13260 /* BackgroundTaskManager.swift in Sources */,
8CBD3DC72149B9AD0070C963 /* AppDelegate.m in Sources */,
DC974AFF251748B300A139EC /* SeismicFiltersViewController.swift in Sources */,
DCB28CEE24FB8400001F557E /* SettingsViewController.swift in Sources */,
+3
View File
@@ -56,6 +56,9 @@
[EQNManager defaultManager];
[self configureFirebase];
[self configureFacebookSDKWithApplication:application andOptions:launchOptions];
// schedule background tasks
[BackgroundTaskManager.shared registerTasks];
[BackgroundTaskManager.shared scheduleUpdateServerPosition];
// add some generic logs for Crashlytics
NSString *language = [[NSLocale preferredLanguages] firstObject];
+28 -28
View File
@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.finazzi.distquake.update_server_position</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
@@ -18,16 +22,35 @@
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fb1444404982546319</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>FacebookAdvertiserIDCollectionEnabled</key>
<true/>
<key>FacebookAppID</key>
<string>1444404982546319</string>
<key>FacebookAutoLogAppEventsEnabled</key>
<true/>
<key>FacebookClientToken</key>
<string>46c7a338b2bbd2186b2f1c12865b4004</string>
<key>FacebookDisplayName</key>
<string>Earthquake Network</string>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-0053870219990922~2021960172</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
<string>fbapi</string>
<string>fb-messenger-share-api</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
@@ -43,6 +66,8 @@
<string> Ci occorre la tua posizione per inviare messaggi precisi in caso di terremoto</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>L'accesso alla libreria è richiesto per poter salvare le immagini generate dall'app</string>
<key>NSUserTrackingUsageDescription</key>
<string>Il tracciamento serve a capire se la pubblicità dell'app è efficace</string>
<key>SKAdNetworkItems</key>
<array>
<dict>
@@ -55,6 +80,7 @@
<string>audio</string>
<string>fetch</string>
<string>location</string>
<string>processing</string>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
@@ -78,31 +104,5 @@
</array>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fb1444404982546319</string>
</array>
</dict>
</array>
<key>FacebookAppID</key>
<string>1444404982546319</string>
<key>FacebookClientToken</key>
<string>46c7a338b2bbd2186b2f1c12865b4004</string>
<key>FacebookDisplayName</key>
<string>Earthquake Network</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>fbapi</string>
<string>fb-messenger-share-api</string>
</array>
<key>FacebookAutoLogAppEventsEnabled</key>
<true/>
<key>FacebookAdvertiserIDCollectionEnabled</key>
<true/>
<key>NSUserTrackingUsageDescription</key>
<string>Il tracciamento serve a capire se la pubblicità dell'app è efficace</string>
</dict>
</plist>
@@ -0,0 +1,27 @@
//
// BackgroundTaskIdentifiable.swift
// Earthquake Network
//
// Created by Andrea Busi on 11/09/23.
// Copyright © 2023 Earthquake Network. All rights reserved.
//
import Foundation
import BackgroundTasks
protocol BackgroundTaskIdentifiable {
typealias TaskCompletion = (_ success: Bool) -> Void
static var identifier: String { get }
static var interval: TimeInterval { get }
init()
func handle(_ task: BGTask, completion: @escaping TaskCompletion)
func exipration()
}
extension BackgroundTaskIdentifiable {
func exipration() { }
}
@@ -0,0 +1,81 @@
//
// BackgroundTaskManager.swift
// Earthquake Network
//
// Created by Andrea Busi on 16/08/23.
// Copyright © 2023 Earthquake Network. All rights reserved.
//
import Foundation
import BackgroundTasks
@objc
class BackgroundTaskManager: NSObject {
@objc
static let shared = BackgroundTaskManager()
private let identifiers: [BackgroundTaskIdentifiable.Type] = [UpdateUserLocationTask.self]
// MARK: - Public
@objc
func registerTasks() {
identifiers
.forEach { taskIdentifiable in
BGTaskScheduler.shared.register(forTaskWithIdentifier: taskIdentifiable.identifier, using: DispatchQueue.global()) { [weak self] task in
guard let appTask = task as? BGAppRefreshTask else { return }
self?.handleTask(appTask, with: taskIdentifiable)
self?.scheduleTaskRequest(for: taskIdentifiable)
}
}
}
// MARK: - Public
@objc
func scheduleUpdateServerPosition() {
scheduleTaskRequest(for: UpdateUserLocationTask.self)
}
// MARK: - Private
private func scheduleTaskRequest(
for taskIdentifiable: BackgroundTaskIdentifiable.Type
) {
let request = BGAppRefreshTaskRequest(identifier: taskIdentifiable.identifier)
// Fetch no earlier than X minutes from now
request.earliestBeginDate = Date(timeIntervalSinceNow: taskIdentifiable.interval)
//request.requiresNetworkConnectivity = true
do {
try BGTaskScheduler.shared.submit(request)
print("[BackgroundTaskManager] Background task scheduler submitted")
} catch {
print("[BackgroundTaskManager] Could not schedule background taksk. Error: \(error)")
}
}
private func handleTask(
_ appTask: BGAppRefreshTask,
with taskIdentifiable: BackgroundTaskIdentifiable.Type
) {
// create a task
let task = taskIdentifiable.init()
// Provide the background task with an expiration handler that cancels the operation.
appTask.expirationHandler = {
task.exipration()
}
// Handle workload
task.handle(appTask) { success in
// Inform the system that the background task is complete
// when the operation completes.
appTask.setTaskCompleted(success: success)
}
}
}
@@ -0,0 +1,46 @@
//
// EQNBackgroundPosition.swift
// Earthquake Network
//
// Created by Andrea Busi on 16/08/23.
// Copyright © 2023 Earthquake Network. All rights reserved.
//
import Foundation
import CoreLocation
struct EQNBackgroundPosition: Codable {
let date: Date
private let latitude: Double
private let longitude: Double
var coordinate: CLLocationCoordinate2D {
.init(latitude: latitude, longitude: longitude)
}
// MARK: - Class
static func save(
coordinate: CLLocationCoordinate2D
) {
var positions = load()
positions.append(.init(date: Date(), latitude: coordinate.latitude, longitude: coordinate.longitude))
if let data = try? JSONEncoder().encode(positions) {
UserDefaults.standard.set(data, forKey: "BackgroundPositions")
}
}
static func reset() {
UserDefaults.standard.removeObject(forKey: "BackgroundPositions")
}
static func load() -> [EQNBackgroundPosition] {
guard let data = UserDefaults.standard.object(forKey: "BackgroundPositions") as? Data else {
return []
}
let positions = try? JSONDecoder().decode([EQNBackgroundPosition].self, from: data)
return positions ?? []
}
}
@@ -0,0 +1,83 @@
//
// UpdateUserLocationTask.swift
// Earthquake Network
//
// Created by Andrea Busi on 16/08/23.
// Copyright © 2023 Earthquake Network. All rights reserved.
//
import Foundation
import BackgroundTasks
import CoreLocation
final class UpdateUserLocationTask: NSObject, BackgroundTaskIdentifiable {
static var identifier: String {
"com.finazzi.distquake.update_server_position"
}
static var interval: TimeInterval {
5 * 60
}
static let shared = UpdateUserLocationTask()
// MARK: - Internal
private lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
manager.allowsBackgroundLocationUpdates = true
manager.pausesLocationUpdatesAutomatically = false
return manager
}()
var appTaskCompletion: BackgroundTaskIdentifiable.TaskCompletion?
func handle(_ task: BGTask, completion: @escaping (_ success: Bool) -> Void) {
self.appTaskCompletion = completion
// ricaviamo la posizione corrente dell'utente
if let location = locationManager.location {
complete(with: location)
} else {
locationManager.requestLocation()
}
}
func exipration() {
locationManager.stopUpdatingLocation()
failed()
}
// MARK: - Private
private func complete(with location: CLLocation) {
EQNBackgroundPosition.save(coordinate: location.coordinate)
appTaskCompletion?(true)
}
private func failed() {
appTaskCompletion?(false)
}
}
extension UpdateUserLocationTask: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
complete(with: location)
} else {
failed()
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("[UpdateUserLocationTask] Location manager failed. Error: \(error.localizedDescription)")
// nope, but mandatory
failed()
}
}