SwiftUI Background Tasks

I'm trying to add Background Tasks (not a timer) to a SwiftUI project for iOS 15 to fetch data from an API periodically.

It seems I'm seeing issues with my Published variable not updating my SwiftUI View until the application has been opened. Second issue is, I'm seeing AsyncImage not loading after a background task has been run, it will just show my placeholder ProgressView loading.

I'm notified by UserNotifications when the background task has been run, and I assumed the Published variable from my ObservableObject class would have been updated in the background? I'm also not sure why AsyncImage breaks while using Background Tasks.

Data Model

struct Pokemon: Codable {
    let sprites: Sprites
    let name: String
    
    enum CodingKeys: String, CodingKey {
        case sprites
        case name
    }
}

struct Sprites: Codable {
    let frontDefault: String
    enum CodingKeys: String, CodingKey {
        case frontDefault = "front_default"
    }
}

class DataModel: ObservableObject {
    let taskIdentifier = "com.example.test.refresh"
    private var cancellables = Set<AnyCancellable>()
    
    @Published var pokemon = Pokemon(sprites: Sprites(frontDefault: "", frontShiny: ""), name: "", types: [])

    func getpokemon() {
        let request = URLRequest(url: URL(string: "https://pokeapi.co/api/v2/pokemon/\(Int.random(in: 1..<450))")!)
        URLSession.shared.dataTaskPublisher(for: request)
            .map{ $0.data }
            .decode(type: Pokemon.self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { print ("Received completion: \($0).") },
                  receiveValue: { pokemon in
                self.pokemon = pokemon
            })
            .store(in: &cancellables)
    }
    
    func register() {
        BGTaskScheduler.shared.register(forTaskWithIdentifier: taskIdentifier, using: nil) { task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }
    }
    
    func scheduleAppRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: taskIdentifier)
        request.earliestBeginDate = Date(timeIntervalSinceNow: 10 * 60)
        do {
            try BGTaskScheduler.shared.submit(request)
        } catch {
            print("Could not schedule app refresh: \(error)")
        }
    }
    
    
    func handleAppRefresh(task: BGAppRefreshTask) {
        scheduleAppRefresh()
        task.expirationHandler = {
            print("expired")
            //task.setTaskCompleted(success: false)
        }
        self.getpokemon()
        task.setTaskCompleted(success: true)
    }
}

App

import SwiftUI
import BackgroundTasks

@main
struct BackgroudTaskApp: App {
    @ObservedObject var data = DataModel()
    
    init() {
        data.register()
        data.scheduleAppRefresh()
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(data)
                .onAppear(perform: data.getpokemon)
        }
    }
}

SwiftUI View

struct ContentView: View {
    @EnvironmentObject var data: DataModel
    var body: some View {
        Button(action: {
            withAnimation {
                data.getpokemon()
            }
        }, label: {
            VStack(spacing: 0) {
                AsyncImage(url:URL(string:data.pokemon.sprites.frontDefault)) { image in
                    image
                        .resizable()
                } placeholder: {
                    ProgressView()
                }
                .frame(width: 120, height: 120)
            }}
        ).buttonStyle(.plain)
    }
}

Did you get further on this? I am interested in doing something similar.

SwiftUI Background Tasks
 
 
Q