Efficiency awaits: Background tasks in SwiftUI

RSS for tag

Discuss the WWDC22 Session Efficiency awaits: Background tasks in SwiftUI

Posts under wwdc2022-10142 tag

11 Posts

Post

Replies

Boosts

Views

Activity

Can an app be killed even in the background state?
I know ios apps have the following lifecycle. active background suspended not running inactive My question is if my app can be killed in background state in the lifecycle above? Especially, I'm using beginBackgroundTask(expirationHandler:) to extend background time before suspended. Then my app is terminated with the following message without expirationHandler being called. Message from debugger: Terminated due to signal 9 Does this potentially happen? Actually I'm runnning a heavy task that requires CPU usage in background state, so is this a cause of this problem?
4
0
1.2k
May ’23
does the .backgroundTask SwiftUI modifier work with BGProcessingTaskRequest requests?
Need to run a task in the background that will take several minutes, so BGProcessingTaskRequest seems more appropriate than BGAppRefreshTaskRequest. I'm not sure however that the .backgroundTask modifier will run this since its two possible arguments are either .appRefresh or .UrlSession Thank you in advance, Javier
2
1
1.7k
May ’23
Background Tasks in SwiftUI iOS 16
After following along with the documentation and WWDC22 video on using the new SwiftUI Background Tasks API. My application was successfully updating in the background, then I ran into an issue where the data won't update resulting in a progress view showing and no new data being fetched but will eventually correct itself which doesn't seem right. See below screenshots below. Data is nil at 9:13pm Corrected itself at 9:34pm struct DemoApp: App { @StateObject var viewModel = ViewModel() var body: some Scene { WindowGroup { ContentView() .environmentObject(viewModel) } .backgroundTask(.appRefresh("myapprefresh")) { await viewModel.fetchData() } } } class ViewModel: ObservableObject { @Published var pokemon = PokeAPI(name: "", sprites: Sprites(frontDefault: "")) func fetchData() async { URLSession.shared.dataTaskPublisher(for: request) .map{ $0.data } .decode(type: PokeAPI.self, decoder: JSONDecoder()) .replaceError(with: PokeAPI(name: "", sprites: Sprites(frontDefault: ""))) .receive(on: DispatchQueue.main) .sink(receiveCompletion: {_ in }, receiveValue: { value in self.pokemon = value }).store(in: &cancellables) } }
0
0
1.5k
Mar ’23
Can't Simulate BgTaskScheduler to run the tasks in LLDB
`   func submitBackgroundTasks() {    // Declared at the "Permitted background task scheduler identifiers" in info.plist    let backgroundAppRefreshTaskSchedulerIdentifier = "com.FeedX.backgroundAlerts"    let timeDelay = 10.0    do {     let backgroundAppRefreshTaskRequest = BGAppRefreshTaskRequest(identifier: backgroundAppRefreshTaskSchedulerIdentifier)     backgroundAppRefreshTaskRequest.earliestBeginDate = Date(timeIntervalSinceNow: timeDelay)     try BGTaskScheduler.shared.submit(backgroundAppRefreshTaskRequest)     print("Submitted task request")    } catch {     print("Failed to submit BGTask")    }   } I am able to submit the task and hit the breakpoint but in the LLDB i try to simulate the task using the command e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.FeedX.backgroundAlerts"] But i get the following error : error: Error [IRForTarget]: Rewriting an Objective-C constant string requires CFStringCreateWithBytes Any idea on how to solve this or run the task using a different way? Thanks.
1
0
2.3k
Jan ’23
Deduplicated URLRequests
Hi, In the session it's mentioned that requests are being deduplicated when a new request with the same method, url is being sent in the same session while another one is still being performed. I never heard of it before and used to implement that manually in different apps. Is it a new feature of URLSession or should be expect this before? Does anybody know? Best, Karl
4
2
1.8k
Dec ’22
TaskGroup lockup with more than 7 tasks
Platform: macOS 12.4, MacBook Pro 2.3GHz Quad-Core i5, 16GB RAM I'm trying to read an OSLog concurrently because it is big and I don't need any of the data in order. Because the return from .getEntries is a sequence, the most efficient approach seems to be to iterate through. Iteration through the sequence from start to finish can take a long time so I thought I can split it up into days and concurrently process each day. I'm using a task group to do this and it works as long as the number of tasks is less than 8. When it works, I do get the result faster but not a lot faster. I guess there is a lot of overhead but actually it seems to be that my log file is dominated by the processing on 1 of the days. Ideally I want more concurrent tasks to break up the day into smaller blocks. But as soon as I try to create 8 or more tasks, I get a lockup with the following error posted to the console. enable_updates_common timed out waiting for updates to reenable Here are my tests. First - a pure iterative approach. No tasks. completion of this routine on my i5 quad core takes 229s    func scanLogarchiveIterative(url: URL) async {     do {       let timer = Date()               let logStore = try OSLogStore(url: url)       let last5days = logStore.position(timeIntervalSinceEnd: -3600*24*5)       let filteredEntries = try logStore.getEntries(at: last5days)               var processedEntries: [String] = []               for entry in filteredEntries {         processedEntries.append(entry.composedMessage)       }               print("Completed iterative scan in: ", timer.timeIntervalSinceNow)     } catch {             }   } Next is a concurrent approach using a TaskGroup which creates 5 child tasks. Completion takes 181s. Faster but the last day dominates so not a huge benefit as the most time is taken by a single task processing the last day.   func scanLogarchiveConcurrent(url: URL) async {     do {       let timer = Date()               var processedEntries: [String] = []               try await withThrowingTaskGroup(of: [String].self) { group in         let timestep = 3600*24         for logSectionStartPosition in stride(from: 0, to: -3600*24*5, by: -1*timestep) {           group.addTask {             let logStore = try OSLogStore(url: url)             let filteredEntries = try logStore.getEntries(at: logStore.position(timeIntervalSinceEnd: TimeInterval(logSectionStartPosition)))             var processedEntriesConcurrent: [String] = []             let endDate = logStore.position(timeIntervalSinceEnd: TimeInterval(logSectionStartPosition + timestep)).value(forKey: "date") as? Date             for entry in filteredEntries {               if entry.date > (endDate ?? Date()) {                 break               }               processedEntriesConcurrent.append(entry.composedMessage)             }             return processedEntriesConcurrent           }         }                   for try await processedEntriesConcurrent in group {           print("received task completion")           processedEntries.append(contentsOf: processedEntriesConcurrent)         }       }               print("Completed concurrent scan in: ", timer.timeIntervalSinceNow)             } catch {             }   } If I split this further to concurrently process half days, then the app locks up. The console periodically prints enable_updates_common timed out waiting for updates to reenable If I pause the debugger, it seems like there is a wait on a semaphore which must be internal to the concurrent framework?    func scanLogarchiveConcurrentManyTasks(url: URL) async {     do {       let timer = Date()               var processedEntries: [String] = []               try await withThrowingTaskGroup(of: [String].self) { group in         let timestep = 3600*12         for logSectionStartPosition in stride(from: 0, to: -3600*24*5, by: -1*timestep) {           group.addTask {             let logStore = try OSLogStore(url: url)             let filteredEntries = try logStore.getEntries(at: logStore.position(timeIntervalSinceEnd: TimeInterval(logSectionStartPosition)))             var processedEntriesConcurrent: [String] = []             let endDate = logStore.position(timeIntervalSinceEnd: TimeInterval(logSectionStartPosition + timestep)).value(forKey: "date") as? Date             for entry in filteredEntries {               if entry.date > (endDate ?? Date()) {                 break               }               processedEntriesConcurrent.append(entry.composedMessage)             }             return processedEntriesConcurrent           }         }                   for try await processedEntriesConcurrent in group {           print("received task completion")           processedEntries.append(contentsOf: processedEntriesConcurrent)         }       }               print("Completed concurrent scan in: ", timer.timeIntervalSinceNow)             } catch {             }   } I read that it may be possible to get more insight into concurrency issue by setting the following environment: LIBDISPATCH_COOPERATIVE_POOL_STRICT 1 This stops the lockup but it is because each task is run sequentially so there is no benefit from concurrency anymore. I cannot see where to go next apart from accept the linear processing time. It also feels like doing any concurrency (even if under 8 tasks) is risky as there is no documentation to suggest that is a limit. Could it be that concurrently processing the sequence from OSLog .getEntries is not suitable for concurrent access and shouldn't be done? Again, I don't see any documentation to suggest this is the case. Finally, the processing of each entry is so light that there is little benefit to offloading just the processing to other tasks. The time taken seems to be purely dominated by iterating the sequence. In reality I do use a predicate in .getEntries which helps a bit but its not enough and concurrency would still be valuable if I could process 1 hour blocks concurrently.
2
0
2.7k
Jun ’22
SwiftUI .backgroundTask requires registering and providing a handler
Hi I tried adding the new .backgroundTask modifier to my project but it still requires calling func register(forTaskWithIdentifier identifier: String, using queue: DispatchQueue?, launchHandler: @escaping (BGTask) -> Void) -> Bool without it I get the error No launch handler registered for task with identifier... but providing a launchHandler doesn't make any sense as it's handled in the .backgroundTask closure. Am I missing something here? also can it be used for BGProcessingTasks as well, the WWDC session didn't mention that Radar/Feedback Number: FB10249217
0
0
983
Jun ’22
.backgroundTask in SwiftUI cannot compile
Error Code: error build: Command CompileSwift failed with a nonzero exit code My Code: .backgroundTask(.appRefresh("checkValidity")) { //   scheduleAppRefresh() //  checkRecords() }
Replies
17
Boosts
2
Views
4.8k
Activity
Jan ’24
Can an app be killed even in the background state?
I know ios apps have the following lifecycle. active background suspended not running inactive My question is if my app can be killed in background state in the lifecycle above? Especially, I'm using beginBackgroundTask(expirationHandler:) to extend background time before suspended. Then my app is terminated with the following message without expirationHandler being called. Message from debugger: Terminated due to signal 9 Does this potentially happen? Actually I'm runnning a heavy task that requires CPU usage in background state, so is this a cause of this problem?
Replies
4
Boosts
0
Views
1.2k
Activity
May ’23
does the .backgroundTask SwiftUI modifier work with BGProcessingTaskRequest requests?
Need to run a task in the background that will take several minutes, so BGProcessingTaskRequest seems more appropriate than BGAppRefreshTaskRequest. I'm not sure however that the .backgroundTask modifier will run this since its two possible arguments are either .appRefresh or .UrlSession Thank you in advance, Javier
Replies
2
Boosts
1
Views
1.7k
Activity
May ’23
Background Tasks in SwiftUI iOS 16
After following along with the documentation and WWDC22 video on using the new SwiftUI Background Tasks API. My application was successfully updating in the background, then I ran into an issue where the data won't update resulting in a progress view showing and no new data being fetched but will eventually correct itself which doesn't seem right. See below screenshots below. Data is nil at 9:13pm Corrected itself at 9:34pm struct DemoApp: App { @StateObject var viewModel = ViewModel() var body: some Scene { WindowGroup { ContentView() .environmentObject(viewModel) } .backgroundTask(.appRefresh("myapprefresh")) { await viewModel.fetchData() } } } class ViewModel: ObservableObject { @Published var pokemon = PokeAPI(name: "", sprites: Sprites(frontDefault: "")) func fetchData() async { URLSession.shared.dataTaskPublisher(for: request) .map{ $0.data } .decode(type: PokeAPI.self, decoder: JSONDecoder()) .replaceError(with: PokeAPI(name: "", sprites: Sprites(frontDefault: ""))) .receive(on: DispatchQueue.main) .sink(receiveCompletion: {_ in }, receiveValue: { value in self.pokemon = value }).store(in: &cancellables) } }
Replies
0
Boosts
0
Views
1.5k
Activity
Mar ’23
Can't Simulate BgTaskScheduler to run the tasks in LLDB
`   func submitBackgroundTasks() {    // Declared at the "Permitted background task scheduler identifiers" in info.plist    let backgroundAppRefreshTaskSchedulerIdentifier = "com.FeedX.backgroundAlerts"    let timeDelay = 10.0    do {     let backgroundAppRefreshTaskRequest = BGAppRefreshTaskRequest(identifier: backgroundAppRefreshTaskSchedulerIdentifier)     backgroundAppRefreshTaskRequest.earliestBeginDate = Date(timeIntervalSinceNow: timeDelay)     try BGTaskScheduler.shared.submit(backgroundAppRefreshTaskRequest)     print("Submitted task request")    } catch {     print("Failed to submit BGTask")    }   } I am able to submit the task and hit the breakpoint but in the LLDB i try to simulate the task using the command e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.FeedX.backgroundAlerts"] But i get the following error : error: Error [IRForTarget]: Rewriting an Objective-C constant string requires CFStringCreateWithBytes Any idea on how to solve this or run the task using a different way? Thanks.
Replies
1
Boosts
0
Views
2.3k
Activity
Jan ’23
Deduplicated URLRequests
Hi, In the session it's mentioned that requests are being deduplicated when a new request with the same method, url is being sent in the same session while another one is still being performed. I never heard of it before and used to implement that manually in different apps. Is it a new feature of URLSession or should be expect this before? Does anybody know? Best, Karl
Replies
4
Boosts
2
Views
1.8k
Activity
Dec ’22
Running app , even though user completely closed the app
Is it possible to run the app in the background even though the user has completely closed it? I want to fetch device activity every 15 mins. Is this possible to fetch even though the app is completely closed?
Replies
3
Boosts
0
Views
1.2k
Activity
Dec ’22
More detail on .backgroundTask(.urlSession("isStormy")) shown at 11:50
In the slides at 11:50 the following code snippet is shown: .backgroundTask(.urlSession("isStormy")) { // ... } Please could you explain what should be done in this block? The video just cuts off right after and seems like the explanation is missing. Thanks.
Replies
3
Boosts
4
Views
2.3k
Activity
Jul ’22
TaskGroup lockup with more than 7 tasks
Platform: macOS 12.4, MacBook Pro 2.3GHz Quad-Core i5, 16GB RAM I'm trying to read an OSLog concurrently because it is big and I don't need any of the data in order. Because the return from .getEntries is a sequence, the most efficient approach seems to be to iterate through. Iteration through the sequence from start to finish can take a long time so I thought I can split it up into days and concurrently process each day. I'm using a task group to do this and it works as long as the number of tasks is less than 8. When it works, I do get the result faster but not a lot faster. I guess there is a lot of overhead but actually it seems to be that my log file is dominated by the processing on 1 of the days. Ideally I want more concurrent tasks to break up the day into smaller blocks. But as soon as I try to create 8 or more tasks, I get a lockup with the following error posted to the console. enable_updates_common timed out waiting for updates to reenable Here are my tests. First - a pure iterative approach. No tasks. completion of this routine on my i5 quad core takes 229s    func scanLogarchiveIterative(url: URL) async {     do {       let timer = Date()               let logStore = try OSLogStore(url: url)       let last5days = logStore.position(timeIntervalSinceEnd: -3600*24*5)       let filteredEntries = try logStore.getEntries(at: last5days)               var processedEntries: [String] = []               for entry in filteredEntries {         processedEntries.append(entry.composedMessage)       }               print("Completed iterative scan in: ", timer.timeIntervalSinceNow)     } catch {             }   } Next is a concurrent approach using a TaskGroup which creates 5 child tasks. Completion takes 181s. Faster but the last day dominates so not a huge benefit as the most time is taken by a single task processing the last day.   func scanLogarchiveConcurrent(url: URL) async {     do {       let timer = Date()               var processedEntries: [String] = []               try await withThrowingTaskGroup(of: [String].self) { group in         let timestep = 3600*24         for logSectionStartPosition in stride(from: 0, to: -3600*24*5, by: -1*timestep) {           group.addTask {             let logStore = try OSLogStore(url: url)             let filteredEntries = try logStore.getEntries(at: logStore.position(timeIntervalSinceEnd: TimeInterval(logSectionStartPosition)))             var processedEntriesConcurrent: [String] = []             let endDate = logStore.position(timeIntervalSinceEnd: TimeInterval(logSectionStartPosition + timestep)).value(forKey: "date") as? Date             for entry in filteredEntries {               if entry.date > (endDate ?? Date()) {                 break               }               processedEntriesConcurrent.append(entry.composedMessage)             }             return processedEntriesConcurrent           }         }                   for try await processedEntriesConcurrent in group {           print("received task completion")           processedEntries.append(contentsOf: processedEntriesConcurrent)         }       }               print("Completed concurrent scan in: ", timer.timeIntervalSinceNow)             } catch {             }   } If I split this further to concurrently process half days, then the app locks up. The console periodically prints enable_updates_common timed out waiting for updates to reenable If I pause the debugger, it seems like there is a wait on a semaphore which must be internal to the concurrent framework?    func scanLogarchiveConcurrentManyTasks(url: URL) async {     do {       let timer = Date()               var processedEntries: [String] = []               try await withThrowingTaskGroup(of: [String].self) { group in         let timestep = 3600*12         for logSectionStartPosition in stride(from: 0, to: -3600*24*5, by: -1*timestep) {           group.addTask {             let logStore = try OSLogStore(url: url)             let filteredEntries = try logStore.getEntries(at: logStore.position(timeIntervalSinceEnd: TimeInterval(logSectionStartPosition)))             var processedEntriesConcurrent: [String] = []             let endDate = logStore.position(timeIntervalSinceEnd: TimeInterval(logSectionStartPosition + timestep)).value(forKey: "date") as? Date             for entry in filteredEntries {               if entry.date > (endDate ?? Date()) {                 break               }               processedEntriesConcurrent.append(entry.composedMessage)             }             return processedEntriesConcurrent           }         }                   for try await processedEntriesConcurrent in group {           print("received task completion")           processedEntries.append(contentsOf: processedEntriesConcurrent)         }       }               print("Completed concurrent scan in: ", timer.timeIntervalSinceNow)             } catch {             }   } I read that it may be possible to get more insight into concurrency issue by setting the following environment: LIBDISPATCH_COOPERATIVE_POOL_STRICT 1 This stops the lockup but it is because each task is run sequentially so there is no benefit from concurrency anymore. I cannot see where to go next apart from accept the linear processing time. It also feels like doing any concurrency (even if under 8 tasks) is risky as there is no documentation to suggest that is a limit. Could it be that concurrently processing the sequence from OSLog .getEntries is not suitable for concurrent access and shouldn't be done? Again, I don't see any documentation to suggest this is the case. Finally, the processing of each entry is so light that there is little benefit to offloading just the processing to other tasks. The time taken seems to be purely dominated by iterating the sequence. In reality I do use a predicate in .getEntries which helps a bit but its not enough and concurrency would still be valuable if I could process 1 hour blocks concurrently.
Replies
2
Boosts
0
Views
2.7k
Activity
Jun ’22
SwiftUI .backgroundTask requires registering and providing a handler
Hi I tried adding the new .backgroundTask modifier to my project but it still requires calling func register(forTaskWithIdentifier identifier: String, using queue: DispatchQueue?, launchHandler: @escaping (BGTask) -> Void) -> Bool without it I get the error No launch handler registered for task with identifier... but providing a launchHandler doesn't make any sense as it's handled in the .backgroundTask closure. Am I missing something here? also can it be used for BGProcessingTasks as well, the WWDC session didn't mention that Radar/Feedback Number: FB10249217
Replies
0
Boosts
0
Views
983
Activity
Jun ’22
multiple background requests
In Efficiency awaits: Background tasks in SwiftUI, the isStormy() func has only one request. What if we have multiple requests. What's the best implementation?
Replies
1
Boosts
1
Views
1.3k
Activity
Jun ’22