0

I am currently testing a background fetch/process with minimum functionality, which I couldn't make it happen after several days. What I originally wanted was that background task to happen every 5 or 10 minutes after clicking a button that leads to IBAction: submitBGTask, which would increment an integer saved in UserDefaults by one every time interval. If I press the button that is connected with IBAction: setLongBGTask, it is supposed to be doing a background task after 12 hours.

Also please note that I am simulating the whole process on a testflight application, not just in the simulator.

Is there any suggestion for my code I should fix to make my purpose to work?

simulator


import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var status: UILabel!
    @IBOutlet weak var count: UILabel!
    override func viewDidLoad() {
        NotificationCenter.default.addObserver(self, selector: #selector(didSentBG), name: NSNotification.Name("one"), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(didFailBG), name: NSNotification.Name("two"), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(didFinishBG), name: NSNotification.Name("three"), object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(didFailSending), name: NSNotification.Name("fail"), object: nil)
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    @IBAction func setLongBGTask(_ sender: Any) {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.scheduleLongAppRefresh()
    }
    @IBAction func submitBGTask(_ sender: Any) {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.scheduleAppRefresh()
    }
    @IBAction func updateView(_ sender: Any) {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        count.text = String(UserDefaults.standard.integer(forKey: "count"))
        status.text = "updated"
    }
    @objc func didSentBG() {
        status.text = "sent"
    }
    @objc func didFailBG() {
        status.text = "fail"
    }
    @objc func didFinishBG() {
        count.text = String(UserDefaults.standard.integer(forKey: "count"))
        status.text = "finish"
    }
    @objc func didFailSending() {
        count.text = String(UserDefaults.standard.integer(forKey: "count"))
        status.text = "ff"
    }
}

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        UserDefaults.standard.set(0, forKey: "count")
        // Override point for customization after application launch.
        // 1. App Refresh Task
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.runnan.background_fetch", using: nil) { task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.runnan.background_fetch_long", using: nil) { task in
            self.handleLongAppRefresh(task: task as! BGAppRefreshTask)
        }
        
        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }
    func handleAppRefresh(task: BGAppRefreshTask) {
        scheduleAppRefresh()
        let value = UserDefaults.standard.integer(forKey: "count")
        UserDefaults.standard.set(value + 1, forKey: "count")
        task.expirationHandler = {
            task.setTaskCompleted(success: false)
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "two"), object: nil)
        }
        task.setTaskCompleted(success: false)
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "three"), object: nil)
    }
    func handleLongAppRefresh(task: BGAppRefreshTask) {
        UserDefaults.standard.set(20000, forKey: "count")
        task.expirationHandler = {
            task.setTaskCompleted(success: false)
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "two"), object: nil)
        }
        task.setTaskCompleted(success: false)
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "three"), object: nil)
    }
    func scheduleAppRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: "com.runnan.background_fetch")

        request.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60)
        do {
            try BGTaskScheduler.shared.submit(request)
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "one"), object: nil)
            // Set a breakpoint in the code that executes after a successful call to submit(_:).
        } catch {
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "fail"), object: nil)
            print("\(Date()): Could not schedule app refresh: \(error)")
        }
    }
    func scheduleLongAppRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: "com.runnan.background_fetch_long")

        request.earliestBeginDate = Date(timeIntervalSinceNow: 12 * 60 * 60)
        do {
            try BGTaskScheduler.shared.submit(request)
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "one"), object: nil)
            // Set a breakpoint in the code that executes after a successful call to submit(_:).
        } catch {
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "fail"), object: nil)
            print("\(Date()): Could not schedule app refresh: \(error)")
        }
    }

}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>BGTaskSchedulerPermittedIdentifiers</key>
    <array>
        <string>com.runnan.background_fetch_long</string>
        <string>com.runnan.background_process</string>
        <string>com.runnan.background_fetch</string>
    </array>
    <key>UIApplicationSceneManifest</key>
    <dict>
        <key>UIApplicationSupportsMultipleScenes</key>
        <false/>
        <key>UISceneConfigurations</key>
        <dict>
            <key>UIWindowSceneSessionRoleApplication</key>
            <array>
                <dict>
                    <key>UISceneConfigurationName</key>
                    <string>Default Configuration</string>
                    <key>UISceneDelegateClassName</key>
                    <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                    <key>UISceneStoryboardFile</key>
                    <string>Main</string>
                </dict>
            </array>
        </dict>
    </dict>
    <key>UIBackgroundModes</key>
    <array>
        <string>fetch</string>
        <string>processing</string>
    </array>
</dict>
</plist>

capbilities

Runnan
  • 7
  • 2

0 Answers0