Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions LoopFollow.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
DD493AE72ACF23CF009A6922 /* DeviceStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD493AE62ACF23CF009A6922 /* DeviceStatus.swift */; };
DD493AE92ACF2445009A6922 /* BGData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD493AE82ACF2445009A6922 /* BGData.swift */; };
DD608A082C1F584900F91132 /* DeviceStatusLoop.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD608A072C1F584900F91132 /* DeviceStatusLoop.swift */; };
DD608A0C2C27415C00F91132 /* BackgroundAlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD608A0B2C27415C00F91132 /* BackgroundAlertManager.swift */; };
DD6A935E2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6A935D2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift */; };
DD7E19842ACDA50C00DBD158 /* Overrides.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19832ACDA50C00DBD158 /* Overrides.swift */; };
DD7E19862ACDA59700DBD158 /* BGCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19852ACDA59700DBD158 /* BGCheck.swift */; };
Expand Down Expand Up @@ -214,6 +215,7 @@
DD493AE62ACF23CF009A6922 /* DeviceStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatus.swift; sourceTree = "<group>"; };
DD493AE82ACF2445009A6922 /* BGData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGData.swift; sourceTree = "<group>"; };
DD608A072C1F584900F91132 /* DeviceStatusLoop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatusLoop.swift; sourceTree = "<group>"; };
DD608A0B2C27415C00F91132 /* BackgroundAlertManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundAlertManager.swift; sourceTree = "<group>"; };
DD6A935D2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatusOpenAPS.swift; sourceTree = "<group>"; };
DD7E19832ACDA50C00DBD158 /* Overrides.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Overrides.swift; sourceTree = "<group>"; };
DD7E19852ACDA59700DBD158 /* BGCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGCheck.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -501,6 +503,7 @@
FC1BDD2A24A22650001B652C /* Stats.swift */,
FC1BDD2C24A23204001B652C /* StatsView.swift */,
FCA2DDE52501095000254A8C /* Timers.swift */,
DD608A0B2C27415C00F91132 /* BackgroundAlertManager.swift */,
);
path = Controllers;
sourceTree = "<group>";
Expand Down Expand Up @@ -1025,6 +1028,7 @@
FCC0FAC224922A22003E610E /* DictionaryKeyPath.swift in Sources */,
DD493AD72ACF2139009A6922 /* SuspendPump.swift in Sources */,
FC9788182485969B00A7906C /* AppDelegate.swift in Sources */,
DD608A0C2C27415C00F91132 /* BackgroundAlertManager.swift in Sources */,
DD493AD92ACF2171009A6922 /* Carbs.swift in Sources */,
DD493AE92ACF2445009A6922 /* BGData.swift in Sources */,
FCC6886B24898FD800A0279D /* ObservationToken.swift in Sources */,
Expand Down
34 changes: 22 additions & 12 deletions LoopFollow/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?
let notificationCenter = UNUserNotificationCenter.current()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.


let options: UNAuthorizationOptions = [.alert, .sound, .badge]
notificationCenter.requestAuthorization(options: options) {
(didAllow, error) in
Expand All @@ -37,11 +36,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}

let action = UNNotificationAction(identifier: "OPEN_APP_ACTION", title: "Open App", options: .foreground)
let category = UNNotificationCategory(identifier: "loopfollow.background.alert", actions: [action], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])

UNUserNotificationCenter.current().delegate = self
return true
}

return true
}

func applicationWillTerminate(_ application: UIApplication) {
if UserDefaultsRepository.alertAppInactive.value {
AlarmSound.setSoundFile(str: "Alarm_Buzzer")
Expand All @@ -53,7 +56,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {


// This application should be called in background every X Minutes
UIApplication.shared.setMinimumBackgroundFetchInterval(
TimeInterval(UserDefaultsRepository.backgroundRefreshFrequency.value * 60)
Expand Down Expand Up @@ -91,13 +93,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
*/
let container = NSPersistentCloudKitContainer(name: "LoopFollow")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
Expand Down Expand Up @@ -128,13 +130,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
if response.actionIdentifier == "OPEN_APP_ACTION" {
if let window = window {
window.rootViewController?.dismiss(animated: true, completion: nil)
window.rootViewController?.present(MainViewController(), animated: true, completion: nil)
}
}
completionHandler()
}
}

extension AppDelegate: UNUserNotificationCenterDelegate {

func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
{
completionHandler(.alert)
}
Expand Down
52 changes: 52 additions & 0 deletions LoopFollow/Controllers/BackgroundAlertManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// BackgroundAlertManager.swift
// LoopFollow
//
// Created by Jonas Björkert on 2024-06-22.
// Copyright © 2024 Jon Fawcett. All rights reserved.
//

import Foundation
import UserNotifications

class BackgroundAlertManager {
static let shared = BackgroundAlertManager()

private init() {}

private var isAlertScheduled: Bool = false

func startBackgroundAlert() {
isAlertScheduled = true
scheduleBackgroundAlert()
}

func stopBackgroundAlert() {
isAlertScheduled = false
cancelBackgroundAlert()
}

func scheduleBackgroundAlert() {
guard isAlertScheduled, UserDefaultsRepository.backgroundRefresh.value else { return }

let content = UNMutableNotificationContent()
content.title = "LoopFollow Background Refresh"
content.body = "The app is not active, open the app to resume."
content.sound = .defaultCritical
content.categoryIdentifier = "loopfollow.background.alert"

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 360, repeats: false)

let request = UNNotificationRequest(identifier: "loopfollow.background.alert", content: content, trigger: trigger)

UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error scheduling background alert: \(error)")
}
}
}

private func cancelBackgroundAlert() {
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["loopfollow.background.alert"])
}
}
3 changes: 1 addition & 2 deletions LoopFollow/Controllers/Timers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ extension MainViewController {
}

@objc func bgTimerDidEnd(_ timer:Timer) {

// reset timer to 1 minute if settings aren't entered
if UserDefaultsRepository.shareUserName.value == "" && UserDefaultsRepository.sharePassword.value == "" && UserDefaultsRepository.url.value == "" {
startBGTimer(time: 60)
Expand All @@ -155,7 +154,7 @@ extension MainViewController {
} else {
webLoadNSBGData()
}

BackgroundAlertManager.shared.scheduleBackgroundAlert()
}

// Device Status Timer
Expand Down
2 changes: 2 additions & 0 deletions LoopFollow/ViewControllers/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
// Cancel the current timer and start a fresh background timer using the settings value only if background task is enabled

if UserDefaultsRepository.backgroundRefresh.value {
BackgroundAlertManager.shared.startBackgroundAlert()
backgroundTask.startBackgroundTask()
}

Expand All @@ -444,6 +445,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
// Cancel the background tasks, start a fresh timer
if UserDefaultsRepository.backgroundRefresh.value {
backgroundTask.stopBackgroundTask()
BackgroundAlertManager.shared.stopBackgroundAlert()
}

restartAllTimers()
Expand Down