Как сделать асинхронную фоновую задачу fini sh в Swift для загрузки файла Firebase - PullRequest
0 голосов
/ 03 мая 2020

Я реализовал фоновые задачи в Swift, и все работает отлично, функция выполняется, когда я запускаю приложение в фоновом режиме на реальном устройстве. Когда я хочу загрузить изображение в хранилище Firebase, используя putData в фоновой задаче типа BGAppRefreshTask, обработчик завершения putData вызывается только тогда, когда приложение возвращается на передний план и не выполняется в фоновой задаче.

Как я могу обеспечить выполнение задачи загрузки во время фоновой задачи? Спасибо всем за помощь !!

Здесь я регистрирую задачу

//register for background tasks
    BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.foodnotifs.checknewfotos", using: nil) {(task) in
        self.handleAppRefreshTask(task: task as! BGAppRefreshTask)
        print("task registered")
        //
    }

Здесь я отправляю задачу

let checkFotosTask = BGAppRefreshTaskRequest(identifier: "com.foodnotifs.checknewfotos")
checkFotosTask.earliestBeginDate = Date(timeIntervalSinceNow: 10*60)//every 10 min
do{
    try BGTaskScheduler.shared.submit(checkFotosTask)
} catch{
    print("unable to submit task: \(error.localizedDescription)")
}

Теперь вот фактическая фоновая задача, которая выполняется:

func handleAppRefreshTask(task: BGAppRefreshTask)
    {

        let df = DateFormatter()
        df.dateFormat = "yyyy-MM-dd hh:mm:ss"
        let now:String = df.string(from: Date())
        let user = Auth.auth().currentUser
        if(user == nil){
            task.setTaskCompleted(success: false)} //if no user for some reason, end task
        let uid = user!.uid
        let databaseRef = Database.database().reference()
        databaseRef.child("users/" + uid + "/BACKGROUNDFETCHEDIMAGES/" + now).setValue(numberOfNewImages)
        print("number of new images written to database")

        let newAsset = fetchResults.object(at: 0)
        let size = CGSize(width: newAsset.pixelWidth/3, height: newAsset.pixelHeight/3)

        PHImageManager.default().requestImage(for: newAsset, targetSize: (size), contentMode: .aspectFill, options: nil, resultHandler: { image, _ in
            print("image retrieved successfully from phmanager")
            let storageRef = Storage.storage().reference(forURL: "gs://foodnotifs.appspot.com")
            let storageProfileRef = storageRef.child(uid).child(now)
                //create some self defined metadata
                let metadata = StorageMetadata()

                metadata.contentType = "image/jpg"
                metadata.customMetadata = ["uid": uid, "date":now, "image_name":now]

                //now upload the image to storage and update database entry
            //let uploadTask = storageProfileRef.putData(image!.jpegData(compressionQuality: 0.4)!, metadata: metadata)

           //CODE EXECUTES UP TO HERE, completion handler of putData is not executed
                storageProfileRef.putData(image!.jpegData(compressionQuality: 0.4)!, metadata: metadata) { (uploadedMetadata, error) in
                        if error != nil{
                            print("getting error uploading to storage")
                            print(error!.localizedDescription)
                            return
                        }
                        else{
                            print("upload to storage successful")
                            var DLurl:String = ""
                            storageProfileRef.downloadURL { (url,error) in
                                guard let DLurl = url?.absoluteString else{
                                    print("error getting download url for uploaded image")
                                    print(error!.localizedDescription)
                                    return
                                }
                                print("download url is: " + DLurl)

                            //successful upload, now update the database of this user with the image url
                                databaseRef.child("users").child(uid).child("IMAGE_URLs").child(now).setValue(DLurl){ (error, ref) in
                                if error == nil {
                                    //on sucess, also save url to most recent
                                        print("uploaded to database")
                                    databaseRef.child("users").child(user!.uid).child("IMAGE_URLs").child("MOST_RECENT").setValue(DLurl)



                                        }
                                        else{
                                        print("error in uploading to database")
                                        print(error!.localizedDescription)
                                        }
                                }//database updated succssfully
                            } //download url gotten successfully
                } //succesful upload
            }//storage profile ref putdata completion
        })



    }
    else{
        print("no camera roll found")
    }



   //CODE CONTINUES HERE AND REACHES THE END OF THE FUNCTION WITHOUT UPLOADING THE IMAGE
   self.appWillBecomeInactive() //schedule again
    print("background task finished")
    task.setTaskCompleted(success: true) //task completed

}//end function

