Jonas Björkert před 3 roky
rodič
revize
bd20e5c75c

+ 4 - 0
LoopFollow.xcodeproj/project.pbxproj

@@ -10,6 +10,7 @@
 		3F1335F351590E573D8E6962 /* Pods_LoopFollow.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7D55B42A22051DAD69E89D0 /* Pods_LoopFollow.framework */; };
 		DD07B5C929E2F9C400C6A635 /* NightscoutUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD07B5C829E2F9C400C6A635 /* NightscoutUtils.swift */; };
 		DD152D3B24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD152D3A24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift */; };
+		DD7FFAFD2A72953000C3A304 /* EKEventStore+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7FFAFC2A72953000C3A304 /* EKEventStore+Extensions.swift */; };
 		DD98F54424BCEFEE0007425A /* ShareClientExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */; };
 		DDCF979424C0D380002C9752 /* UIViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979324C0D380002C9752 /* UIViewExtension.swift */; };
 		DDCF979624C1443C002C9752 /* GeneralSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979524C1443C002C9752 /* GeneralSettingsViewController.swift */; };
@@ -177,6 +178,7 @@
 		A7D55B42A22051DAD69E89D0 /* Pods_LoopFollow.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LoopFollow.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		DD07B5C829E2F9C400C6A635 /* NightscoutUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutUtils.swift; sourceTree = "<group>"; };
 		DD152D3A24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoDisplaySettingsViewController.swift; sourceTree = "<group>"; };
+		DD7FFAFC2A72953000C3A304 /* EKEventStore+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EKEventStore+Extensions.swift"; sourceTree = "<group>"; };
 		DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareClientExtension.swift; sourceTree = "<group>"; };
 		DDCF979324C0D380002C9752 /* UIViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewExtension.swift; sourceTree = "<group>"; };
 		DDCF979524C1443C002C9752 /* GeneralSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsViewController.swift; sourceTree = "<group>"; };
@@ -382,6 +384,7 @@
 				DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */,
 				DDCF979324C0D380002C9752 /* UIViewExtension.swift */,
 				FC1BB3A7252CA4C5003839C2 /* UIImageExtension.swift */,
+				DD7FFAFC2A72953000C3A304 /* EKEventStore+Extensions.swift */,
 			);
 			path = Extensions;
 			sourceTree = "<group>";
@@ -862,6 +865,7 @@
 			files = (
 				FCC68850248935D800A0279D /* AlarmViewController.swift in Sources */,
 				FC7CE59F248D8D23001F83B8 /* SnoozeViewController.swift in Sources */,
+				DD7FFAFD2A72953000C3A304 /* EKEventStore+Extensions.swift in Sources */,
 				FCC6886724898F8000A0279D /* UserDefaultsValue.swift in Sources */,
 				DDCF979E24C2382A002C9752 /* AppStateController.swift in Sources */,
 				FC97881E2485969B00A7906C /* NightScoutViewController.swift in Sources */,

+ 6 - 2
LoopFollow/Application/AppDelegate.swift

@@ -28,9 +28,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
                 print("User has declined notifications")
             }
         }
+        
         let store = EKEventStore()
-        store.requestAccess(to: .event) {(granted, error) in
-           if !granted { return }
+        store.requestCalendarAccess { (granted, error) in
+            if !granted {
+                print("Failed to get calendar access: \(String(describing: error))")
+                return
+            }
         }
         
         UNUserNotificationCenter.current().delegate = self

+ 34 - 0
LoopFollow/Extensions/EKEventStore+Extensions.swift

@@ -0,0 +1,34 @@
+//
+//  EKEventStore+Extensions.swift
+//  LoopFollow
+//
+//  Created by Jonas Björkert on 2023-07-27.
+//  Copyright © 2023 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import EventKit
+
+#if swift(>=5.9)
+extension EKEventStore {
+    func requestCalendarAccess(completion: @escaping (Bool, Error?) -> Void) {
+        if #available(iOS 17, *) {
+            requestFullAccessToEvents { (granted, error) in
+                completion(granted, error)
+            }
+        } else {
+            requestAccess(to: .event) { (granted, error) in
+                completion(granted, error)
+            }
+        }
+    }
+}
+#else
+extension EKEventStore {
+    func requestCalendarAccess(completion: @escaping (Bool, Error?) -> Void) {
+        requestAccess(to: .event) { (granted, error) in
+            completion(granted, error)
+        }
+    }
+}
+#endif

