ApplicationManager : allows to add closures that need to be run when app comes to background, foreground or terminates. Example when app comes to foreground : stop playing sound

This commit is contained in:
Johan Degraeve 2019-05-25 18:18:45 +02:00
parent b1f18c1c1d
commit fc0b025423
8 changed files with 120 additions and 59 deletions

View File

@ -31,6 +31,7 @@
F81FA006228E09D40028C70F /* TextsCalibration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F81FA005228E09D40028C70F /* TextsCalibration.swift */; };
F81FA008228F52450028C70F /* HomeView.strings in Resources */ = {isa = PBXBuildFile; fileRef = F81FA007228F52450028C70F /* HomeView.strings */; };
F81FA00A228F53680028C70F /* TextsHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F81FA009228F53680028C70F /* TextsHomeView.swift */; };
F81FA00C2298A0190028C70F /* ApplicationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F81FA00B2298A0170028C70F /* ApplicationManager.swift */; };
F85DC2ED21CFE2F500B9F74A /* BgReading+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85DC2E721CFE2F500B9F74A /* BgReading+CoreDataProperties.swift */; };
F85DC2EF21CFE2F500B9F74A /* Sensor+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85DC2E921CFE2F500B9F74A /* Sensor+CoreDataProperties.swift */; };
F85DC2F321CFE3D400B9F74A /* Calibration+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85DC2F021CFE3D400B9F74A /* Calibration+CoreDataClass.swift */; };
@ -170,6 +171,7 @@
F81FA005228E09D40028C70F /* TextsCalibration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextsCalibration.swift; sourceTree = "<group>"; };
F81FA007228F52450028C70F /* HomeView.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = HomeView.strings; sourceTree = "<group>"; };
F81FA009228F53680028C70F /* TextsHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextsHomeView.swift; sourceTree = "<group>"; };
F81FA00B2298A0170028C70F /* ApplicationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationManager.swift; sourceTree = "<group>"; };
F85DC2E721CFE2F500B9F74A /* BgReading+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "BgReading+CoreDataProperties.swift"; path = "../Extensions/BgReading+CoreDataProperties.swift"; sourceTree = "<group>"; };
F85DC2E921CFE2F500B9F74A /* Sensor+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Sensor+CoreDataProperties.swift"; path = "../Extensions/Sensor+CoreDataProperties.swift"; sourceTree = "<group>"; };
F85DC2F021CFE3D400B9F74A /* Calibration+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Calibration+CoreDataClass.swift"; sourceTree = "<group>"; };
@ -423,6 +425,7 @@
children = (
F8B3A7A82265254D004BA588 /* Alerts */,
F85DC2F721D2632F00B9F74A /* CoreDataManager.swift */,
F81FA00B2298A0170028C70F /* ApplicationManager.swift */,
);
path = Managers;
sourceTree = "<group>";
@ -1046,6 +1049,7 @@
F81F9FFC2288C7530028C70F /* NewAlertSettingsViewController.swift in Sources */,
F81FA0002289E4990028C70F /* AlertSettingsViewControllerData.swift in Sources */,
F8B3A834227F08AC004BA588 /* PickerViewData.swift in Sources */,
F81FA00C2298A0190028C70F /* ApplicationManager.swift in Sources */,
F8025C0F21D95EC200ECF0C0 /* CGMTransmitterDelegate.swift in Sources */,
F8B3A79522635A25004BA588 /* AlertType+CoreDataProperties.swift in Sources */,
F8B3A84C227F090E004BA588 /* SettingsViewController.swift in Sources */,

View File

@ -11,7 +11,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: - Application Life Cycle
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
@ -24,16 +23,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
/// set appInForeGround to true
UserDefaults.standard.appInForeGround = false
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
/// set appInForeGround to true
UserDefaults.standard.appInForeGround = true
}
func applicationDidBecomeActive(_ application: UIApplication) {
@ -45,6 +38,5 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Saves changes in the application's managed object context before the application terminates.
}
}

View File