1 Ответ

0 голосов
/ 04 мая 2020

на всякий случай, если у кого-то возникнет такая же проблема, я наконец решил ее с помощью замыканий. Вот код, который работает.

func uploadImage(completion:@escaping((String?) ->() )) {let df = DateFormatter()
        df.dateFormat = "yyyy-MM-dd hh:mm:ss"
        let now:String = df.string(from: Date())
        let user = Auth.auth().currentUser
        if(user == nil){
            completion(nil)} //if no user for some reason, end task
        let uid = user!.uid
        let databaseRef = Database.database().reference()
        databaseRef.child("users/" + uid + "/BACKGROUNDFETCHEDIMAGES/" + now).setValue(numberOfNewImages)
        print("number of new images written to database")


            let storageProfileRef = storageRef.child(uid).child(now)
                //create some self defined metadata
                let metadata = StorageMetadata()



                //now upload the image to storage and update database entry
            //let uploadTask = storageProfileRef.putData(image!.jpegData(compressionQuality: 0.4)!, metadata: metadata)

                storageProfileRef.putData(image!.jpegData(compressionQuality: 0.4)!, metadata: metadata) { (uploadedMetadata, error) in
                        if error != nil{
                            print("getting error uploading to storage")
                            print(error!.localizedDescription)
                            return
                        }
                        else{
                            print("upload to storage successful")
                            var DLurl:String = ""
                            storageProfileRef.downloadURL { (url,error) in
                                guard let DLurl = url?.absoluteString else{
                                    print("error getting download url for uploaded image")
                                    print(error!.localizedDescription)
                                    return
                                }
                                print("download url is: " + DLurl)


                                databaseRef.child("users").child(uid).child("IMAGE_URLs").child(now).setValue(DLurl){ (error, ref) in
                                if error == nil {
                                    //on sucess, also save url to most recent
                                        print("uploaded to database")
                                    databaseRef.child("users").child(user!.uid).child("IMAGE_URLs").child("MOST_RECENT").setValue(DLurl)
                                         completion(DLurl)
                                        }
                                        else{
                                        print("error in uploading to database")
                                        print(error!.localizedDescription)
                                        }
                                }//database updated succssfully
                            } //download url gotten successfully
                } //succesful upload
            }//storage profile ref putdata completion
        })

}

внутри дескриптора обработчика AppRefreshTasks, включая загружаемое изображение:

//refresh task has a max run time of 30 seconds, process task has more but called less feequently
func handleAppRefreshTask(task: BGAppRefreshTask)
{
    task.expirationHandler = {
        print("expiration time for background foto update reached")
        //do other handling when expiration time is reached
        task.setTaskCompleted(success: false)
        return
    }
    print("going into background task")
    //first check if we have internet connection, if not stop the task
    let monitor = NWPathMonitor()
    monitor.pathUpdateHandler = { path in
        if path.status == .satisfied {
            print("We're connected!")
        } else {
            print("No connection.")
            //we have no connection, finish the background task
            task.setTaskCompleted(success: true)
            return
        }
    }
    print("background task entered")


   uploadImage { (dlurl) in

        if (dlurl != nil){
            print("finished upload with download url:" + dlurl!)}
        else{
            print("dl url is nil, upload failed")
        }

        self.appWillBecomeInactive() //schedule again
       print("background task finished")
       task.setTaskCompleted(success: true) //task completed
    }

}//end function
...