Post

Replies

Boosts

Views

Activity

Reply to Background App Refresh
Just following up as I still don't quite understand. Does RefreshAppContentsOperation run automatically in the background at the designated time if the task is BGTaskScheduler.shared.register() and BGTaskScheduler.shared.submit() ran, or is that why there is operationQueue? If I were to not use operationQueue what would be the alternative? class BackgroundTasksController: ObservableObject { func scheduleTask() { BGTaskScheduler.shared.register(forTaskWithIdentifier: "app.Task", using: nil) { task in Task { await self.handleAppRefresh(task: task as! BGAppRefreshTask) } } } private func handleAppRefresh(task: BGAppRefreshTask) async { Task { scheduleAppRefresh() RefreshAppContentsOperation() } } func scheduleAppRefresh() { let request = BGAppRefreshTaskRequest(identifier: "app.Task") request.earliestBeginDate = Date(timeIntervalSinceNow: 60) do { try BGTaskScheduler.shared.submit(request) } catch { print("Could not schedule app refresh: \(error)") } } } final class RefreshAppContentsOperation: Operation, @unchecked Sendable { }
Jul ’25
Reply to Background App Refresh
Thank you for the explanation. I am getting two errors: Unless I pass task to handleAppRefresh as shown below I get an error as nothing is being passed to it: self.handleAppRefresh(appRefreshTask: task as! BGAppRefreshTask) And an error with this line as well: appRefreshTask.setTaskCompleted(success: success) "Cannot convert value of type '()' to expected argument type 'Bool'
Jul ’25
Reply to Background App Refresh
With that implementation the BGTaskScheduler function does not run within the start() function despite result.start() being called in the App struct. "will schedule, task: app.Task" in scheduleAppRefresh prints in the console, and I also have print(request) that prints this: <BGAppRefreshTaskRequest: app.TaskDate: 2025-07-24 18:30:14 +0000> Also, just to clarify, what function is the background task itself supposed to be in?
Jul ’25
Reply to Background App Refresh
With the following configuration, start(), handleAppRefresh(), and updateNow() doesn't run. The only thing that is different with my implementation that I have noticed is that I passed true to setTaskCompleted instead of success because I was getting an error. App: struct Peace_of_MindApp: App { var backgroundRefreshModel: BackgroundRefreshModel = { let result = BackgroundRefreshModel() result.start() return result }() var body: some Scene { WindowGroup { ContentView(backgroundRefreshModel: backgroundRefreshModel) } } } ContentView: struct ContentView: View { let backgroundRefreshModel: BackgroundRefreshModel var body: some View { ZStack { } .onAppear { backgroundRefreshModel.scheduleAppRefresh() } } BackgroundRefreshModel: @Observable @MainActor final class BackgroundRefreshModel { let routineResetID = "app.Task" private var isAppRefreshScheduled: Bool = false private(set) var lastUpdate: Date? = nil private var isUpdating: Bool = false let log = Logger(subsystem: "app.Task", category: "refresh") func updateNow() async { do { if self.isUpdating { return } self.isUpdating = true defer { self.isUpdating = false } try await Task.sleep(for: .seconds(3)) self.lastUpdate = .now } catch { // discard errors } } func start() { BGTaskScheduler.shared.register(forTaskWithIdentifier: routineResetID, using: .main) { task in let appRefreshTask = task as! BGAppRefreshTask self.handleAppRefresh(appRefreshTask: appRefreshTask) } } private func handleAppRefresh(appRefreshTask: BGAppRefreshTask) { self.isAppRefreshScheduled = false let swiftTask = Task { let success = await self.updateNow() appRefreshTask.setTaskCompleted(success: true) appRefreshTask.expirationHandler = nil } appRefreshTask.expirationHandler = { swiftTask.cancel() } } func scheduleAppRefresh() { if self.isAppRefreshScheduled { self.log.debug("will not schedule, already scheduled") return } self.isAppRefreshScheduled = true do { self.log.debug("will schedule, task: \(self.routineResetID, privacy: .public)") let request = BGAppRefreshTaskRequest( identifier: routineResetID ) request.earliestBeginDate = Date(timeIntervalSinceNow: 10) try BGTaskScheduler.shared.submit(request) } catch { // discard errors } } }
Jul ’25
Reply to @State variable returns empty despite being set in .onAppear function
struct ContentView: View { @State var data: [Data] @State var index: Int = 0 var body: some View { VStack { DataView(data: features[index]) } .onAppear { print("on appear") let filePath = Bundle.main.path(forResource: "data", ofType: "json") let url = URL(fileURLWithPath: filePath!) data = getData(url: url) print(data) } } func getData(url: URL) -> [Data] { do { let data = try Data(contentsOf: url) let jsonDecoded = try JSONDecoder().decode([Data].self, from: data) return jsonDecoded } catch let error as NSError { print("Fail: \(error.localizedDescription)") } catch { print("Fail: \(error)") } print("test 1") return [Data(index: 0, str: "")] } } struct Data: Decodable, Hashable { let index: Int let str: String } [ { "index": 0, "str": "String 1" }, { "index": 1, "str": "String 2" } ]
Topic: Design SubTopic: General Tags:
Aug ’25
Reply to @State variable returns empty despite being set in .onAppear function
My apologies. The code below should compile. struct ContentView: View { @State var sampleData: [SampleData] @State var index: Int = 0 var body: some View { VStack { DataView(data: sampleData[index]) } .onAppear { print("on appear") let filePath = Bundle.main.path(forResource: "data", ofType: "json") let url = URL(fileURLWithPath: filePath!) sampleData = getData(url: url) print(sampleData) } } func getData(url: URL) -> [SampleData] { do { let data = try Data(contentsOf: url) let jsonDecoded = try JSONDecoder().decode([SampleData].self, from: data) return jsonDecoded } catch let error as NSError { print("Fail: \(error.localizedDescription)") } catch { print("Fail: \(error)") } print("test 1") return [SampleData(index: 0, str: "")] } }
Topic: Design SubTopic: General Tags:
Aug ’25
Reply to @State variable returns empty despite being set in .onAppear function
I have a follow up question which you may have answered but I'm not sure. I am incrementing/decrementing a @State variable as a @Binding variable in another view: IndexView(current: $index) .onChange(of: index) { newValue in print("new value \(newValue)") } The variable newValue prints as it should (incrementing and decrementing) but the index variable doesn't seem to change. DataView(datum: data[index]) The DataView only displays data of index 0 despite the index incrementing.
Topic: Design SubTopic: General Tags:
Aug ’25
Reply to @State variable returns empty despite being set in .onAppear function
struct ContentView: View { @State var data: [Data2] @State var index: Int = 0 init() { let filePath = Bundle.main.path(forResource: "data", ofType: "json") let url = URL(fileURLWithPath: filePath!) // data = getData(url: url) // We cannot call getData in init do { let dataRead = try Data(contentsOf: url) let jsonDecoded = try JSONDecoder().decode([Data2].self, from: dataRead) data = jsonDecoded return } catch let error as NSError { print("Fail: \(error.localizedDescription)") } catch { print("Fail: \(error)") } data = [] return } var body: some View { VStack { DataView(data: data[index]) SlideDots(current: $index) } } } struct SlideDots: View { @Binding var current: Int var body: some View { HStack { if current > 0 { Spacer() .frame(width: 25) Button(action: { if current > 0 { current -= 1 }}) { Image(systemName: "chevron.left") } .foregroundStyle(getColor(type: .main)) .frame(width: 35) } else { Spacer() .frame(width: 60) } Spacer() ForEach(0..<4) { index in Spacer() .frame(width: 15) Circle() .fill(index == current ? Color.gray : Color.white) .stroke(Color.gray, lineWidth: 1) .frame(width: 15, height: 15) Spacer() .frame(width: 15) } Spacer() if current < 3 { Button(action: { if current < 3 { current += 1 }}) { Image(systemName: "chevron.right") } .foregroundStyle(getColor(type: .main)) .frame(width: 35) Spacer() .frame(width: 25) } else { Spacer() .frame(width: 60) } } } }
Topic: Design SubTopic: General Tags:
Aug ’25
Reply to @State variable returns empty despite being set in .onAppear function
Here is a revised version of code: struct ContentView: View { @State var data: [Data2] @State var index: Int init() { index = 0 let filePath = Bundle.main.path(forResource: "data", ofType: "json") let url = URL(fileURLWithPath: filePath!) do { let data = try Data(contentsOf: url) let jsonDecoded = try JSONDecoder().decode([Data2].self, from: data) data = jsonDecoded return } catch let error as NSError { print("Fail: \(error.localizedDescription)") } catch { print("Fail: \(error)") } data = [] return } var body: some View { VStack { DataView(datum: data[index]) Spacer() SlideDots(current: $index) } }
Topic: Design SubTopic: General Tags:
Aug ’25
Reply to Background App Refresh
I was using BGAppRefreshTask instead of BGProcessingTask which was the issue. I am still in need of assistance implementing BGProcessingTask. I have the code below: BGTaskScheduler.shared.register(forTaskWithIdentifier: taskID, using: .main) { task in self.preformTask(task: task as! BGProcessingTask) } func preformTask(task: BGProcessingTask) { self.isAppRefreshScheduled = false let swiftTask = Task { let success = await self.updateNow() print("Success \(success)") task.setTaskCompleted(success: true) task.expirationHandler = nil } task.expirationHandler = { swiftTask.cancel() } func updateNow() async { do { if self.isUpdating { return } self.isUpdating = true defer { self.isUpdating = false } try await Task.sleep(for: .seconds(3)) UserDefaults.standard.set("taskCompleted", forKey: "test") print("Task") self.lastUpdate = .now } catch { // discard errors } } func scheduleTask() { UserDefaults.standard.set("taskScheduled", forKey: "test") if self.isAppRefreshScheduled { self.log.debug("will not schedule, already scheduled") return } self.isAppRefreshScheduled = true do { self.log.debug("will schedule, task: \(self.routineResetID, privacy: .public)") let request = BGAppRefreshTaskRequest( identifier: taskID ) request.earliestBeginDate = Date(timeIntervalSinceNow: 1) try BGTaskScheduler.shared.submit(request) print(request) } catch { // discard errors } } .onAppear() { let testVal = UserDefaults.standard.string(forKey: "test") ?? "default" print(testVal) backgroundRefreshModel.scheduleTask() } I'm not sure if that's the proper implementation. If it's not, what needs to be changed? To test this, I'm calling scheduleTask() in onAppear() in ContentView which runs. I set a UserDefaults variable to "taskScheduled" and in updateNow() I set that variable to "taskCompleted". "taskScheduled" prints in onAppear() when loading the app, but after a minute of the app being in the background and opening it again nothing prints. I thought that after closing and opening the app onAppear() would run. Is that an implementation error or is there another function for that purpose?
3d