Skip to content

Commit 592b229

Browse files
committed
Modified background updates.
1 parent 85e0314 commit 592b229

File tree

12 files changed

+36
-547
lines changed

12 files changed

+36
-547
lines changed

nightguard WatchKit App/ExtensionDelegate.swift

Lines changed: 0 additions & 300 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ class ExtensionDelegate: NSObject, WKApplicationDelegate {
4444
ExtensionDelegate.singleton = self
4545

4646
BackgroundRefreshLogger.info("Application did finish launching")
47-
scheduleBackgroundRefresh()
4847
AppMessageService.singleton.keepAwakePhoneApp()
4948
}
5049

@@ -67,11 +66,6 @@ class ExtensionDelegate: NSObject, WKApplicationDelegate {
6766
MainController.mainViewModel.refreshData(forceRefresh: true, moveToLatestValue: false)
6867
}
6968

70-
// nightscout data message
71-
WatchMessageService.singleton.onMessage { [weak self] (message: NightscoutDataMessage) in
72-
self?.onNightscoutDataReceivedFromPhoneApp(message.nightscoutData)
73-
}
74-
7569
// user defaults sync message
7670
WatchMessageService.singleton.onMessage { (message: UserDefaultSyncMessage) in
7771

@@ -127,300 +121,6 @@ class ExtensionDelegate: NSObject, WKApplicationDelegate {
127121
AppState.isUIActive = false
128122

129123
print("Application will resign active.")
130-
scheduleBackgroundRefresh()
131124
AppMessageService.singleton.keepAwakePhoneApp()
132125
}
133-
134-
public func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
135-
136-
for task in backgroundTasks {
137-
138-
// crash solving trick: acces the task user info to avoid a rare, but weird crash.. (https://forums.developer.apple.com/thread/96504 and https://stackoverflow.com/questions/46464660/wkrefreshbackgroundtask-cleanupstorage-error-attempting-to-reach-file)
139-
userInfoAccess = task.userInfo
140-
141-
if let watchConnectivityBackgroundTask = task as? WKWatchConnectivityRefreshBackgroundTask {
142-
handleWatchConnectivityBackgroundTask(watchConnectivityBackgroundTask)
143-
} else if let snapshotTask = task as? WKSnapshotRefreshBackgroundTask {
144-
handleSnapshotTask(snapshotTask)
145-
} else if let sessionTask = task as? WKURLSessionRefreshBackgroundTask {
146-
handleURLSessionTask(sessionTask)
147-
} else if let refreshTask = task as? WKApplicationRefreshBackgroundTask, WKApplication.shared().applicationState == .background {
148-
handleRefreshTask(refreshTask)
149-
} else {
150-
// not handled!
151-
task.setTaskCompletedWithSnapshot(false)
152-
}
153-
}
154-
}
155-
}
156-
157-
extension ExtensionDelegate {
158-
159-
// MARK:- Background update methods
160-
161-
func handleWatchConnectivityBackgroundTask (_ watchConnectivityBackgroundTask: WKWatchConnectivityRefreshBackgroundTask) {
162-
163-
BackgroundRefreshLogger.info("WKWatchConnectivityRefreshBackgroundTask received")
164-
watchConnectivityBackgroundTask.setTaskCompletedWithSnapshot(false)
165-
}
166-
167-
func handleSnapshotTask(_ snapshotTask : WKSnapshotRefreshBackgroundTask) {
168-
169-
BackgroundRefreshLogger.info("WKSnapshotRefreshBackgroundTask received")
170-
171-
// update user interface with current nightscout data (or error)
172-
let currentNightscoutData = NightscoutCacheService.singleton.getCurrentNightscoutData()
173-
MainController.mainViewModel.pushBackgroundData(newNightscoutData: currentNightscoutData)
174-
175-
snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil)
176-
}
177-
178-
func handleRefreshTask(_ task : WKRefreshBackgroundTask) {
179-
180-
BackgroundRefreshLogger.info("WKApplicationRefreshBackgroundTask received")
181-
BackgroundRefreshLogger.backgroundRefreshes += 1
182-
183-
scheduleURLSessionIfNeeded()
184-
185-
// schedule the next background refresh
186-
BackgroundRefreshScheduler.instance.schedule()
187-
188-
// Ask to refresh all complications
189-
WidgetCenter.shared.reloadAllTimelines()
190-
191-
task.setTaskCompletedWithSnapshot(false)
192-
}
193-
194-
func handleURLSessionTask(_ sessionTask: WKURLSessionRefreshBackgroundTask) {
195-
196-
BackgroundRefreshLogger.info("WKURLSessionRefreshBackgroundTask received")
197-
198-
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: sessionTask.sessionIdentifier)
199-
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
200-
print("Rejoining session ", backgroundSession)
201-
202-
// keep the session background task, it will be ended later... (https://stackoverflow.com/questions/41156386/wkurlsessionrefreshbackgroundtask-isnt-called-when-attempting-to-do-background)
203-
self.pendingBackgroundURLTask = sessionTask
204-
}
205-
206-
@discardableResult
207-
func onNightscoutDataReceivedFromPhoneApp(_ nightscoutData: NightscoutData) -> Bool {
208-
209-
BackgroundRefreshLogger.phoneUpdates += 1
210-
211-
guard !nightscoutData.isOlderThanXMinutes(60) else {
212-
BackgroundRefreshLogger.info("📱Rejected nightscout data (>1hr old!)")
213-
return false
214-
}
215-
216-
let updateResult = updateNightscoutData(nightscoutData, updateComplication: true) // always update complication!
217-
BackgroundRefreshLogger.nightscoutDataReceived(nightscoutData, updateResult: updateResult, updateSource: .phoneApp)
218-
switch updateResult {
219-
case .updateDataIsOld:
220-
BackgroundRefreshLogger.phoneUpdatesWithOldData += 1
221-
case .updateDataAlreadyExists:
222-
BackgroundRefreshLogger.phoneUpdatesWithSameData += 1
223-
case .updated:
224-
BackgroundRefreshLogger.phoneUpdatesWithNewData += 1
225-
}
226-
227-
return updateResult != .updateDataIsOld
228-
}
229-
230-
// MARK:- Internals
231-
232-
fileprivate func scheduleBackgroundRefresh() {
233-
BackgroundRefreshScheduler.instance.schedule()
234-
}
235-
236-
enum UpdateSource {
237-
238-
// the update was initiated by phone app
239-
case phoneApp
240-
241-
// the update was initiated by watch (background URL session)
242-
case urlSession
243-
}
244-
245-
enum UpdateResult {
246-
247-
// update succeeded
248-
case updated
249-
250-
// update data already exists (is the current nightscout data) - no need to update!
251-
case updateDataAlreadyExists
252-
253-
// update data is older than current nightscout data
254-
case updateDataIsOld
255-
}
256-
257-
fileprivate func updateNightscoutData(_ newNightscoutData: NightscoutData, updateComplication: Bool = true) -> UpdateResult {
258-
259-
// synchronize the background update to prevent concurrent modifications
260-
objc_sync_enter(self)
261-
262-
defer {
263-
objc_sync_exit(self)
264-
}
265-
266-
// check the data that already exists on the watch... maybe is newer that the received data
267-
let currentNightscoutData = NightscoutCacheService.singleton.getCurrentNightscoutData()
268-
if currentNightscoutData.time.doubleValue > newNightscoutData.time.doubleValue {
269-
270-
// Old data was received from remote (phone app or URL session)! This can happen because:
271-
// 1. if receiving data from phone app: the watch can have newer data than the phone app (phone app background fetch is once in 5 minutes) or because the delivery is not instantaneous and ... and the watch can update its data in between (when the app enters foreground)
272-
// 2. if receiving data from a URL session: the session can complete later, when there are resources available on the watch to execute it... so there is a posibility than the watch app update itself till then
273-
print("Received older nightscout data than current watch nightscout data!")
274-
return .updateDataIsOld
275-
276-
} else if currentNightscoutData.time.doubleValue == newNightscoutData.time.doubleValue {
277-
278-
// already have this data...
279-
return .updateDataAlreadyExists
280-
}
281-
282-
print("Nightscout data was received from remote (phone app or URL session)!")
283-
NightscoutCacheService.singleton.updateCurrentNightscoutData(newNightscoutData: newNightscoutData)
284-
// if #available(watchOSApplicationExtension 3.0, *) {
285-
// scheduleSnapshotRefresh()
286-
// }
287-
288-
return .updated
289-
}
290-
291-
func synced(_ lock: Any, closure: () -> ()) {
292-
objc_sync_enter(lock)
293-
closure()
294-
objc_sync_exit(lock)
295-
}
296-
297-
func scheduleURLSessionIfNeeded() {
298-
299-
// let currentNightscoutData = NightscoutCacheService.singleton.getCurrentNightscoutData()
300-
// guard currentNightscoutData.isOlderThan5Minutes() else {
301-
// BackgroundRefreshLogger.info("Recent nightscout data, skipping URL session!")
302-
// return
303-
// }
304-
305-
if self.backgroundSession != nil {
306-
307-
if let sessionStartTime = self.sessionStartTime, Calendar.current.date(byAdding: .minute, value: BackgroundRefreshSettings.urlSessionTaskTimeout, to: sessionStartTime)! > Date() {
308-
309-
// URL session running.. we'll let it do its work!
310-
BackgroundRefreshLogger.info("URL session already exists, cannot start a new one!")
311-
return
312-
} else {
313-
314-
// timeout reached for URL session, we'll start a new one!
315-
BackgroundRefreshLogger.info("URL session timeout exceeded, finishing current and starting a new one!")
316-
completePendingURLSessionTask()
317-
}
318-
}
319-
320-
guard let (backgroundSession, downloadTask) = scheduleURLSession() else {
321-
BackgroundRefreshLogger.info("URL session cannot be created, probably base uri is not configured!")
322-
return
323-
}
324-
325-
self.sessionStartTime = Date()
326-
self.backgroundSession = backgroundSession
327-
self.downloadTask = downloadTask
328-
BackgroundRefreshLogger.backgroundURLSessions += 1
329-
BackgroundRefreshLogger.info("URL session started")
330-
}
331-
}
332-
333-
extension ExtensionDelegate: URLSessionDownloadDelegate {
334-
335-
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
336-
print("Background download was finished.")
337-
338-
// reset the session error
339-
self.sessionError = nil
340-
341-
let nightscoutData = NSData(contentsOf: location as URL)
342-
343-
// extract data on main thead
344-
DispatchQueue.main.async { [unowned self] in
345-
346-
if nightscoutData == nil {
347-
return
348-
}
349-
350-
NightscoutService.singleton.extractApiV2PropertiesData(data: nightscoutData! as Data, { [unowned self] result in
351-
352-
switch result {
353-
case .error(let error):
354-
self.sessionError = error
355-
356-
case .data(let newNightscoutData):
357-
self.sessionError = nil
358-
359-
let updateResult = self.updateNightscoutData(newNightscoutData)
360-
BackgroundRefreshLogger.nightscoutDataReceived(newNightscoutData, updateResult: updateResult, updateSource: .urlSession)
361-
switch updateResult {
362-
case .updateDataIsOld:
363-
BackgroundRefreshLogger.backgroundURLSessionUpdatesWithOldData += 1
364-
BackgroundRefreshLogger.info("URL session data: OLD")
365-
case .updateDataAlreadyExists:
366-
BackgroundRefreshLogger.backgroundURLSessionUpdatesWithSameData += 1
367-
BackgroundRefreshLogger.info("URL session data: EXISTING")
368-
case .updated:
369-
BackgroundRefreshLogger.backgroundURLSessionUpdatesWithNewData += 1
370-
BackgroundRefreshLogger.info("URL session data: NEW")
371-
}
372-
}
373-
})
374-
}
375-
376-
completePendingURLSessionTask()
377-
}
378-
379-
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
380-
print("Background url session completed with error: \(String(describing: error))")
381-
if let error = error {
382-
BackgroundRefreshLogger.info("URL session did complete with error: \(error)")
383-
completePendingURLSessionTask()
384-
}
385-
386-
// keep the session error (if any!)
387-
self.sessionError = error
388-
}
389-
390-
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
391-
BackgroundRefreshLogger.info("URL session did finish events")
392-
// completePendingURLSessionTask()
393-
}
394-
395-
fileprivate func completePendingURLSessionTask() {
396-
397-
self.backgroundSession?.invalidateAndCancel()
398-
self.backgroundSession = nil
399-
self.downloadTask = nil
400-
self.sessionStartTime = nil
401-
(self.pendingBackgroundURLTask as? WKRefreshBackgroundTask)?.setTaskCompletedWithSnapshot(false)
402-
self.pendingBackgroundURLTask = nil
403-
404-
BackgroundRefreshLogger.info("URL session COMPLETED")
405-
}
406-
407-
func scheduleURLSession() -> (URLSession, URLSessionDownloadTask)? {
408-
409-
let baseUri = UserDefaultsRepository.baseUri.value
410-
if baseUri == "" {
411-
return nil
412-
}
413-
414-
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: NSUUID().uuidString)
415-
backgroundConfigObject.sessionSendsLaunchEvents = true
416-
// backgroundConfigObject.timeoutIntervalForRequest = 15 // 15 seconds timeout for request (after 15 seconds, the task is finished and a crash occurs, so... we have to stop it somehow!)
417-
// backgroundConfigObject.timeoutIntervalForResource = 15 // the same for retry interval (no retries!)
418-
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
419-
420-
let downloadURL = URL(string: baseUri + "/pebble")!
421-
let downloadTask = backgroundSession.downloadTask(with: downloadURL)
422-
downloadTask.resume()
423-
424-
return (backgroundSession, downloadTask)
425-
}
426126
}