@ -67,9 +67,6 @@ extension UserDefaults {
/// Transmitter Battery Level
case transmitterBatteryInfo = "transmitterbatteryinfo"
// Application
// is app in foreground or not
case appInForeGround = "appInForeGround"
}
// MARK: - ===== User Configurable Settings ======
@ -405,17 +402,6 @@ extension UserDefaults {
}
}
}
/// is app in foreground or not
@objc dynamic var appInForeGround: Bool {
get {
return bool(forKey: Key.appInForeGround.rawValue)
}
set {
set(newValue, forKey: Key.appInForeGround.rawValue)
}
}
}

View File

@ -38,23 +38,26 @@ public class AlertManager:NSObject {
/// playSound instance
private var soundPlayer:SoundPlayer?
// snooze parameters
/// snooze parameters
private var snoozeParameters = [Int: SnoozeParameters]()
// helper array with all alert notification identifiers
/// helper array with all alert notification identifiers
private var alertNotificationIdentifers = [String]()
// permanent reference to notificationcenter
/// permanent reference to notificationcenter
private let uNUserNotificationCenter:UNUserNotificationCenter
// snooze times in minutes
/// snooze times in minutes
private let snoozeValueMinutes = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 75, 90, 120, 150, 180, 240, 300, 360, 420, 480, 540, 600, 1440, 10080]
// snooze times as shown to the user, actual strings will be replaced during init
/// snooze times as shown to the user, actual strings will be replaced during init
private var snoozeValueStrings = ["5 minutes", "10 minutes", "15 minutes", "20 minutes", "25 minutes", "30 minutes", "35 minutes",
"40 minutes", "45 minutes", "50 minutes", "55 minutes", "1 hour", "1 hour 15 minutes", "1,5 hours", "2 hours", "2,5 hours", "3 hours", "4 hours",
"5 hours", "6 hours", "7 hours", "8 hours", "9 hours", "10 hours", "1 day", "1 week"]
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground - for closure that will stop playing sound
private let applicationManagerKeyStopPlayingSound = "alertmanagerstopplayingsound"
// MARK: - initializer
init(coreDataManager:CoreDataManager, soundPlayer:SoundPlayer?) {
@ -83,12 +86,17 @@ public class AlertManager:NSObject {
// initialize array of alertNotifications
initAlertNotificationIdentiferArray()
// need to set the alert notification categories, get the existing ones first
// need to set the alert notification categories, get the existing categories first, call setAlertNotificationCategories in competionhandler
UNUserNotificationCenter.current().getNotificationCategories(completionHandler: setAlertNotificationCategories(_:))
// observe changes to app status, foreground or background
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.appInForeGround.rawValue, options: .new, context: nil)
// alertManager may have raised an alert with a sound played by soundplayer. If user brings the app to the foreground, the soundPlayer needs to stop playing
ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground(key: applicationManagerKeyStopPlayingSound, closure: {
if let soundPlayer = soundPlayer {
soundPlayer.stopPlaying()
}
})
}
// MARK: - public functions
@ -240,27 +248,6 @@ public class AlertManager:NSObject {
return returnValue
}
// MARK: - overriden functions
// interested in changes to some of the settings
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let keyPath = keyPath {
if let keyPathEnum = UserDefaults.Key(rawValue: keyPath) {
switch keyPathEnum {
case .appInForeGround:
// app comes to foreground, if app would still be playing sound, then it doesn't make sense to continue playing, it would mean that app was playing while in the background, most probably for an alert, user opens the app, there's no need to continue alerting the user
if let soundPlayer = self.soundPlayer {
soundPlayer.stopPlaying()
}
default:
break
}
}
}
}
// MARK: - private helper functions
private func createPickerViewData(forAlertKind alertKind:AlertKind) -> PickerViewData {

View File

@ -0,0 +1,90 @@
import UIKit
/// class defines methods to allow running of closures when app comes to foreground or to background
class ApplicationManager {
// MARK: - private properties
/// list of closures to run when app enters background
private var closuresToRunWhenAppDidEnterBackground = [String : (() -> ())]()
/// list of closures to run when app enters background
private var closuresToRunWhenAppWillEnterForeground = [String : (() -> ())]()
/// list of closures to run when app will terminate
private var closuresToRunWhenAppWillTerminate = [String : (() -> ())]()
/// access to shared instance of ApplicationManager
static let shared = ApplicationManager()
// MARK: - initializer
/// init is private, to avoid creation
private init() {
// setup notification handling
setupNotificationHandling()
}
private func setupNotificationHandling() {
/// listen for required notifications
let notificationCenter = NotificationCenter.default
/// did enter background
notificationCenter.addObserver(self, selector: #selector(runWhenAppDidEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
/// will enter foreground
notificationCenter.addObserver(self, selector: #selector(runWhenAppWillEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
/// will terminate
notificationCenter.addObserver(self, selector: #selector(runWhenAppWillTerminate(_:)), name: UIApplication.willTerminateNotification, object: nil)
}
// MARK: - public functions
/// adds closure to run identified by key, when app moved to background
///
/// closures are stored in a dictionary, key is the identifier
func addClosureToRunWhenAppDidEnterBackground(key:String, closure:@escaping () -> ()) {
closuresToRunWhenAppDidEnterBackground[key] = closure
}
/// adds closure to run identified by key, when app moved to foreground
///
/// closures are stored in a dictionary, key is the identifier
func addClosureToRunWhenAppWillEnterForeground(key:String, closure:@escaping () -> ()) {
closuresToRunWhenAppWillEnterForeground[key] = closure
}
/// adds closure to run identified by key, when app will terminate
///
/// closures are stored in a dictionary, key is the identifier
func addClosureToRunWhenAppWillTerminate(key:String, closure:@escaping () -> ()) {
closuresToRunWhenAppWillTerminate[key] = closure
}
// MARK: - private helper functions
@objc private func runWhenAppDidEnterBackground(_ : Notification) {
// run the closures
for closure in closuresToRunWhenAppDidEnterBackground {
closure.value()
}
}
@objc private func runWhenAppWillEnterForeground(_ : Notification) {
// run the closures
for closure in closuresToRunWhenAppWillEnterForeground {
closure.value()
}
}
@objc private func runWhenAppWillTerminate(_ : Notification) {
// run the closures
for closure in closuresToRunWhenAppWillTerminate {
closure.value()
}
}
}

View File

@ -15,6 +15,10 @@ final class CoreDataManager {
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryCoreDataManager)
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground - for closure that will stop playing sound
private let applicationManagerKeySaveChanges = "coredatamanagersavechanges"
// MARK: -
private let completion: CoreDataManagerCompletion
@ -96,12 +100,6 @@ final class CoreDataManager {
setupCoreDataStack()
}
// MARK: - Notification Handling
@objc func saveChanges(_ notification: Notification) {
saveChanges()
}
// MARK: - Helper Methods
private func setupCoreDataStack() {
@ -117,6 +115,9 @@ final class CoreDataManager {
// Invoke Completion On Main Queue
DispatchQueue.main.async { self.completion() }
}
// when app terminates, call saveChanges, just in case that somewhere in the code
ApplicationManager.shared.addClosureToRunWhenAppWillTerminate(key: applicationManagerKeySaveChanges, closure: {self.saveChanges()})
}
// MARK: -

View File

@ -6,6 +6,7 @@ import Foundation
///
/// where this needs to be checked, create an instance of KeyValueObserverTimeKeeper, example in NightScoutUploader
class KeyValueObserverTimeKeeper {
private var keyObserverKeeper = [String: Date]()
/// call this to check if last observe of key was at least withMinimumDelayMilliSeconds ago

View File

@ -673,7 +673,7 @@ final class RootViewController: UIViewController {
}
// add transmitterBatteryInfo if known
if let transmitterBatteryInfo = UserDefaults.standard.transmitterBatteryInfo, let transmitterType = UserDefaults.standard.transmitterType {
if let transmitterBatteryInfo = UserDefaults.standard.transmitterBatteryInfo {
textToShow += Texts_HomeView.transmitterBatteryLevel + " : " + transmitterBatteryInfo.description
// add 1 newline with last connection timestamp
textToShow += "\r\n\r\n"