Skip to content

Commit 23ba87a

Browse files
committed
refactor: background handling
Signed-off-by: Tommy van der Vorst <tommy@pixelspark.nl>
1 parent a46b512 commit 23ba87a

File tree

5 files changed

+215
-43
lines changed

5 files changed

+215
-43
lines changed

Localizable.xcstrings

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7204,6 +7204,52 @@
72047204
}
72057205
}
72067206
},
7207+
"continued" : {
7208+
"localizations" : {
7209+
"de" : {
7210+
"stringUnit" : {
7211+
"state" : "translated",
7212+
"value" : "fortgesetzt"
7213+
}
7214+
},
7215+
"es" : {
7216+
"stringUnit" : {
7217+
"state" : "translated",
7218+
"value" : "continuado"
7219+
}
7220+
},
7221+
"it" : {
7222+
"stringUnit" : {
7223+
"state" : "translated",
7224+
"value" : "continuato"
7225+
}
7226+
},
7227+
"ja" : {
7228+
"stringUnit" : {
7229+
"state" : "translated",
7230+
"value" : "継続"
7231+
}
7232+
},
7233+
"nl" : {
7234+
"stringUnit" : {
7235+
"state" : "translated",
7236+
"value" : "voortgezet"
7237+
}
7238+
},
7239+
"uk" : {
7240+
"stringUnit" : {
7241+
"state" : "translated",
7242+
"value" : "продовжено"
7243+
}
7244+
},
7245+
"zh-Hans" : {
7246+
"stringUnit" : {
7247+
"state" : "translated",
7248+
"value" : "继续"
7249+
}
7250+
}
7251+
}
7252+
},
72077253
"Copy" : {
72087254
"localizations" : {
72097255
"de" : {
@@ -18898,6 +18944,52 @@
1889818944
}
1889918945
}
1890018946
},
18947+
"long" : {
18948+
"localizations" : {
18949+
"de" : {
18950+
"stringUnit" : {
18951+
"state" : "translated",
18952+
"value" : "lang"
18953+
}
18954+
},
18955+
"es" : {
18956+
"stringUnit" : {
18957+
"state" : "translated",
18958+
"value" : "largo"
18959+
}
18960+
},
18961+
"it" : {
18962+
"stringUnit" : {
18963+
"state" : "translated",
18964+
"value" : "lungo"
18965+
}
18966+
},
18967+
"ja" : {
18968+
"stringUnit" : {
18969+
"state" : "translated",
18970+
"value" : "長い"
18971+
}
18972+
},
18973+
"nl" : {
18974+
"stringUnit" : {
18975+
"state" : "translated",
18976+
"value" : "lang"
18977+
}
18978+
},
18979+
"uk" : {
18980+
"stringUnit" : {
18981+
"state" : "translated",
18982+
"value" : "довгий"
18983+
}
18984+
},
18985+
"zh-Hans" : {
18986+
"stringUnit" : {
18987+
"state" : "translated",
18988+
"value" : "长"
18989+
}
18990+
}
18991+
}
18992+
},
1890118993
"Manage files and folders" : {
1890218994
"localizations" : {
1890318995
"de" : {
@@ -26990,6 +27082,52 @@
2699027082
}
2699127083
}
2699227084
},
27085+
"short" : {
27086+
"localizations" : {
27087+
"de" : {
27088+
"stringUnit" : {
27089+
"state" : "translated",
27090+
"value" : "kurz"
27091+
}
27092+
},
27093+
"es" : {
27094+
"stringUnit" : {
27095+
"state" : "translated",
27096+
"value" : "corto"
27097+
}
27098+
},
27099+
"it" : {
27100+
"stringUnit" : {
27101+
"state" : "translated",
27102+
"value" : "breve"
27103+
}
27104+
},
27105+
"ja" : {
27106+
"stringUnit" : {
27107+
"state" : "translated",
27108+
"value" : "短い"
27109+
}
27110+
},
27111+
"nl" : {
27112+
"stringUnit" : {
27113+
"state" : "translated",
27114+
"value" : "kort"
27115+
}
27116+
},
27117+
"uk" : {
27118+
"stringUnit" : {
27119+
"state" : "translated",
27120+
"value" : "короткий"
27121+
}
27122+
},
27123+
"zh-Hans" : {
27124+
"stringUnit" : {
27125+
"state" : "translated",
27126+
"value" : "短"
27127+
}
27128+
}
27129+
}
27130+
},
2699327131
"Short device ID" : {
2699427132
"localizations" : {
2699527133
"de" : {

Sushitrain/AppState.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ struct SyncState {
812812
}
813813

814814
func cancelLingering() {
815-
Log.info("Cancel lingering")
815+
Log.info("Cancel lingering lingerTask=\(self.lingerTask.debugDescription)")
816816
if let lt = self.lingerTask {
817817
UIApplication.shared.endBackgroundTask(lt)
818818
}

Sushitrain/BackgroundManager.swift

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,43 @@ import SwiftUI
77
@preconcurrency import SushitrainCore
88
import BackgroundTasks
99

10+
struct BackgroundSyncRun: Codable, Equatable {
11+
var started: Date
12+
var ended: Date?
13+
var taskType: BackgroundTaskType?
14+
15+
var asString: String {
16+
if let ended = self.ended {
17+
if let tt = self.taskType {
18+
return "\(self.started.formatted()) - \(ended.formatted()) (\(tt.localizedTypeName))"
19+
}
20+
return "\(self.started.formatted()) - \(ended.formatted())"
21+
}
22+
return self.started.formatted()
23+
}
24+
}
25+
26+
enum BackgroundTaskType: String, Codable, Equatable {
27+
case short = "nl.t-shaped.sushitrain.short-background-sync"
28+
case long = "nl.t-shaped.sushitrain.background-sync"
29+
case continued = "nl.t-shaped.sushitrain.background-sync.continued"
30+
31+
var identifier: String {
32+
return self.rawValue
33+
}
34+
35+
var localizedTypeName: String {
36+
switch self {
37+
case .short: String(localized: "short")
38+
case .long: String(localized: "long")
39+
case .continued: String(localized: "continued")
40+
}
41+
}
42+
}
43+
1044
#if os(iOS)
1145
@MainActor class BackgroundManager: ObservableObject {
12-
private static let longBackgroundSyncID = "nl.t-shaped.sushitrain.background-sync"
13-
private static let shortBackgroundSyncID = "nl.t-shaped.sushitrain.short-background-sync"
1446
private static let watchdogNotificationID = "nl.t-shaped.sushitrain.watchdog-notification"
15-
private static let continuedBackgroundSyncID = "nl.t-shaped.sushitrain.background-sync.continued"
1647

1748
// Time before the end of allotted background time to start ending the task to prevent forceful expiration by the OS
1849
private static let backgroundTimeReserve: TimeInterval = 5.6
@@ -59,13 +90,13 @@ import BackgroundTasks
5990
// Schedule background synchronization task
6091
// Must start on a specified queue (here we simply use main) to prevent a crash in dispatch_assert_queue
6192
BGTaskScheduler.shared.register(
62-
forTaskWithIdentifier: Self.longBackgroundSyncID, using: DispatchQueue.main,
93+
forTaskWithIdentifier: BackgroundTaskType.long.identifier, using: DispatchQueue.main,
6394
launchHandler: self.backgroundLaunchHandler)
6495
BGTaskScheduler.shared.register(
65-
forTaskWithIdentifier: Self.shortBackgroundSyncID, using: DispatchQueue.main,
96+
forTaskWithIdentifier: BackgroundTaskType.short.identifier, using: DispatchQueue.main,
6697
launchHandler: self.backgroundLaunchHandler)
6798
BGTaskScheduler.shared.register(
68-
forTaskWithIdentifier: Self.continuedBackgroundSyncID, using: DispatchQueue.main,
99+
forTaskWithIdentifier: BackgroundTaskType.continued.identifier, using: DispatchQueue.main,
69100
launchHandler: self.backgroundLaunchHandler)
70101

71102
updateBackgroundRunHistory(appending: nil)
@@ -77,16 +108,24 @@ import BackgroundTasks
77108
}
78109

79110
@available(iOS 26, *) func startContinuedSync() throws {
80-
let request = BGContinuedProcessingTaskRequest(
81-
identifier: Self.continuedBackgroundSyncID,
82-
title: "Synchronize files",
83-
subtitle: "About to start...",
84-
)
85-
request.strategy = .fail
86-
try BGTaskScheduler.shared.submit(request)
111+
do {
112+
let request = BGContinuedProcessingTaskRequest(
113+
identifier: BackgroundTaskType.continued.identifier,
114+
title: "Synchronize files",
115+
subtitle: "About to start...",
116+
)
117+
request.strategy = .queue
118+
Log.info("Scheduling continued background processing task")
119+
try BGTaskScheduler.shared.submit(request)
120+
}
121+
catch {
122+
Log.warn("Failed to schedule continued background processing task: \(error.localizedDescription)")
123+
throw error
124+
}
87125
}
88126

89127
private func backgroundLaunchHandler(_ task: BGTask) {
128+
Log.info("Background launch handler: \(task.identifier)")
90129
Task { @MainActor in
91130
await self.handleBackgroundSync(task: task)
92131
}
@@ -102,9 +141,15 @@ import BackgroundTasks
102141
}
103142

104143
private func handleBackgroundSync(task: BGTask) async {
144+
guard let taskType = BackgroundTaskType(rawValue: task.identifier) else {
145+
Log.warn("invalid background task type identifier=\(task.identifier)")
146+
return
147+
}
148+
105149
let start = Date.now
106150
self.currentBackgroundTask = task
107-
Log.info("Start background task at \(start) \(task.identifier)")
151+
Log.info("Start background task at \(start) \(task.identifier) type=\(taskType)")
152+
108153
DispatchQueue.main.async {
109154
_ = self.scheduleBackgroundSync()
110155
}
@@ -144,20 +189,20 @@ import BackgroundTasks
144189

145190
// Start photo back-up if the user has enabled it
146191
var photoBackupTask: Task<(), Error>? = nil
147-
if self.appState.photoBackup.enableBackgroundCopy && task.identifier == Self.longBackgroundSyncID {
192+
if self.appState.photoBackup.enableBackgroundCopy && taskType == .long {
148193
Log.info("Start photo backup task")
149194
photoBackupTask = self.appState.photoBackup.backup(appState: self.appState, fullExport: false, isInBackground: true)
150195
}
151196

152197
// Start background sync on long and short sync task (if enabled) and continued task
153-
if appState.userSettings.longBackgroundSyncEnabled || appState.userSettings.shortBackgroundSyncEnabled
154-
|| task.identifier == Self.continuedBackgroundSyncID
198+
if (taskType == .long && appState.userSettings.longBackgroundSyncEnabled)
199+
|| (taskType == .short && appState.userSettings.shortBackgroundSyncEnabled) || taskType == .continued
155200
{
156201
Log.info(
157202
"Start background sync, time remaining = \(UIApplication.shared.backgroundTimeRemaining)"
158203
)
159204
await self.appState.suspend(false)
160-
currentRun = BackgroundSyncRun(started: start, ended: nil)
205+
currentRun = BackgroundSyncRun(started: start, ended: nil, taskType: taskType)
161206
self.lastBackgroundSyncRun = currentRun
162207
if #available(iOS 26, *) {
163208
if let cg = task as? BGContinuedProcessingTask {
@@ -179,6 +224,7 @@ import BackgroundTasks
179224

180225
// Run to expiration
181226
task.expirationHandler = {
227+
Log.warn("Task expiration handler called identifier=\(task.identifier)")
182228
Task { @MainActor in
183229
Log.warn(
184230
"Background task expired (this should not happen because our timer should have expired the task first; perhaps iOS changed its mind?) Remaining = \(UIApplication.shared.backgroundTimeRemaining)"
@@ -189,7 +235,7 @@ import BackgroundTasks
189235
}
190236
else {
191237
// We're just doing some photo backupping this time
192-
if task.identifier == Self.longBackgroundSyncID {
238+
if taskType == .long {
193239
// When background task expires, end photo back-up
194240
task.expirationHandler = {
195241
Log.warn(
@@ -289,7 +335,7 @@ import BackgroundTasks
289335
var success = true
290336

291337
if appState.userSettings.longBackgroundSyncEnabled {
292-
let longRequest = BGProcessingTaskRequest(identifier: Self.longBackgroundSyncID)
338+
let longRequest = BGProcessingTaskRequest(identifier: BackgroundTaskType.long.identifier)
293339

294340
// No earlier than within 15 minutes
295341
longRequest.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
@@ -309,7 +355,7 @@ import BackgroundTasks
309355
}
310356

311357
if appState.userSettings.shortBackgroundSyncEnabled {
312-
let shortRequest = BGAppRefreshTaskRequest(identifier: Self.shortBackgroundSyncID)
358+
let shortRequest = BGAppRefreshTaskRequest(identifier: BackgroundTaskType.short.identifier)
313359
// No earlier than within 15 minutes
314360
shortRequest.earliestBeginDate = Date(timeIntervalSinceNow: 30 * 60)
315361
Log.info(

Sushitrain/SettingsView.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -631,18 +631,18 @@ struct AdvancedSettingsView: View {
631631

632632
Section("Last background synchronization") {
633633
if let lastSyncRun = self.appState.backgroundManager.lastBackgroundSyncRun {
634-
Text("Started").badge(
635-
lastSyncRun.started.formatted(
636-
date: .abbreviated, time: .shortened))
634+
Text("Started").badge(lastSyncRun.started.formatted(date: .abbreviated, time: .shortened))
637635

638636
if let lastSyncEnded = lastSyncRun.ended {
639-
Text("Ended").badge(
640-
lastSyncEnded.formatted(
641-
date: .abbreviated, time: .shortened))
637+
Text("Ended").badge(lastSyncEnded.formatted(date: .abbreviated, time: .shortened))
638+
642639
Text("Duration").badge(
643-
durationFormatter.string(
644-
from: lastSyncEnded.timeIntervalSince(
645-
lastSyncRun.started)))
640+
durationFormatter.string(from: lastSyncEnded.timeIntervalSince(lastSyncRun.started))
641+
)
642+
643+
if let taskType = lastSyncRun.taskType {
644+
Text("Type").badge(taskType.localizedTypeName)
645+
}
646646
}
647647
}
648648
else {

Sushitrain/Utils.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -542,18 +542,6 @@ extension SushitrainFolder: @retroactive Identifiable {
542542
extension SushitrainChange: @retroactive Identifiable {
543543
}
544544

545-
struct BackgroundSyncRun: Codable, Equatable {
546-
var started: Date
547-
var ended: Date?
548-
549-
var asString: String {
550-
if let ended = self.ended {
551-
return "\(self.started.formatted()) - \(ended.formatted())"
552-
}
553-
return self.started.formatted()
554-
}
555-
}
556-
557545
enum SushitrainEntryTransferableError: Error {
558546
case notAvailable
559547
}

0 commit comments

Comments
 (0)