nightguard WatchKit App/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<key>CFBundleSignature</key>
2222
<string>????</string>
2323
<key>CFBundleVersion</key>
24-
<string>872</string>
24+
<string>874</string>
2525
<key>CLKComplicationPrincipalClass</key>
2626
<string>$(PRODUCT_MODULE_NAME).ComplicationController</string>
2727
<key>NSAppTransportSecurity</key>

nightguard WatchKit App/app/BackgroundRefreshLogger.swift

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class BackgroundRefreshLogger {
4949

5050
private static var logStartTime: Date?
5151
private static var appStartTime: Date?
52-
private static let showLogs = BackgroundRefreshSettings.showBackgroundTasksLogs
52+
private static let showLogs = true
5353

5454
static func info(_ text: String) {
5555
resetStatsDataIfNeeded()
@@ -62,39 +62,6 @@ class BackgroundRefreshLogger {
6262
}
6363
}
6464

65-
66-
static func nightscoutDataReceived(_ nightscoutData: NightscoutData, updateResult: ExtensionDelegate.UpdateResult, updateSource: ExtensionDelegate.UpdateSource) {
67-
68-
var updateSourceString = ""
69-
switch updateSource {
70-
case .phoneApp :
71-
updateSourceString = "📱"
72-
default:
73-
updateSourceString = ""
74-
}
75-
76-
var updateResultString = ""
77-
switch updateResult {
78-
case .updated:
79-
updateResultString = "NEW"
80-
case .updateDataAlreadyExists:
81-
updateResultString = "EXISTING"
82-
case .updateDataIsOld:
83-
updateResultString = "OLD"
84-
}
85-
86-
let nightscoutDataTime = Date(timeIntervalSince1970: nightscoutData.time.doubleValue / 1000)
87-
let nightscoutDataTimeString = formattedTime(nightscoutDataTime, showSeconds: false)
88-
89-
let logEntry = formattedTime(Date()) + " " + updateSourceString + updateResultString + " (\(nightscoutData.sgv)@\(nightscoutDataTimeString))"
90-
NSLog(logEntry)
91-
92-
if showLogs {
93-
receivedData.append(logEntry)
94-
}
95-
96-
}
97-
9865
private static func resetStatsDataIfNeeded() {
9966

10067
let now = Date()

0 commit comments

Comments
 (0)