Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Below is compilable code:
struct ContentView: View {
var body: some View {
Text("Text")
.onAppear() {
print("test")
}
}
}
"test" is printed in the console after a new build, and when I go to the Home Screen and return to the app, but if I swipe up from the bottom, and then swipe up from the menu of apps to close the app, it does not print upon opening the app again. What would be an example of code that does run under the circumstance of fully quitting the app and opening it again?
Topic:
UI Frameworks
SubTopic:
SwiftUI
Tags:
I think I understand what you are saying, however with the code I have above, wouldn't the task run after the app being closed for a minute, or is there an error in that code?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
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?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Just following up as I am still in need of a resolution.
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
struct DataView: View {
@State var datum: Data
var body: some View {
VStack {
Text(datum.str)
}
}
}
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)
}
}
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)
}
}
}
}
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.
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: "")]
}
}
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"
}
]
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
}
}
}
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
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?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
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'
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags: