Reimplemented background task for GPS fix sending

This commit is contained in:
Raimund Wege committed 2016-07-21 16:28:16 +02:00
1 parent a0dee0a2b5
commit 980dd6455b
9 files changed
+163 -126

No files matched your search

@@ -308,6 +308,7 @@
7FED45CC1A25F5910022505F /* leaderboard1.json in Resources */ = {isa = PBXBuildFile; fileRef = 7FED45C61A25F5900022505F /* leaderboard1.json */; };
9E95C3D61C723AB30016632B /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E95C3D51C723AB30016632B /* Constants.swift */; };
9ECBDDAE1C9C4F0300F76AEB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9ECBDDAD1C9C4F0300F76AEB /* LaunchScreen.storyboard */; };
C84936181D40D295000F30E8 /* GPSFixController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84936171D40D295000F30E8 /* GPSFixController.swift */; };
C852BAA21CF5E1EA0093B0C5 /* RequestManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C852BAA11CF5E1EA0093B0C5 /* RequestManager.swift */; };
C852BAAB1CF736680093B0C5 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = C852BAAA1CF736680093B0C5 /* Preferences.swift */; };
C8603D511C52892A002B2F28 /* Acknowledgements.markdown in Resources */ = {isa = PBXBuildFile; fileRef = C8603D491C528832002B2F28 /* Acknowledgements.markdown */; };
@@ -701,6 +702,7 @@
9E95C3D51C723AB30016632B /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
9ECBDDAD1C9C4F0300F76AEB /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
B1229BB88872CD20C0AE25DE /* libPods-SAPTracker.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SAPTracker.a"; sourceTree = BUILT_PRODUCTS_DIR; };
C84936171D40D295000F30E8 /* GPSFixController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GPSFixController.swift; sourceTree = "<group>"; };
C852BAA11CF5E1EA0093B0C5 /* RequestManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestManager.swift; sourceTree = "<group>"; };
C852BAAA1CF736680093B0C5 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
C8603D491C528832002B2F28 /* Acknowledgements.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Acknowledgements.markdown; sourceTree = "<group>"; };
@@ -803,10 +805,10 @@
7F06590219F7FEA40077FF95 /* DataController */ = {
isa = PBXGroup;
children = (
C8739E0E1D2BEE7900A89F3C /* RegattaController.swift */,
7F23F0871A1A3DEF00F03F1C /* CheckInController.swift */,
C84936171D40D295000F30E8 /* GPSFixController.swift */,
C8739E0E1D2BEE7900A89F3C /* RegattaController.swift */,
C8739E101D2BF13200A89F3C /* Data */,
C852BAA61CF5E1F40093B0C5 /* CheckIn */,
);
name = DataController;
sourceTree = "<group>";
@@ -1170,13 +1172,6 @@
name = Pods;
sourceTree = "<group>";
};
C852BAA61CF5E1F40093B0C5 /* CheckIn */ = {
isa = PBXGroup;
children = (
);
name = CheckIn;
sourceTree = "<group>";
};
C852BAA91CF736440093B0C5 /* Preferences */ = {
isa = PBXGroup;
children = (
@@ -1877,6 +1872,7 @@
C89C5FCA1D06CECD002AAAD2 /* StatusTrackingTableViewCell.swift in Sources */,
C8739E051D2AA2A500A89F3C /* Regatta.swift in Sources */,
7F8B8D3F1A37356C00C34B88 /* TrackingPageViewController.swift in Sources */,
C84936181D40D295000F30E8 /* GPSFixController.swift in Sources */,
7F99DE3C1A126554009B3619 /* RegattaViewController.swift in Sources */,
7F8CC2081A10D47A00C56D7F /* SettingsViewController.swift in Sources */,
C8739E0D1D2AA2A500A89F3C /* Event.swift in Sources */,
@@ -12,10 +12,10 @@ import AVFoundation
@objc protocol CheckInControllerDelegate {
func showCheckInAlert(checkInController: CheckInController, alertController: UIAlertController)
func showCheckInAlert(sender: CheckInController, alertController: UIAlertController)
optional func checkInDidStart(checkInController: CheckInController)
optional func checkInDidEnd(checkInController: CheckInController, withSuccess succeed: Bool)
optional func checkInDidStart(sender: CheckInController)
optional func checkInDidEnd(sender: CheckInController, withSuccess succeed: Bool)
}
@@ -71,30 +71,15 @@ class CheckInController : NSObject {
}
private func postCheckInSuccess(regattaData: RegattaData) {
// Regatta
let regatta = CoreDataManager.sharedManager.fetchRegatta(regattaData.eventID,
leaderboardName: regattaData.leaderboardName,
competitorID: regattaData.competitorID
) ?? CoreDataManager.sharedManager.newRegatta()
let regatta = CoreDataManager.sharedManager.fetchRegatta(regattaData) ?? CoreDataManager.sharedManager.newRegatta()
regatta.updateWirhRegattaData(regattaData)
// Event
let event = regatta.event ?? CoreDataManager.sharedManager.newEvent(regatta)
event.updateWithEventData(regattaData.eventData)
// Leaderboard
let leaderboard = regatta.leaderboard ?? CoreDataManager.sharedManager.newLeaderboard(regatta)
leaderboard.updateWithLeaderboardData(regattaData.leaderboardData)
// Competitor
let competitor = regatta.competitor ?? CoreDataManager.sharedManager.newCompetitor(regatta)
competitor.updateWithCompetitorData(regattaData.competitorData)
// Save
CoreDataManager.sharedManager.saveContext()
// Check-in completed
checkInDidEnd(withSuccess: true)
}
@@ -20,16 +20,13 @@ public class CoreDataManager: NSObject {
// MARK: - Fetch
func fetchRegatta(eventID: String,
leaderboardName: String,
competitorID: String) -> Regatta?
{
func fetchRegatta(regattaData: RegattaData) -> Regatta? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName("Regatta", inManagedObjectContext: managedObjectContext!)
fetchRequest.predicate = NSPredicate(format: "event.eventID = %@ AND leaderboard.name = %@ AND competitor.competitorID = %@",
eventID,
leaderboardName,
competitorID)
regattaData.eventID,
regattaData.leaderboardName,
regattaData.competitorID)
do {
let results = try managedObjectContext!.executeFetchRequest(fetchRequest)
if results.count == 0 {
@@ -81,16 +78,13 @@ public class CoreDataManager: NSObject {
// MARK: - Delete
func deleteRegatta(regatta: Regatta) {
managedObjectContext!.deleteObject(regatta)
func deleteObject(object: AnyObject?) {
guard let o = object as? NSManagedObject else { return }
managedObjectContext?.deleteObject(o)
}
func deleteGPSFixes(gpsFixes: [GPSFix]) {
gpsFixes.forEach { (gpsFix) in deleteGPSFix(gpsFix) }
}
func deleteGPSFix(gpsFix: GPSFix) {
managedObjectContext!.deleteObject(gpsFix)
func deleteObjects(objects: NSSet?) {
objects?.forEach { (o) in deleteObject(o) }
}
// MARK: - Save
@@ -0,0 +1,73 @@
//
// GPSFixController.swift
// SAPTracker
//
// Created by Raimund Wege on 21.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
import UIKit
class GPSFixController: NSObject {
let regatta: Regatta
init(regatta: Regatta) {
self.regatta = regatta
}
// MARK: - Methods
func sendGPSFixes(completion: () -> Void) {
guard let gpsFixes = regatta.gpsFixes as? Set<GPSFix> else {
log("Can't get GPS fixes")
completion()
return
}
guard gpsFixes.count > 0 else {
log("No GPS fixes available")
completion()
return
}
sendGPSFixes(gpsFixes, completion: completion)
}
func sendGPSFixes(gpsFixes: Set<GPSFix>, completion: () -> Void) {
log("\(gpsFixes.count) GPS fixes will be sent")
requestManager.postGPSFixes(gpsFixes,
success: { (operation, responseObject) in self.sendGPSFixesSuccess(gpsFixes, completion: completion) },
failure: { (operation, error) in self.sendGPSFixesFailure(error, completion: completion) }
)
}
private func sendGPSFixesSuccess(gpsFixes: Set<GPSFix>, completion: () -> Void) {
dispatch_async(dispatch_get_main_queue(), {
self.log("Sending \(gpsFixes.count) GPS fixes was successful")
CoreDataManager.sharedManager.deleteObjects(gpsFixes)
CoreDataManager.sharedManager.saveContext()
self.log("\(gpsFixes.count) GPS fixes deleted")
completion()
})
}
private func sendGPSFixesFailure(error: AnyObject, completion: () -> Void) {
dispatch_async(dispatch_get_main_queue(), {
self.log("Sending GPS fixes failed for reason: \(error)")
completion()
})
}
// MARK: - Properties
lazy var requestManager: RequestManager = {
let requestManager = RequestManager(baseURLString: self.regatta.serverURL)
return requestManager
}()
// MARK: - Helper
private func log(message: String) {
print("[GPSFixController] \(message)")
}
}
@@ -264,11 +264,11 @@ extension HomeViewController: NSFetchedResultsControllerDelegate {
extension HomeViewController: CheckInControllerDelegate {
func showCheckInAlert(checkInController: CheckInController, alertController: UIAlertController) {
func showCheckInAlert(sender: CheckInController, alertController: UIAlertController) {
presentViewController(alertController, animated: true, completion: nil)
}
func checkInDidEnd(checkInController: CheckInController, withSuccess succeed: Bool) {
func checkInDidEnd(sender: CheckInController, withSuccess succeed: Bool) {
Preferences.newCheckInURL = nil
}
@@ -10,12 +10,8 @@ import UIKit
@objc protocol RegattaControllerDelegate {
func showRegattaAlert(regattaController: RegattaController, alertController: UIAlertController)
optional func regattaControllerDidUpdate(regattaController: RegattaController)
// optional func checkInDidStart(checkInController: CheckInController)
// optional func checkInDidEnd(checkInController: CheckInController, withSuccess succeed: Bool)
// optional func regattaControllerDidStartTracking(sender: RegattaController)
// optional func regattaControllerDidStopTracking(sender: RegattaController)
}
@@ -30,7 +26,7 @@ class RegattaController: NSObject {
}
enum Mode: String {
case BatterySaving = "Battery Saving"
case BatterySaving = "BatterySaving"
case Error = "Error"
case Online = "Online"
case Offline = "Offline"
@@ -52,12 +48,15 @@ class RegattaController: NSObject {
let regatta: Regatta
var delegate: RegattaControllerDelegate?
var sendingBackgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
var sendingDate: NSDate = NSDate()
private (set) var isTracking: Bool = false
init(regatta: Regatta) {
self.regatta = regatta
super.init()
subscribeForNotifications()
}
// MARK: - Notifications
@@ -82,9 +81,31 @@ class RegattaController: NSObject {
let gpsFix = CoreDataManager.sharedManager.newGPSFix(self.regatta)
gpsFix.updateWithLocationData(locationData)
CoreDataManager.sharedManager.saveContext()
self.beginGPSFixSendingInBackgroundTask()
})
}
// MARK: - Background Task
private func beginGPSFixSendingInBackgroundTask() {
if sendingDate.compare(NSDate()) == .OrderedAscending {
sendingBackgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithName("Send GPS Fixes", expirationHandler: {
self.endGPSFixSendingInBackgroundTask()
})
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
self.sendingDate = NSDate().dateByAddingTimeInterval(self.sendingPeriod)
self.gpsFixController.sendGPSFixes({
self.endGPSFixSendingInBackgroundTask()
})
})
}
}
private func endGPSFixSendingInBackgroundTask() {
UIApplication.sharedApplication().endBackgroundTask(sendingBackgroundTask)
sendingBackgroundTask = UIBackgroundTaskInvalid;
}
// MARK: - Update
func update() {
@@ -119,84 +140,31 @@ class RegattaController: NSObject {
// MARK: - Methods
func startTracking() -> Bool {
func startTracking() throws {
isTracking = false
do {
try LocationManager.sharedManager.startTracking()
isTracking = true
} catch let error as LocationManager.LocationManagerError {
let alertController = UIAlertController(title: error.description, message: nil, preferredStyle: .Alert)
let cancelTitle = NSLocalizedString("Cancel", comment: "")
let cancelAction = UIAlertAction(title: cancelTitle, style: .Cancel, handler: nil)
alertController.addAction(cancelAction)
delegate?.showRegattaAlert(self, alertController: alertController)
} catch {
print("Unknown error")
}
try LocationManager.sharedManager.startTracking()
isTracking = true
sendGPSFixes()
return isTracking
}
func stopTracking() {
isTracking = false
LocationManager.sharedManager.stopTracking()
}
func sendGPSFixes() {
var gpsFixes = [GPSFix]()
if regatta.gpsFixes != nil {
regatta.gpsFixes!.forEach { (gpsFix) in gpsFixes.append(gpsFix as! GPSFix) }
}
if gpsFixes.count > 0 {
print("\(gpsFixes.count) GPS fixes will be sent")
requestManager.postGPSFixes(gpsFixes,
success: { (operation, responseObject) in self.sendGPSFixesSuccess(gpsFixes) },
failure: { (operation, error) in self.sendGPSFixesFailure(error) })
} else {
print("No GPS fixes available")
sendGPSFixesFinished()
}
}
private func sendGPSFixesSuccess(gpsFixes: [GPSFix]!) {
print(NSThread.isMainThread)
dispatch_async(dispatch_get_main_queue(), {
print("sending \(gpsFixes.count) GPS fixes was successful")
CoreDataManager.sharedManager.deleteGPSFixes(gpsFixes)
CoreDataManager.sharedManager.saveContext()
self.sendGPSFixesFinished()
})
}
private func sendGPSFixesFailure(error: AnyObject) {
print(NSThread.isMainThread)
dispatch_async(dispatch_get_main_queue(), {
print("sending GPS fixes failed for reason: \(error)")
self.sendGPSFixesFinished()
})
}
private func sendGPSFixesFinished() {
if isTracking {
let delay = sendingPeriod
print("sending next GPS fixes in \(delay) seconds")
performSelector(#selector(RegattaController.sendGPSFixes), withObject: nil, afterDelay: delay)
} else {
print("sending GPS fixes stopped")
}
}
// MARK: - Helper
var sendingPeriod: NSTimeInterval { get { return BatteryManager.sharedManager.batterySaving ? SendingInterval.BatterySaving : SendingInterval.Normal } }
// MARK: - Properties
lazy var gpsFixController: GPSFixController = {
let gpsFixController = GPSFixController(regatta: self.regatta)
return gpsFixController
}()
lazy var requestManager: RequestManager = {
let requestManager = RequestManager(baseURLString: self.regatta.serverURL)
return requestManager
}()
// MARK: - Helper
var sendingPeriod: NSTimeInterval { get { return BatteryManager.sharedManager.batterySaving ? SendingInterval.BatterySaving : SendingInterval.Normal } }
}
@@ -50,7 +50,7 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
setupImageSourceTypes()
setupTeamImage()
// Test
// Update
regattaController.update()
}
@@ -212,8 +212,17 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
}
@IBAction func startTrackingButtonTapped(sender: AnyObject) {
if regattaController.startTracking() {
do {
try regattaController.startTracking()
performSegueWithIdentifier("Tracking", sender: sender)
} catch let error as LocationManager.LocationManagerError {
let alertController = UIAlertController(title: error.description, message: nil, preferredStyle: .Alert)
let cancelTitle = NSLocalizedString("Cancel", comment: "")
let cancelAction = UIAlertAction(title: cancelTitle, style: .Cancel, handler: nil)
alertController.addAction(cancelAction)
presentViewController(alertController, animated: true, completion: nil)
} catch {
print("Unknown error")
}
}
@@ -237,7 +246,7 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
success: { (operation, responseObject) in },
failure: { (operation, error) in
})
CoreDataManager.sharedManager.deleteRegatta(regatta)
CoreDataManager.sharedManager.deleteObject(regatta)
CoreDataManager.sharedManager.saveContext()
navigationController!.popViewControllerAnimated(true)
}
@@ -354,8 +363,20 @@ extension RegattaViewController: UIImagePickerControllerDelegate {
extension RegattaViewController: RegattaControllerDelegate {
func showRegattaAlert(regattaController: RegattaController, alertController: UIAlertController) {
presentViewController(alertController, animated: true, completion: nil)
// func showRegattaAlert(sender: RegattaController, alertController: UIAlertController) {
// presentViewController(alertController, animated: true, completion: nil)
// }
// func regattaControllerDidUpdate(sender: RegattaController) {
// // FIXME: Update UI
// }
func regattaControllerDidStartTracking(sender: RegattaController) {
}
func regattaControllerDidStopTracking(sender: RegattaController) {
}
}
@@ -254,7 +254,7 @@ class RequestManager: NSObject {
manager.POST(urlString, parameters: body, success: success, failure: failure)
}
func postGPSFixes(gpsFixes: [GPSFix]!,
func postGPSFixes(gpsFixes: Set<GPSFix>,
success: (AFHTTPRequestOperation!, AnyObject!) -> Void,
failure: (AFHTTPRequestOperation!, AnyObject!) -> Void)
{
@@ -116,11 +116,11 @@ class ScanViewController: UIViewController {
extension ScanViewController: CheckInControllerDelegate {
func showCheckInAlert(checkInController: CheckInController, alertController: UIAlertController) {
func showCheckInAlert(sender: CheckInController, alertController: UIAlertController) {
presentViewController(alertController, animated: true, completion: nil)
}
func checkInDidEnd(checkInController: CheckInController, withSuccess succeed: Bool) {
func checkInDidEnd(sender: CheckInController, withSuccess succeed: Bool) {
if succeed {
navigationController?.popViewControllerAnimated(true)
} else {