Search code examples
swiftxcodefirebaseflaskremote-server

Background Processing in Swift


I Switched to push notifications, check my answer below.

I'm trying to implement a function that runs in the background when the app is put in the background. The implementation is similar to a timer that has to keep running when we exit the app. I already activated "Background Processing" in my app and this is the function I want to run in the background:

 public func keepCheck(timer:Timer) {
        DispatchQueue.global(qos: DispatchQoS.background.qosClass).async {
            print("do some background task")
            if self.check == true {
                if self.outBool == false {
                    self.residualWaterWeight -= self.indoorDryRate
                    if self.residualWaterWeight <= 0.0 {
                        self.appDelegate?.scheduleNotification(notificationType: "Your Clothes are Dry!",body: "I Love fabric conditioner smell in the morning <3")
                        self.defaults.set(false, forKey: "keep_check")
                    }
                    print(self.residualWaterWeight)
                } else {
                    self.do10min()
                }
                self.check = self.defaults.bool(forKey: "keep_check")

            }

            DispatchQueue.main.async {
                print("update some UI")
            }
        }
    }

Now this function runs in the background when the app is open but it stop when you tap the home button. How can I prevent this function from stopping on app pause?


Solution

  • I switched from local notifications to push notifications setting my remote flask application to handle all the background processing and notification sending with firebase:

    1. Set your app up to receive push notifications

    2. Create a flask application on your remote server and deploy it. Here are some sample instructions to create and deploy your flask application

    3. Configure flask with your Firebase as a server

    DISCLAIMER: you should't proceed if you haven't follow successfully the previous steps.

    1. Now if you are new to Flask, it handle HTTP requests pretty neatly and it's very intuitive.

      4.1. Open your Flask application file for editing

      4.2 Add import requests and from pyfcm import FCMNotification in your imports and this right after the imports: push_service = FCMNotification(api_key=SERVER_KEY) where SERVER_KEY is your server key from firebase in your project settings.

      4.3 Add the following code (every bit of this code in ALL CAPS is something you can change):

       @app.route('/MY_REQUEST_NAME')
       def MY_FUNCTION():
           # MY_ARGUMENT in this case will be the iOS device token
           MY_ARGUMENT = str(request.args.get('MY_ARGUMENT'))
           # do calculations
           result = push_service.notify_single_device(
               registration_id=MY_ARGUMENT,
               message_title=MESSAGE,
               message_body=BODY
           )
           return MY_RESULTS
           # if your goal is just sending notification you don't really need a return statement
      

      4.4 Activate your virtual environment source myprojectenv/bin/activate and type pip install requests pyfcm

      The Flask application will trigger the function we just wrote when you type in your browser: https://example.com/my_request_name?my_argument=my_argument_value or you can use your server public IP address instead of the domain if you don't have one.

    2. Go to your AppDelegate and modify the following function to store the device fcm_token somewhere. I personally used the defaults.standard:

       func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
         print("Firebase registration token: \(fcmToken)")
      
         // Save Token:
         UserDefaults.standard.set(fcmToken, forKey: "FCM_TOKEN")
      
         NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
         // TODO: If necessary send token to application server.
         // Note: This callback is fired at each app startup and whenever a new token is generated.
       }
      
    3. Finally, in Xcode you can call your request like this:

       let token = UserDefaults.standard.string(forKey: "FCM_TOKEN")!
       let url : URL = URL(string: "https://example.com/my_request_name?my_argument=\(token)")!
       let task = URLSession.shared.dataTask(with: url)
       task.resume()