+ 3 - 1
LoopFollow/Info.plist

@@ -87,7 +87,9 @@
 		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationLandscapeRight</string>
 	</array>
+	<key>NSCalendarsFullAccessUsageDescription</key>
+	<string>Loop Follow would like to access your calendar to update BG readings</string>
 	<key>ITSAppUsesNonExemptEncryption</key>
-<false/>
+	<false/>
 </dict>
 </plist>

+ 118 - 111
LoopFollow/ViewControllers/MainViewController.swift

@@ -641,126 +641,133 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
             }
         })
     }
-    
-    // Write calendar
+
     func writeCalendar() {
-        if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Write calendar start") }
-        store.requestAccess(to: .event) {(granted, error) in
-            if !granted { return }
-            
-            if UserDefaultsRepository.calendarIdentifier.value == "" { return }
-            
-            if self.bgData.count < 1 { return }
-                
-            // This lets us fire the method to write Min Ago entries only once a minute starting after 6 minutes but allows new readings through
-            if self.lastCalDate == self.bgData[self.bgData.count - 1].date
-                && (self.calTimer.isValid || (dateTimeUtils.getNowTimeIntervalUTC() - self.lastCalDate) < 360) {
+        if UserDefaultsRepository.debugLog.value {
+            self.writeDebugLog(value: "Write calendar start")
+        }
+        
+        self.store.requestCalendarAccess { (granted, error) in
+            if !granted {
+                print("Failed to get calendar access: \(String(describing: error))")
                 return
             }
+            self.processCalendarUpdates()
+        }
+    }
+    
+    func processCalendarUpdates() {
+        if UserDefaultsRepository.calendarIdentifier.value == "" { return }
+        
+        if self.bgData.count < 1 { return }
+            
+        // This lets us fire the method to write Min Ago entries only once a minute starting after 6 minutes but allows new readings through
+        if self.lastCalDate == self.bgData[self.bgData.count - 1].date
+            && (self.calTimer.isValid || (dateTimeUtils.getNowTimeIntervalUTC() - self.lastCalDate) < 360) {
+            return
+        }
 
-                // Create Event info
-                let deltaBG = self.bgData[self.bgData.count - 1].sgv -  self.bgData[self.bgData.count - 2].sgv as Int
-                let deltaTime = (TimeInterval(Date().timeIntervalSince1970) - self.bgData[self.bgData.count - 1].date) / 60
-                var deltaString = ""
-                if deltaBG < 0 {
-                    deltaString = bgUnits.toDisplayUnits(String(deltaBG))
-                }
-                else
-                {
-                    deltaString = "+" + bgUnits.toDisplayUnits(String(deltaBG))
-                }
-                let direction = self.bgDirectionGraphic(self.bgData[self.bgData.count - 1].direction ?? "")
-                
-                var eventStartDate = Date(timeIntervalSince1970: self.bgData[self.bgData.count - 1].date)
+            // Create Event info
+            let deltaBG = self.bgData[self.bgData.count - 1].sgv -  self.bgData[self.bgData.count - 2].sgv as Int
+            let deltaTime = (TimeInterval(Date().timeIntervalSince1970) - self.bgData[self.bgData.count - 1].date) / 60
+            var deltaString = ""
+            if deltaBG < 0 {
+                deltaString = bgUnits.toDisplayUnits(String(deltaBG))
+            }
+            else
+            {
+                deltaString = "+" + bgUnits.toDisplayUnits(String(deltaBG))
+            }
+            let direction = self.bgDirectionGraphic(self.bgData[self.bgData.count - 1].direction ?? "")
+            
+            var eventStartDate = Date(timeIntervalSince1970: self.bgData[self.bgData.count - 1].date)
 //                if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Calendar start date") }
-                var eventEndDate = eventStartDate.addingTimeInterval(60 * 10)
-                var  eventTitle = UserDefaultsRepository.watchLine1.value
-                if (UserDefaultsRepository.watchLine2.value.count > 1) {
-                    eventTitle += "\n" + UserDefaultsRepository.watchLine2.value
-                }
-                eventTitle = eventTitle.replacingOccurrences(of: "%BG%", with: bgUnits.toDisplayUnits(String(self.bgData[self.bgData.count - 1].sgv)))
-                eventTitle = eventTitle.replacingOccurrences(of: "%DIRECTION%", with: direction)
-                eventTitle = eventTitle.replacingOccurrences(of: "%DELTA%", with: deltaString)
-                if self.currentOverride != 1.0 {
-                    let val = Int( self.currentOverride*100)
-                    // let overrideText = String(format:"%f1", self.currentOverride*100)
-                    let text = String(val) + "%"
-                    eventTitle = eventTitle.replacingOccurrences(of: "%OVERRIDE%", with: text)
-                } else {
-                    eventTitle = eventTitle.replacingOccurrences(of: "%OVERRIDE%", with: "")
-                }
-                eventTitle = eventTitle.replacingOccurrences(of: "%LOOP%", with: self.latestLoopStatusString)
-                var minAgo = ""
-                if deltaTime > 9 {
-                    // write old BG reading and continue pushing out end date to show last entry
-                    minAgo = String(Int(deltaTime)) + " min"
-                    eventEndDate = eventStartDate.addingTimeInterval((60 * 10) + (deltaTime * 60))
-                }
-                var cob = "0"
-                if self.latestCOB != "" {
-                    cob = self.latestCOB
-                }
-                var basal = "~"
-                if self.latestBasal != "" {
-                    basal = self.latestBasal
-                }
-                var iob = "0"
-                if self.latestIOB != "" {
-                    iob = self.latestIOB
-                }
-                eventTitle = eventTitle.replacingOccurrences(of: "%MINAGO%", with: minAgo)
-                eventTitle = eventTitle.replacingOccurrences(of: "%IOB%", with: iob)
-                eventTitle = eventTitle.replacingOccurrences(of: "%COB%", with: cob)
-                eventTitle = eventTitle.replacingOccurrences(of: "%BASAL%", with: basal)
-                
-                
-                
-                // Delete Events from last 2 hours and 2 hours in future
-                var deleteStartDate = Date().addingTimeInterval(-60*60*2)
-                var deleteEndDate = Date().addingTimeInterval(60*60*2)
-                // guard solves for some ios upgrades removing the calendar
-                guard let deleteCalendar = self.store.calendar(withIdentifier: UserDefaultsRepository.calendarIdentifier.value) as? EKCalendar else { return }
-                var predicate2 = self.store.predicateForEvents(withStart: deleteStartDate, end: deleteEndDate, calendars: [deleteCalendar])
-                var eVDelete = self.store.events(matching: predicate2) as [EKEvent]?
-                if eVDelete != nil {
-                    for i in eVDelete! {
-                        do {
-                            try self.store.remove(i, span: EKSpan.thisEvent, commit: true)
-                            //if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Calendar Delete") }
-                        } catch let error {
-                            //if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Error - Calendar Delete") }
-                            print(error)
-                        }
-                    }
-                }
-                
-                // Write New Event
-                var event = EKEvent(eventStore: self.store)
-                event.title = eventTitle
-                event.startDate = eventStartDate
-                event.endDate = eventEndDate
-                event.calendar = self.store.calendar(withIdentifier: UserDefaultsRepository.calendarIdentifier.value)
-                do {
-                    try self.store.save(event, span: .thisEvent, commit: true)
-                    self.calTimer.invalidate()
-                    self.startCalTimer(time: (60 * 1))
-                    
-                    self.lastCalDate = self.bgData[self.bgData.count - 1].date
-                    //if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Calendar Write: " + eventTitle) }
-                    //UserDefaultsRepository.savedEventID.value = event.eventIdentifier //save event id to access this particular event later
-                } catch {
-                    // Display error to user
-                    //if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Error: Calendar Write") }
-                }
+            var eventEndDate = eventStartDate.addingTimeInterval(60 * 10)
+            var  eventTitle = UserDefaultsRepository.watchLine1.value
+            if (UserDefaultsRepository.watchLine2.value.count > 1) {
+                eventTitle += "\n" + UserDefaultsRepository.watchLine2.value
+            }
+            eventTitle = eventTitle.replacingOccurrences(of: "%BG%", with: bgUnits.toDisplayUnits(String(self.bgData[self.bgData.count - 1].sgv)))
+            eventTitle = eventTitle.replacingOccurrences(of: "%DIRECTION%", with: direction)
+            eventTitle = eventTitle.replacingOccurrences(of: "%DELTA%", with: deltaString)
+            if self.currentOverride != 1.0 {
+                let val = Int( self.currentOverride*100)
+                // let overrideText = String(format:"%f1", self.currentOverride*100)
+                let text = String(val) + "%"
+                eventTitle = eventTitle.replacingOccurrences(of: "%OVERRIDE%", with: text)
+            } else {
+                eventTitle = eventTitle.replacingOccurrences(of: "%OVERRIDE%", with: "")
+            }
+            eventTitle = eventTitle.replacingOccurrences(of: "%LOOP%", with: self.latestLoopStatusString)
+            var minAgo = ""
+            if deltaTime > 9 {
+                // write old BG reading and continue pushing out end date to show last entry
+                minAgo = String(Int(deltaTime)) + " min"
+                eventEndDate = eventStartDate.addingTimeInterval((60 * 10) + (deltaTime * 60))
+            }
+            var cob = "0"
+            if self.latestCOB != "" {
+                cob = self.latestCOB
+            }
+            var basal = "~"
+            if self.latestBasal != "" {
+                basal = self.latestBasal
+            }
+            var iob = "0"
+            if self.latestIOB != "" {
+                iob = self.latestIOB
+            }
+            eventTitle = eventTitle.replacingOccurrences(of: "%MINAGO%", with: minAgo)
+            eventTitle = eventTitle.replacingOccurrences(of: "%IOB%", with: iob)
+            eventTitle = eventTitle.replacingOccurrences(of: "%COB%", with: cob)
+            eventTitle = eventTitle.replacingOccurrences(of: "%BASAL%", with: basal)
             
-            if UserDefaultsRepository.saveImage.value {
-                DispatchQueue.main.async {
-                    self.saveChartImage()
+            
+            
+            // Delete Events from last 2 hours and 2 hours in future
+            var deleteStartDate = Date().addingTimeInterval(-60*60*2)
+            var deleteEndDate = Date().addingTimeInterval(60*60*2)
+            // guard solves for some ios upgrades removing the calendar
+            guard let deleteCalendar = self.store.calendar(withIdentifier: UserDefaultsRepository.calendarIdentifier.value) as? EKCalendar else { return }
+            var predicate2 = self.store.predicateForEvents(withStart: deleteStartDate, end: deleteEndDate, calendars: [deleteCalendar])
+            var eVDelete = self.store.events(matching: predicate2) as [EKEvent]?
+            if eVDelete != nil {
+                for i in eVDelete! {
+                    do {
+                        try self.store.remove(i, span: EKSpan.thisEvent, commit: true)
+                        //if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Calendar Delete") }
+                    } catch let error {
+                        //if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Error - Calendar Delete") }
+                        print(error)
+                    }
                 }
-                    
             }
             
-            
+            // Write New Event
+            var event = EKEvent(eventStore: self.store)
+            event.title = eventTitle
+            event.startDate = eventStartDate
+            event.endDate = eventEndDate
+            event.calendar = self.store.calendar(withIdentifier: UserDefaultsRepository.calendarIdentifier.value)
+            do {
+                try self.store.save(event, span: .thisEvent, commit: true)
+                self.calTimer.invalidate()
+                self.startCalTimer(time: (60 * 1))
+                
+                self.lastCalDate = self.bgData[self.bgData.count - 1].date
+                //if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Calendar Write: " + eventTitle) }
+                //UserDefaultsRepository.savedEventID.value = event.eventIdentifier //save event id to access this particular event later
+            } catch {
+                print("*** Error storing to the calendar")
+                // Display error to user
+                //if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Error: Calendar Write") }
+            }
+        
+        if UserDefaultsRepository.saveImage.value {
+            DispatchQueue.main.async {
+                self.saveChartImage()
+            }
+                
         }
     }
     

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 19 - 4
LoopFollow/ViewControllers/WatchSettingsViewController.swift